diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 67eb80cf4d6..0f1ae9f821d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -96,7 +96,7 @@ cargo-check-benches: stage: test <<: *docker-cache-status script: - - time cargo check --all --benches --target $CARGO_TARGET --locked --verbose --color=always + - time cargo check --workspace --benches --target $CARGO_TARGET --locked --verbose --color=always --all-features - sccache --show-stats cargo-audit: diff --git a/Cargo.lock b/Cargo.lock index b353573b2c2..9c0312077d9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -212,7 +212,6 @@ dependencies = [ "log", "lru-cache", "machine", - "macros", "parity-bytes", "parity-crypto", "parking_lot 0.10.0", @@ -232,24 +231,29 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" +[[package]] +name = "autocfg" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" + [[package]] name = "backtrace" -version = "0.3.9" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a47830402e9981c5c41223151efcced65a0510c13097c769cede7efb34782a" +checksum = "7f80256bc78f67e7df7e36d77366f636ed976895d91fe2ab9efa3973e8fe8c4f" dependencies = [ "backtrace-sys", "cfg-if", "libc", "rustc-demangle", - "winapi 0.3.8", ] [[package]] name = "backtrace-sys" -version = "0.1.24" +version = "0.1.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66d56ac8dabd07f6aacdaf633f4b8262f5b3601a810a0dcddffd5c22c69daa0" +checksum = "5d6575f128516de27e3ce99689419835fce9643a9b215a14d2b5b685be018491" dependencies = [ "cc", "libc", @@ -308,7 +312,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f04a5e50dc80b3d5d35320889053637d15011aed5e66b66b37ae798c65da6f7" dependencies = [ - "autocfg", + "autocfg 0.1.7", "byteorder", "serde", ] @@ -543,12 +547,11 @@ checksum = "926013f2860c46252efceabb19f4a6b308197505082c609025aa6706c011d427" [[package]] name = "cc" -version = "1.0.46" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0213d356d3c4ea2c18c40b037c3be23cd639825c18f25ee670ac7813beeef99c" +checksum = "8dae9c4b8fedcae85592ba623c4fd08cfdab3e3b72d6df780c6ead964a69bfff" dependencies = [ - "jobserver", - "num_cpus", + "rayon", ] [[package]] @@ -667,7 +670,6 @@ dependencies = [ "log", "lru-cache", "machine", - "macros", "parity-crypto", "parking_lot 0.10.0", "rand 0.7.2", @@ -689,9 +691,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.35" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ec65ee4f9c9d16f335091d23693457ed4928657ba4982289d7fafee03bc614a" +checksum = "81fb25b677f8bf1eb325017cb6bb8452f87969db0fedb4f757b297bee78a7c62" dependencies = [ "cc", ] @@ -712,7 +714,7 @@ dependencies = [ "parity-util-mem", "patricia-trie-ethereum", "rlp", - "rlp_derive", + "rlp-derive", "rustc-hex 2.1.0", "unexpected", "vm", @@ -774,9 +776,9 @@ dependencies = [ [[package]] name = "criterion" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938703e165481c8d612ea3479ac8342e5615185db37765162e762ec3523e2fc6" +checksum = "1fc755679c12bda8e5523a71e4d654b6bf2e14bd838dfc48cde6559a05caf7d1" dependencies = [ "atty", "cast", @@ -786,10 +788,10 @@ dependencies = [ "itertools", "lazy_static", "num-traits 0.2.6", - "rand_core 0.5.1", - "rand_os", - "rand_xoshiro", + "oorandom", + "plotters", "rayon", + "regex", "serde", "serde_derive", "serde_json", @@ -799,9 +801,9 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eccdc6ce8bbe352ca89025bee672aa6d24f4eb8c53e3a8b5d1bc58011da072a2" +checksum = "a01e15e0ea58e8234f96146b1f91fa9d0e4dd7a38da93ff7a75d42c0b9d3a545" dependencies = [ "cast", "itertools", @@ -819,12 +821,13 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" +checksum = "9f02af974daeee82218205558e51ec8768b48cf524bd01d550abe5573a608285" dependencies = [ - "crossbeam-epoch 0.7.2", - "crossbeam-utils 0.6.6", + "crossbeam-epoch 0.8.2", + "crossbeam-utils 0.7.2", + "maybe-uninit", ] [[package]] @@ -843,14 +846,15 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" +checksum = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace" dependencies = [ - "autocfg", + "autocfg 1.0.0", "cfg-if", - "crossbeam-utils 0.7.0", + "crossbeam-utils 0.7.2", "lazy_static", + "maybe-uninit", "memoffset", "scopeguard 1.0.0", ] @@ -864,6 +868,16 @@ dependencies = [ "crossbeam-utils 0.6.6", ] +[[package]] +name = "crossbeam-queue" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" +dependencies = [ + "cfg-if", + "crossbeam-utils 0.7.2", +] + [[package]] name = "crossbeam-utils" version = "0.5.0" @@ -882,11 +896,11 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.7.0" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" +checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" dependencies = [ - "autocfg", + "autocfg 1.0.0", "cfg-if", "lazy_static", ] @@ -1037,18 +1051,6 @@ dependencies = [ "rustc-hex 2.1.0", ] -[[package]] -name = "eip-2124" -version = "0.1.0" -dependencies = [ - "crc", - "ethereum-types", - "hex-literal", - "maplit", - "rlp", - "rlp_derive", -] - [[package]] name = "eip-712" version = "0.1.1" @@ -1156,7 +1158,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "tiny-keccak", + "tiny-keccak 1.5.0", ] [[package]] @@ -1186,12 +1188,13 @@ dependencies = [ "criterion", "either", "ethereum-types", + "hex-literal", "keccak-hash", "log", "memmap", "parking_lot 0.10.0", "primal", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", "serde_json", "static_assertions", "tempdir", @@ -1211,7 +1214,6 @@ dependencies = [ "keccak-hash", "log", "machine", - "macros", "rlp", "spec", "tempdir", @@ -1228,7 +1230,7 @@ dependencies = [ "fixed-hash", "impl-rlp", "impl-serde", - "tiny-keccak", + "tiny-keccak 1.5.0", ] [[package]] @@ -1270,7 +1272,6 @@ dependencies = [ "lazy_static", "log", "machine", - "macros", "memory-cache", "parity-bytes", "parity-crypto", @@ -1279,7 +1280,7 @@ dependencies = [ "patricia-trie-ethereum", "pod", "rand 0.7.2", - "rand_xorshift", + "rand_xorshift 0.2.0", "rayon", "registrar", "rlp", @@ -1343,9 +1344,9 @@ dependencies = [ "rand 0.7.2", "rayon", "rlp", + "rlp-derive", "rlp_compress", - "rlp_derive", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", "tempdir", "triehash-ethereum", ] @@ -1370,7 +1371,7 @@ dependencies = [ "hex-literal", "keccak-hash", "log", - "macros", + "maplit", "num", "parity-bytes", "parity-crypto", @@ -1395,7 +1396,7 @@ dependencies = [ "parity-util-mem", "parking_lot 0.10.0", "rlp", - "rlp_derive", + "rlp-derive", ] [[package]] @@ -1451,7 +1452,7 @@ dependencies = [ "patricia-trie-ethereum", "rand 0.7.2", "rlp", - "rlp_derive", + "rlp-derive", "serde", "serde_derive", "smallvec 1.2.0", @@ -1505,7 +1506,7 @@ dependencies = [ "price-info", "registrar", "rlp", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", "serde", "serde_derive", "serde_json", @@ -1544,6 +1545,7 @@ dependencies = [ "ethcore-io", "ethcore-network", "ethereum-types", + "hex-literal", "igd", "ipnetwork", "keccak-hash", @@ -1559,12 +1561,11 @@ dependencies = [ "parking_lot 0.10.0", "rand 0.7.2", "rlp", - "rustc-hex 1.0.0", "serde", "serde_json", "slab 0.2.0", "tempdir", - "tiny-keccak", + "tiny-keccak 1.5.0", ] [[package]] @@ -1602,15 +1603,15 @@ dependencies = [ "patricia-trie-ethereum", "registrar", "rlp", - "rlp_derive", - "rustc-hex 1.0.0", + "rlp-derive", + "rustc-hex 2.1.0", "serde", "serde_derive", "serde_json", "spec", "state-db", "time-utils", - "tiny-keccak", + "tiny-keccak 1.5.0", "trace", "transaction-pool", "trie-db", @@ -1618,43 +1619,6 @@ dependencies = [ "vm", ] -[[package]] -name = "ethcore-secretstore" -version = "1.0.0" -dependencies = [ - "byteorder", - "env_logger 0.5.13", - "ethabi", - "ethabi-contract", - "ethabi-derive", - "ethereum-types", - "ethkey", - "futures", - "hyper", - "jsonrpc-server-utils", - "keccak-hash", - "kvdb", - "kvdb-rocksdb", - "kvdb-sled", - "lazy_static", - "log", - "parity-bytes", - "parity-crypto", - "parity-runtime", - "parking_lot 0.10.0", - "percent-encoding 2.1.0", - "rustc-hex 1.0.0", - "serde", - "serde_derive", - "serde_json", - "tempdir", - "tiny-keccak", - "tokio", - "tokio-io", - "tokio-service", - "url 2.1.0", -] - [[package]] name = "ethcore-service" version = "0.1.0" @@ -1708,6 +1672,7 @@ dependencies = [ "ethcore-network", "ethcore-network-devp2p", "ethcore-private-tx", + "ethereum-forkid", "ethereum-types", "fastmap", "futures", @@ -1716,22 +1681,35 @@ dependencies = [ "kvdb-memorydb", "log", "machine", - "macros", "parity-bytes", "parity-crypto", "parity-runtime", "parity-util-mem", "parking_lot 0.10.0", "rand 0.7.2", - "rand_xorshift", + "rand_xorshift 0.2.0", "rlp", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", "snapshot", "spec", "trace-time", "triehash-ethereum", ] +[[package]] +name = "ethereum-forkid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2f547713a9a1e69a55529f3981dbded2c59c69629c7df795bdc7deef3375f59" +dependencies = [ + "crc", + "ethereum-types", + "maplit", + "parity-util-mem", + "rlp", + "rlp-derive", +] + [[package]] name = "ethereum-types" version = "0.8.0" @@ -1751,8 +1729,8 @@ name = "ethjson" version = "0.1.0" dependencies = [ "ethereum-types", - "macros", - "rustc-hex 1.0.0", + "maplit", + "rustc-hex 2.1.0", "serde", "serde_json", ] @@ -1779,7 +1757,7 @@ dependencies = [ "panic_hook", "parity-crypto", "parity-wordlist", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", "serde", "serde_derive", "threadpool", @@ -1799,14 +1777,14 @@ dependencies = [ "parity-wordlist", "parking_lot 0.10.0", "rand 0.7.2", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", "serde", "serde_derive", "serde_json", "smallvec 1.2.0", "tempdir", "time", - "tiny-keccak", + "tiny-keccak 1.5.0", ] [[package]] @@ -1822,7 +1800,7 @@ dependencies = [ "panic_hook", "parity-crypto", "parking_lot 0.10.0", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", "serde", "serde_derive", "tempdir", @@ -1843,7 +1821,6 @@ dependencies = [ "parity-bytes", "parity-util-mem", "parking_lot 0.10.0", - "rustc-hex 1.0.0", "vm", ] @@ -1860,10 +1837,11 @@ dependencies = [ "ethereum-types", "ethjson", "evm", + "hex-literal", "panic_hook", "parity-bytes", "pod", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", "serde", "serde_json", "spec", @@ -1879,11 +1857,15 @@ dependencies = [ "account-db", "account-state", "common-types", + "criterion", "env_logger 0.5.13", "ethcore", + "ethcore-db", "ethereum-types", "evm", "hash-db", + "hex-literal", + "journaldb", "keccak-hash", "keccak-hasher 0.1.1", "kvdb", @@ -1893,8 +1875,9 @@ dependencies = [ "parity-crypto", "patricia-trie-ethereum", "pod", - "rustc-hex 1.0.0", "spec", + "state-db", + "tempdir", "trace", "trie-db", "trie-vm-factories", @@ -2174,7 +2157,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e6073d0ca812575946eb5f35ff68dbe519907b25c42530389ff946dc84c6ead" dependencies = [ "ahash", - "autocfg", + "autocfg 0.1.7", ] [[package]] @@ -2388,7 +2371,7 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712d7b3ea5827fcb9d4fda14bf4da5f136f0db2ae9c8f4bd4e2d1c6fde4e6db2" dependencies = [ - "autocfg", + "autocfg 0.1.7", ] [[package]] @@ -2472,17 +2455,6 @@ dependencies = [ "libc", ] -[[package]] -name = "jobserver" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f74e73053eaf95399bf926e48fc7a2a3ce50bd0eaaa2357d391e95b2dcdd4f10" -dependencies = [ - "libc", - "log", - "rand 0.7.2", -] - [[package]] name = "journaldb" version = "0.2.0" @@ -2628,7 +2600,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e563fa6fe52b2686094846118bf2cb2e6f75e6b8cec6c3aba09be8e835c7f998" dependencies = [ "primitive-types", - "tiny-keccak", + "tiny-keccak 1.5.0", ] [[package]] @@ -2638,7 +2610,7 @@ dependencies = [ "ethereum-types", "hash-db", "plain_hasher", - "tiny-keccak", + "tiny-keccak 1.5.0", ] [[package]] @@ -2649,7 +2621,7 @@ checksum = "3bf18164fd7ce989041f8fc4a1ae72a8bd1bec3575f2aeaf1d4968fc053aabef" dependencies = [ "hash-db", "hash256-std-hasher", - "tiny-keccak", + "tiny-keccak 1.5.0", ] [[package]] @@ -2761,6 +2733,19 @@ dependencies = [ "libc", ] +[[package]] +name = "libsecp256k1" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc1e2c808481a63dc6da2074752fdd4336a3c8fcc68b83db6f1fd5224ae7962" +dependencies = [ + "arrayref", + "crunchy 0.2.2", + "digest 0.8.0", + "rand 0.7.2", + "subtle 2.2.2", +] + [[package]] name = "linked-hash-map" version = "0.5.1" @@ -2864,15 +2849,14 @@ dependencies = [ "ethereum-types", "ethjson", "evm", + "hex-literal", "keccak-hash", "log", "lru-cache", - "macros", "parity-bytes", "parity-crypto", "parking_lot 0.10.0", "rlp", - "rustc-hex 1.0.0", "spec", "state-db", "tempdir", @@ -2881,10 +2865,6 @@ dependencies = [ "vm", ] -[[package]] -name = "macros" -version = "0.1.0" - [[package]] name = "maplit" version = "1.0.2" @@ -2897,6 +2877,12 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" +[[package]] +name = "maybe-uninit" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" + [[package]] name = "memchr" version = "2.2.1" @@ -3062,7 +3048,7 @@ checksum = "c62469025f45dee2464ef9fc845f4683c543993792c1993e7d903c17a4546b74" dependencies = [ "sha1", "sha2 0.7.1", - "tiny-keccak", + "tiny-keccak 1.5.0", ] [[package]] @@ -3227,6 +3213,12 @@ dependencies = [ "winapi-build", ] +[[package]] +name = "oorandom" +version = "11.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebcec7c9c2a95cacc7cd0ecb89d8a8454eca13906f6deb55258ffff0adeb9405" + [[package]] name = "opaque-debug" version = "0.2.2" @@ -3284,9 +3276,9 @@ checksum = "fa5168b4cf41f3835e4bc6ffb32f51bc9365dc50cb351904595b3931d917fd0c" [[package]] name = "parity-crypto" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27a9c2b525c93d717a234eb220c26474f8d97b08ac50d79faeac4cb6c74bf0b9" +checksum = "e4015edcfb0304c3b32f6c4ad68fb8b0a14f1d7d250bd0c6b9cd312d38038000" dependencies = [ "aes", "aes-ctr", @@ -3295,15 +3287,15 @@ dependencies = [ "ethereum-types", "hmac", "lazy_static", - "parity-secp256k1", "pbkdf2", "rand 0.7.2", "ripemd160", "rustc-hex 2.1.0", "scrypt", + "secp256k1", "sha2 0.8.0", - "subtle 2.1.0", - "tiny-keccak", + "subtle 2.2.2", + "tiny-keccak 2.0.1", "zeroize", ] @@ -3347,7 +3339,6 @@ dependencies = [ "ethcore-miner", "ethcore-network", "ethcore-private-tx", - "ethcore-secretstore", "ethcore-service", "ethcore-sync", "ethereum-types", @@ -3376,6 +3367,7 @@ dependencies = [ "parity-path", "parity-rpc", "parity-runtime", + "parity-secretstore", "parity-updater", "parity-util-mem", "parity-version", @@ -3385,7 +3377,7 @@ dependencies = [ "registrar", "rlp", "rpassword", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", "rustc_version", "semver", "serde", @@ -3423,7 +3415,7 @@ dependencies = [ "parking_lot 0.10.0", "rand 0.7.2", "registrar", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", ] [[package]] @@ -3505,7 +3497,7 @@ dependencies = [ "keccak-hash", "log", "machine", - "macros", + "maplit", "multihash", "order-stat", "parity-bytes", @@ -3516,9 +3508,9 @@ dependencies = [ "parking_lot 0.10.0", "pretty_assertions", "rand 0.7.2", - "rand_xorshift", + "rand_xorshift 0.2.0", "rlp", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", "semver", "serde", "serde_derive", @@ -3527,7 +3519,7 @@ dependencies = [ "spec", "stats", "tempdir", - "tiny-keccak", + "tiny-keccak 1.5.0", "tokio-timer 0.1.2", "trace", "transaction-pool", @@ -3556,7 +3548,9 @@ dependencies = [ [[package]] name = "parity-runtime" -version = "0.1.0" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710e8d8e9769827952aa83a44d33bc993658cccd97e15e3b5eb070d1a70d1a3a" dependencies = [ "futures", "tokio", @@ -3575,15 +3569,38 @@ dependencies = [ ] [[package]] -name = "parity-secp256k1" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fca4f82fccae37e8bbdaeb949a4a218a1bbc485d11598f193d2a908042e5fc1" +name = "parity-secretstore" +version = "1.0.0" +source = "git+https://github.com/paritytech/secret-store?rev=ebe751d#ebe751db6af07425d2e1823ac05a84d0fafe3dad" dependencies = [ - "arrayvec 0.5.1", - "cc", - "cfg-if", - "rand 0.7.2", + "byteorder", + "ethabi", + "ethabi-contract", + "ethabi-derive", + "ethereum-types", + "futures", + "hyper", + "jsonrpc-server-utils", + "keccak-hash", + "kvdb", + "kvdb-rocksdb", + "lazy_static", + "libsecp256k1", + "log", + "parity-bytes", + "parity-crypto", + "parity-runtime", + "parking_lot 0.10.0", + "percent-encoding 2.1.0", + "rustc-hex 1.0.0", + "serde", + "serde_derive", + "serde_json", + "tiny-keccak 1.5.0", + "tokio", + "tokio-io", + "tokio-service", + "url 2.1.0", ] [[package]] @@ -3832,6 +3849,18 @@ dependencies = [ "crunchy 0.1.6", ] +[[package]] +name = "plotters" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3bb8da247d27ae212529352020f3e5ee16e83c0c258061d27b08ab92675eeb" +dependencies = [ + "js-sys", + "num-traits 0.2.6", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "pod" version = "0.1.0" @@ -3845,11 +3874,11 @@ dependencies = [ "keccak-hasher 0.1.1", "kvdb", "log", - "macros", + "maplit", "parity-bytes", "patricia-trie-ethereum", "rlp", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", "serde", "trie-db", "triehash-ethereum", @@ -3988,7 +4017,7 @@ dependencies = [ "env_logger 0.5.13", "ethereum-types", "ethjson", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", "serde", "serde_derive", "serde_json", @@ -4057,6 +4086,25 @@ dependencies = [ "winapi 0.3.8", ] +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.7", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift 0.1.1", + "winapi 0.3.8", +] + [[package]] name = "rand" version = "0.7.2" @@ -4065,9 +4113,19 @@ checksum = "3ae1b169243eaf61759b8475a998f0a385e42042370f3a7dbaf35246eacc8412" dependencies = [ "getrandom", "libc", - "rand_chacha", + "rand_chacha 0.2.1", "rand_core 0.5.1", - "rand_hc", + "rand_hc 0.2.0", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.7", + "rand_core 0.3.1", ] [[package]] @@ -4113,6 +4171,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -4122,54 +4189,88 @@ dependencies = [ "rand_core 0.5.1", ] +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi 0.3.8", +] + [[package]] name = "rand_os" -version = "0.2.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a788ae3edb696cfcba1c19bfd388cc4b8c21f8a408432b199c072825084da58a" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" dependencies = [ - "getrandom", - "rand_core 0.5.1", + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi 0.3.8", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.7", + "rand_core 0.4.2", ] [[package]] name = "rand_xorshift" -version = "0.2.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" dependencies = [ - "rand_core 0.5.1", + "rand_core 0.3.1", ] [[package]] -name = "rand_xoshiro" -version = "0.3.1" +name = "rand_xorshift" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e18c91676f670f6f0312764c759405f13afb98d5d73819840cf72a518487bff" +checksum = "77d416b86801d23dde1aa643023b775c3a462efc0ed96443add11546cdf1dca8" dependencies = [ "rand_core 0.5.1", ] [[package]] name = "rayon" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4b0186e22767d5b9738a05eab7c6ac90b15db17e5b5f9bd87976dd7d89a10a4" +checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" dependencies = [ - "crossbeam-deque 0.6.3", + "crossbeam-deque 0.7.3", "either", "rayon-core", ] [[package]] name = "rayon-core" -version = "1.5.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebbe0df8435ac0c397d467b6cad6d25543d06e8a019ef3f6af3c384597515bd2" +checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" dependencies = [ - "crossbeam-deque 0.6.3", - "crossbeam-queue", - "crossbeam-utils 0.6.6", + "crossbeam-deque 0.7.3", + "crossbeam-queue 0.2.1", + "crossbeam-utils 0.7.2", "lazy_static", "num_cpus", ] @@ -4282,22 +4383,23 @@ dependencies = [ ] [[package]] -name = "rlp_compress" +name = "rlp-derive" version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33d7b2abe0c340d8797fe2907d3f20d3b5ea5908683618bfe80df7f621f672a" dependencies = [ - "elastic-array", - "lazy_static", - "rlp", + "proc-macro2 1.0.8", + "quote 1.0.2", + "syn 1.0.14", ] [[package]] -name = "rlp_derive" +name = "rlp_compress" version = "0.1.0" dependencies = [ - "proc-macro2 1.0.8", - "quote 1.0.2", + "elastic-array", + "lazy_static", "rlp", - "syn 1.0.14", ] [[package]] @@ -4452,6 +4554,25 @@ dependencies = [ "untrusted", ] +[[package]] +name = "secp256k1" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2932dc07acd2066ff2e3921a4419606b220ba6cd03a9935123856cc534877056" +dependencies = [ + "rand 0.6.5", + "secp256k1-sys", +] + +[[package]] +name = "secp256k1-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ab2c26f0d3552a0f12e639ae8a64afc2e3db9c52fe32f5fc6c289d38519f220" +dependencies = [ + "cc", +] + [[package]] name = "security-framework" version = "0.3.4" @@ -4668,9 +4789,9 @@ dependencies = [ "parking_lot 0.10.0", "patricia-trie-ethereum", "rand 0.7.2", - "rand_xorshift", + "rand_xorshift 0.2.0", "rlp", - "rlp_derive", + "rlp-derive", "scopeguard 1.0.0", "snapshot-tests", "spec", @@ -4714,7 +4835,7 @@ dependencies = [ "parking_lot 0.10.0", "patricia-trie-ethereum", "rand 0.7.2", - "rand_xorshift", + "rand_xorshift 0.2.0", "rlp", "snapshot", "spec", @@ -4768,6 +4889,7 @@ dependencies = [ "kvdb-memorydb", "log", "machine", + "maplit", "null-engine", "parity-bytes", "pod", @@ -4863,9 +4985,9 @@ checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" [[package]] name = "subtle" -version = "2.1.0" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01dca13cf6c3b179864ab3292bd794e757618d35a7766b7c46050c614ba00829" +checksum = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941" [[package]] name = "syn" @@ -5040,6 +5162,15 @@ dependencies = [ "crunchy 0.2.2", ] +[[package]] +name = "tiny-keccak" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2953ca5148619bc99695c1274cb54c5275bbb913c6adad87e72eaf8db9787f69" +dependencies = [ + "crunchy 0.2.2", +] + [[package]] name = "tinytemplate" version = "1.0.2" @@ -5211,8 +5342,8 @@ version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2bd2c6a3885302581f4401c82af70d792bb9df1700e7437b0aeb4ada94d5388c" dependencies = [ - "crossbeam-deque 0.7.1", - "crossbeam-queue", + "crossbeam-deque 0.7.3", + "crossbeam-queue 0.1.2", "crossbeam-utils 0.6.6", "futures", "lazy_static", @@ -5310,7 +5441,7 @@ dependencies = [ "parity-util-mem", "parking_lot 0.10.0", "rlp", - "rlp_derive", + "rlp-derive", "vm", ] @@ -5403,9 +5534,9 @@ checksum = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" [[package]] name = "typenum" -version = "1.10.0" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" +checksum = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" [[package]] name = "uint" @@ -5552,7 +5683,7 @@ dependencies = [ "parity-util-mem", "parking_lot 0.10.0", "rlp", - "rustc-hex 1.0.0", + "rustc-hex 2.1.0", "spec", "triehash-ethereum", "unexpected", @@ -5649,9 +5780,9 @@ dependencies = [ [[package]] name = "walkdir" -version = "2.2.7" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d9d7ed3431229a144296213105a390676cc49c9b6a72bd19f3176c98e129fa1" +checksum = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" dependencies = [ "same-file", "winapi 0.3.8", @@ -5921,21 +6052,6 @@ dependencies = [ [[package]] name = "zeroize" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45af6a010d13e4cf5b54c94ba5a2b2eba5596b9e46bf5875612d332a1f2b3f86" -dependencies = [ - "zeroize_derive", -] - -[[package]] -name = "zeroize_derive" -version = "0.9.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080616bd0e31f36095288bb0acdf1f78ef02c2fa15527d7e993f2a6c7591643e" -dependencies = [ - "proc-macro2 0.4.20", - "quote 0.6.8", - "syn 0.15.26", - "synstructure 0.10.1", -] +checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" diff --git a/Cargo.toml b/Cargo.toml index f8b90796b16..944b6a0f270 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,7 +30,6 @@ ethcore-logger = { path = "parity/logger" } ethcore-miner = { path = "miner" } ethcore-network = { path = "util/network" } ethcore-private-tx = { path = "ethcore/private-tx" } -ethcore-secretstore = { path = "secret-store", optional = true } ethcore-service = { path = "ethcore/service" } ethcore-sync = { path = "ethcore/sync" } ethereum-types = "0.8.0" @@ -49,14 +48,15 @@ num_cpus = "1.2" number_prefix = "0.2" panic_hook = { path = "util/panic-hook" } parity-bytes = "0.1" -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } parity-daemonize = "0.3" parity-hash-fetch = { path = "updater/hash-fetch" } parity-ipfs-api = { path = "ipfs" } parity-local-store = { path = "miner/local-store" } parity-path = "0.1" parity-rpc = { path = "rpc" } -parity-runtime = { path = "util/runtime" } +parity-runtime = "0.1.1" +parity-secretstore = { git = "https://github.com/paritytech/secret-store", rev = "ebe751d", optional = true } parity-updater = { path = "updater" } parity-util-mem = { version = "0.5.1", features = ["jemalloc-global"] } parity-version = { path = "util/version" } @@ -65,7 +65,7 @@ regex = "1.0" registrar = { path = "util/registrar" } rlp = "0.4.0" rpassword = "1.0" -rustc-hex = "1.0" +rustc-hex = "2.1.0" semver = "0.9" serde = "1.0" serde_derive = "1.0" @@ -98,16 +98,9 @@ test-heavy = ["ethcore/test-heavy"] evm-debug = ["ethcore/evm-debug"] evm-debug-tests = ["ethcore/evm-debug-tests"] slow-blocks = ["ethcore/slow-blocks"] -secretstore = ["ethcore-secretstore", "accounts", "ethabi", "ethcore-call-contract"] +secretstore = ["parity-secretstore", "accounts", "ethabi", "ethcore-call-contract"] final = ["parity-version/final"] deadlock_detection = ["parking_lot/deadlock_detection"] -# to create a memory profile (requires nightly rust), use e.g. -# `heaptrack /path/to/parity `, -# to visualize a memory profile, use `heaptrack_gui` -# or -# `valgrind --tool=massif /path/to/parity ` -# and `massif-visualizer` for visualization -memory_profiling = [] # hardcode version number 1.3.7 of parity to force an update # in order to manually test that parity fall-over to the local version # in case of invalid or deprecated command line arguments are entered @@ -134,7 +127,6 @@ members = [ "chainspec", "ethcore/wasm/run", "evmbin", - "util/EIP-2124" ] [patch.crates-io] diff --git a/README.md b/README.md index 4d1f3beb98a..660ac6ca32a 100644 --- a/README.md +++ b/README.md @@ -292,10 +292,6 @@ Caching, Importing Blocks, and Block Information ```bash parity-rpc ``` -* Parity Ethereum (EthCore) Secret Store - ```bash - ethcore-secretstore - ``` * Parity Updater Service ```bash parity-updater parity-hash-fetch @@ -303,9 +299,9 @@ Caching, Importing Blocks, and Block Information * Parity Core Libraries (Parity Util) ```bash ethcore-bloom-journal blooms-db dir eip-712 fake-fetch fastmap fetch ethcore-io - journaldb keccak-hasher len-caching-lock macros memory-cache memzero + journaldb keccak-hasher len-caching-lock memory-cache memzero migration-rocksdb ethcore-network ethcore-network-devp2p panic_hook - patricia-trie-ethereum registrar rlp_compress rlp_derive parity-runtime stats + patricia-trie-ethereum registrar rlp_compress parity-runtime stats time-utils triehash-ethereum unexpected parity-version ``` diff --git a/SMART_CONTRACT_LICENSE b/SMART_CONTRACT_LICENSE new file mode 100644 index 00000000000..296799337dc --- /dev/null +++ b/SMART_CONTRACT_LICENSE @@ -0,0 +1,49 @@ +SMART CONTRACT LICENSE v1.0 + + +Anyone may run, modify, publicly perform, distribute and redistribute this +software, and create derivative works based on it in each case in compliance +with the permissions granted by the document (“Permissions Document”) whose +**KECCAK256 HASH** equals the value found in the PUBLICLY READABLE VARIABLE +named permissionsDocumentHash on the **ETHEREUM MAINNET** **SMART CONTRACT +ACCOUNT** with the following ADDRESS 0x5a88CA36Fd58Efde3b955758285E8e3347D1eAe3 +which is deemed incorporated into this license by reference. In case of any +conflicts between this license and the Permissions Document, this license shall +prevail. + + +Where: +- KECCAK256 HASH is the cryptographic hash algorithm that may take any document, +expressed as a series of bytes, and convert it to a single value which may be +expressed as a number from 0 to `2**256`; +- ETHEREUM MAINNET is the peer-to-peer blockchain network and shared account +ledger initiated and recognised by the Ethereum Foundation as "Ethereum"; +- SMART CONTRACT ACCOUNT is a single account to be found on the ETHEREUM MAINNET +identified by an ADDRESS and which represents the combination of a computer +programme and some associated values; +- ADDRESS is a number between 0 and `2**160`, which is the primary means of +identifying a single account on the ETHEREUM MAINNET; +- PUBLICLY READABLE VARIABLE is an item in a smart contract storage, publicly +accessible via the smart contract’s ABI using a getter function matching its +name. + +This license supplements and does not replace any other license pertaining to +this software. + +No permissions are granted if no **KECCAK256 HASH** appears in the *ETHEREUM +MAINNET** **SMART CONTRACT ACCOUNT**. + +This license takes effect as a bare licence, and has the effect of granting +rights to each licensee (which may be subject to conditions), BUT IMPOSES NO +OBLIGATIONS OR LIABILITY ON ANY LICENSOR, ANY OWNER OF RIGHTS IN THE SOFTWARE, +OR ANY PERSON INVOLVED IN THE DESIGN, DEVELOPMENT AND CODING OF THE SOFTWARE +(“GRANTOR”). + +Each Grantor shall, to the maximum extent permitted by law, have no liability +for direct, indirect, special, incidental, consequential, exemplary, punitive or +other damages of any character including, without limitation, procurement of +substitute software or services, loss of use, data or profits, or business +interruption, however caused and on any theory of contract, warranty, tort +(including negligence), product liability or otherwise, arising in any way in +relation to the supply, licensing or operation of the software, even if advised +of the possibility of such damages. diff --git a/accounts/Cargo.toml b/accounts/Cargo.toml index 8535ff0ad02..ea303c8a272 100644 --- a/accounts/Cargo.toml +++ b/accounts/Cargo.toml @@ -11,7 +11,7 @@ edition = "2018" ethkey = { path = "ethkey" } ethstore = { path = "ethstore" } log = "0.4" -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } parking_lot = "0.10.0" serde = "1.0" serde_derive = "1.0" diff --git a/accounts/ethkey/Cargo.toml b/accounts/ethkey/Cargo.toml index b783adcf86d..9bf8afc449c 100644 --- a/accounts/ethkey/Cargo.toml +++ b/accounts/ethkey/Cargo.toml @@ -9,5 +9,5 @@ edit-distance = "2.0" log = "0.4" serde = "1.0" serde_derive = "1.0" -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } parity-wordlist = "1.3.1" diff --git a/accounts/ethkey/cli/Cargo.toml b/accounts/ethkey/cli/Cargo.toml index a0ab0724fea..ca7f1e725b6 100644 --- a/accounts/ethkey/cli/Cargo.toml +++ b/accounts/ethkey/cli/Cargo.toml @@ -9,9 +9,9 @@ docopt = "1.0" env_logger = "0.5" ethkey = { path = "../" } panic_hook = { path = "../../../util/panic-hook" } -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } parity-wordlist= "1.3.1" -rustc-hex = "1.0" +rustc-hex = "2.1.0" serde = "1.0" serde_derive = "1.0" threadpool = "1.7" diff --git a/accounts/ethkey/cli/src/main.rs b/accounts/ethkey/cli/src/main.rs index a2f26f34a0d..389e265eb45 100644 --- a/accounts/ethkey/cli/src/main.rs +++ b/accounts/ethkey/cli/src/main.rs @@ -199,7 +199,7 @@ fn execute(command: I) -> Result where I: IntoIterator(command: I) -> Result where I: IntoIterator = args.arg_prefix.from_hex()?; let brain = args.flag_brain; in_threads(move || { let iterations = 1024; @@ -271,7 +271,7 @@ fn execute(command: I) -> Result where I: IntoIterator. -use std::convert::Infallible; use parity_crypto::publickey::{KeyPair, Generator, Secret}; use parity_crypto::Keccak256; use parity_wordlist; @@ -33,9 +32,7 @@ impl Brain { } impl Generator for Brain { - type Error = Infallible; - - fn generate(&mut self) -> Result { + fn generate(&mut self) -> KeyPair { let seed = self.0.clone(); let mut secret = seed.into_bytes().keccak256(); @@ -51,7 +48,7 @@ impl Generator for Brain { { if pair.address()[0] == 0 { trace!("Testing: {}, got: {:?}", self.0, pair.address()); - return Ok(pair) + return pair } } }, @@ -68,8 +65,8 @@ mod tests { #[test] fn test_brain() { let words = "this is sparta!".to_owned(); - let first_keypair = Brain::new(words.clone()).generate().unwrap(); - let second_keypair = Brain::new(words.clone()).generate().unwrap(); + let first_keypair = Brain::new(words.clone()).generate(); + let second_keypair = Brain::new(words.clone()).generate(); assert_eq!(first_keypair.secret(), second_keypair.secret()); } } diff --git a/accounts/ethkey/src/brain_prefix.rs b/accounts/ethkey/src/brain_prefix.rs index 1fdd753fb5d..b916130d19e 100644 --- a/accounts/ethkey/src/brain_prefix.rs +++ b/accounts/ethkey/src/brain_prefix.rs @@ -39,15 +39,11 @@ impl BrainPrefix { pub fn phrase(&self) -> &str { &self.last_phrase } -} - -impl Generator for BrainPrefix { - type Error = Error; - fn generate(&mut self) -> Result { + pub fn generate(&mut self) -> Result { for _ in 0..self.iterations { let phrase = wordlist::random_phrase(self.no_of_words); - let keypair = Brain::new(phrase.clone()).generate().unwrap(); + let keypair = Brain::new(phrase.clone()).generate(); if keypair.address().as_ref().starts_with(&self.prefix) { self.last_phrase = phrase; return Ok(keypair) @@ -61,7 +57,6 @@ impl Generator for BrainPrefix { #[cfg(test)] mod tests { use BrainPrefix; - use parity_crypto::publickey::Generator; #[test] fn prefix_generator() { diff --git a/accounts/ethkey/src/brain_recover.rs b/accounts/ethkey/src/brain_recover.rs index e9673c843e2..0121fc67349 100644 --- a/accounts/ethkey/src/brain_recover.rs +++ b/accounts/ethkey/src/brain_recover.rs @@ -33,7 +33,7 @@ pub fn brain_recover( ) -> Option { let it = PhrasesIterator::from_known_phrase(known_phrase, expected_words); for phrase in it { - let keypair = Brain::new(phrase.clone()).generate().expect("Brain wallets are infallible; qed"); + let keypair = Brain::new(phrase.clone()).generate(); trace!("Testing: {}, got: {:?}", phrase, keypair.address()); if &keypair.address() == address { return Some(phrase); diff --git a/accounts/ethkey/src/prefix.rs b/accounts/ethkey/src/prefix.rs index 1e4d42c0f9b..cd1b16bfa06 100644 --- a/accounts/ethkey/src/prefix.rs +++ b/accounts/ethkey/src/prefix.rs @@ -24,19 +24,12 @@ pub struct Prefix { impl Prefix { pub fn new(prefix: Vec, iterations: usize) -> Self { - Prefix { - prefix: prefix, - iterations: iterations, - } + Prefix { prefix, iterations } } -} - -impl Generator for Prefix { - type Error = Error; - fn generate(&mut self) -> Result { + pub fn generate(&mut self) -> Result { for _ in 0..self.iterations { - let keypair = Random.generate()?; + let keypair = Random.generate(); if keypair.address().as_ref().starts_with(&self.prefix) { return Ok(keypair) } @@ -49,7 +42,6 @@ impl Generator for Prefix { #[cfg(test)] mod tests { use Prefix; - use parity_crypto::publickey::Generator; #[test] fn prefix_generator() { diff --git a/accounts/ethstore/Cargo.toml b/accounts/ethstore/Cargo.toml index 405455d7c82..9ebdaabca5b 100644 --- a/accounts/ethstore/Cargo.toml +++ b/accounts/ethstore/Cargo.toml @@ -12,11 +12,11 @@ ethkey = { path = "../ethkey" } serde = "1.0" serde_json = "1.0" serde_derive = "1.0" -rustc-hex = "1.0" +rustc-hex = "2.1.0" tiny-keccak = "1.4" time = "0.1.34" parking_lot = "0.10.0" -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } ethereum-types = "0.8.0" dir = { path = "../../util/dir" } smallvec = "1.2.0" diff --git a/accounts/ethstore/cli/Cargo.toml b/accounts/ethstore/cli/Cargo.toml index c12d2ebe9dd..4d48900c3e2 100644 --- a/accounts/ethstore/cli/Cargo.toml +++ b/accounts/ethstore/cli/Cargo.toml @@ -8,13 +8,13 @@ authors = ["Parity Technologies "] docopt = "1.0" env_logger = "0.5" num_cpus = "1.6" -rustc-hex = "1.0" +rustc-hex = "2.1.0" serde = "1.0" serde_derive = "1.0" parking_lot = "0.10.0" ethstore = { path = "../" } ethkey = { path = "../../ethkey" } -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } dir = { path = '../../../util/dir' } panic_hook = { path = "../../../util/panic-hook" } diff --git a/accounts/ethstore/src/account/crypto.rs b/accounts/ethstore/src/account/crypto.rs index faa04e645d6..b8e47893554 100644 --- a/accounts/ethstore/src/account/crypto.rs +++ b/accounts/ethstore/src/account/crypto.rs @@ -164,7 +164,7 @@ mod tests { #[test] fn crypto_with_secret_create() { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let passwd = "this is sparta".into(); let crypto = Crypto::with_secret(keypair.secret(), &passwd, 10240).unwrap(); let secret = crypto.secret(&passwd).unwrap(); @@ -173,7 +173,7 @@ mod tests { #[test] fn crypto_with_secret_invalid_password() { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let crypto = Crypto::with_secret(keypair.secret(), &"this is sparta".into(), 10240).unwrap(); assert_matches!(crypto.secret(&"this is sparta!".into()), Err(Error::InvalidPassword)) } diff --git a/accounts/ethstore/src/account/safe_account.rs b/accounts/ethstore/src/account/safe_account.rs index 691b41b9604..797914f6290 100644 --- a/accounts/ethstore/src/account/safe_account.rs +++ b/accounts/ethstore/src/account/safe_account.rs @@ -200,14 +200,14 @@ impl SafeAccount { #[cfg(test)] mod tests { - use crypto::publickey::{Generator, Random, verify_public, Message}; + use crypto::publickey::{Generator, Random, verify_public}; use super::SafeAccount; #[test] fn sign_and_verify_public() { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let password = "hello world".into(); - let message = Message::default(); + let message = [1u8; 32].into(); let account = SafeAccount::create(&keypair, [0u8; 16], &password, 10240, "Test".to_owned(), "{}".to_owned()); let signature = account.unwrap().sign(&password, &message).unwrap(); assert!(verify_public(keypair.public(), &signature, &message).unwrap()); @@ -215,11 +215,11 @@ mod tests { #[test] fn change_password() { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let first_password = "hello world".into(); let sec_password = "this is sparta".into(); let i = 10240; - let message = Message::default(); + let message = [1u8; 32].into(); let account = SafeAccount::create(&keypair, [0u8; 16], &first_password, i, "Test".to_owned(), "{}".to_owned()).unwrap(); let new_account = account.change_password(&first_password, &sec_password, i).unwrap(); assert!(account.sign(&first_password, &message).is_ok()); diff --git a/accounts/ethstore/src/accounts_dir/disk.rs b/accounts/ethstore/src/accounts_dir/disk.rs index e587ffc52ae..08da2cdef17 100644 --- a/accounts/ethstore/src/accounts_dir/disk.rs +++ b/accounts/ethstore/src/accounts_dir/disk.rs @@ -364,7 +364,7 @@ mod test { // given let mut dir = env::temp_dir(); dir.push("ethstore_should_create_new_account"); - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let password = "hello world".into(); let directory = RootDiskDirectory::create(dir.clone()).unwrap(); @@ -385,7 +385,7 @@ mod test { // given let mut dir = env::temp_dir(); dir.push("ethstore_should_handle_duplicate_filenames"); - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let password = "hello world".into(); let directory = RootDiskDirectory::create(dir.clone()).unwrap(); @@ -472,7 +472,7 @@ mod test { 15130871412783076140 ); - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let password = "test pass".into(); let account = SafeAccount::create(&keypair, [0u8; 16], &password, 1024, "Test".to_owned(), "{}".to_owned()); directory.insert(account.unwrap()).expect("Account should be inserted ok"); diff --git a/accounts/ethstore/src/ethstore.rs b/accounts/ethstore/src/ethstore.rs index be4fe6795bf..da77e7ab398 100644 --- a/accounts/ethstore/src/ethstore.rs +++ b/accounts/ethstore/src/ethstore.rs @@ -698,7 +698,7 @@ mod tests { use ethereum_types::H256; fn keypair() -> KeyPair { - Random.generate().unwrap() + Random.generate() } fn store() -> EthStore { @@ -820,6 +820,7 @@ mod tests { let passwd2 = "xzy".into(); let multi_store = multi_store(); let keypair = keypair(); + let message = [1u8; 32].into(); let address = store.insert_account(SecretVaultRef::Root, keypair.secret().clone(), &passwd1).unwrap(); assert_eq!(multi_store.accounts().unwrap().len(), 0); @@ -828,7 +829,7 @@ mod tests { // then assert!(store.test_password(&address, &passwd1).unwrap(), "First password should work for store."); - assert!(multi_store.sign(&address, &passwd2, &Default::default()).is_ok(), "Second password should work for second store."); + assert!(multi_store.sign(&address, &passwd2, &message).is_ok(), "Second password should work for second store."); assert_eq!(multi_store.accounts().unwrap().len(), 1); } @@ -1092,8 +1093,9 @@ mod tests { let accounts = store.accounts().unwrap(); assert_eq!(accounts.len(), 2); + let message = [1u8; 32].into(); // and we can sign with the derived contract - assert!(store.sign(&derived, &"test".into(), &Default::default()).is_ok(), "Second password should work for second store."); + assert!(store.sign(&derived, &"test".into(), &message).is_ok(), "Second password should work for second store."); } #[test] diff --git a/accounts/ethstore/src/json/bytes.rs b/accounts/ethstore/src/json/bytes.rs index 0cb00c8d3b9..1900a1381b1 100644 --- a/accounts/ethstore/src/json/bytes.rs +++ b/accounts/ethstore/src/json/bytes.rs @@ -43,7 +43,7 @@ impl<'a> Deserialize<'a> for Bytes { impl Serialize for Bytes { fn serialize(&self, serializer: S) -> Result where S: Serializer { - serializer.serialize_str(&self.0.to_hex()) + serializer.serialize_str(&self.0.to_hex::()) } } diff --git a/accounts/ethstore/src/json/hash.rs b/accounts/ethstore/src/json/hash.rs index 1a699367a24..16d4ec63b05 100644 --- a/accounts/ethstore/src/json/hash.rs +++ b/accounts/ethstore/src/json/hash.rs @@ -49,8 +49,9 @@ macro_rules! impl_hash { impl Serialize for $name { fn serialize(&self, serializer: S) -> Result - where S: Serializer { - serializer.serialize_str(&self.0.to_hex()) + where S: Serializer + { + serializer.serialize_str(&self.0.to_hex::()) } } @@ -83,7 +84,7 @@ macro_rules! impl_hash { type Err = Error; fn from_str(value: &str) -> Result { - match value.from_hex() { + match value.from_hex::>() { Ok(ref hex) if hex.len() == $size => { let mut hash = [0u8; $size]; hash.clone_from_slice(hex); diff --git a/accounts/ethstore/src/json/id.rs b/accounts/ethstore/src/json/id.rs index 0da0a3b83a0..92e31cf0094 100644 --- a/accounts/ethstore/src/json/id.rs +++ b/accounts/ethstore/src/json/id.rs @@ -15,6 +15,7 @@ // along with Parity Ethereum. If not, see . //! Universaly unique identifier. + use std::{fmt, str}; use rustc_hex::{ToHex, FromHex}; use serde::{Deserialize, Serialize, Deserializer, Serializer}; @@ -62,7 +63,7 @@ impl fmt::Display for Uuid { } fn copy_into(from: &str, into: &mut [u8]) -> Result<(), Error> { - let from = from.from_hex().map_err(|_| Error::InvalidUuid)?; + let from: Vec = from.from_hex().map_err(|_| Error::InvalidUuid)?; if from.len() != into.len() { return Err(Error::InvalidUuid); diff --git a/accounts/ethstore/tests/api.rs b/accounts/ethstore/tests/api.rs index a5d39f346e6..0866dd67406 100644 --- a/accounts/ethstore/tests/api.rs +++ b/accounts/ethstore/tests/api.rs @@ -42,7 +42,7 @@ fn secret_store_open_not_existing() { } fn random_secret() -> Secret { - Random.generate().unwrap().secret().clone() + Random.generate().secret().clone() } #[test] @@ -62,9 +62,10 @@ fn secret_store_sign() { let store = EthStore::open(Box::new(dir)).unwrap(); assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok()); let accounts = store.accounts().unwrap(); + let message = [1u8; 32].into(); assert_eq!(accounts.len(), 1); - assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_ok()); - assert!(store.sign(&accounts[0], &"1".into(), &Default::default()).is_err()); + assert!(store.sign(&accounts[0], &"".into(), &message).is_ok()); + assert!(store.sign(&accounts[0], &"1".into(), &message).is_err()); } #[test] @@ -73,11 +74,12 @@ fn secret_store_change_password() { let store = EthStore::open(Box::new(dir)).unwrap(); assert!(store.insert_account(SecretVaultRef::Root, random_secret(), &"".into()).is_ok()); let accounts = store.accounts().unwrap(); + let message = [1u8; 32].into(); assert_eq!(accounts.len(), 1); - assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_ok()); + assert!(store.sign(&accounts[0], &"".into(), &message).is_ok()); assert!(store.change_password(&accounts[0], &"".into(), &"1".into()).is_ok()); - assert!(store.sign(&accounts[0], &"".into(), &Default::default()).is_err()); - assert!(store.sign(&accounts[0], &"1".into(), &Default::default()).is_ok()); + assert!(store.sign(&accounts[0], &"".into(), &message).is_err()); + assert!(store.sign(&accounts[0], &"1".into(), &message).is_ok()); } #[test] @@ -95,7 +97,7 @@ fn secret_store_remove_account() { fn test_path() -> &'static str { match ::std::fs::metadata("ethstore") { Ok(_) => "ethstore/tests/res/geth_keystore", - Err(_) => "tests/res/geth_keystore", + Err(_) => "tests/res/geth_keystore", } } @@ -148,7 +150,7 @@ fn test_decrypting_files_with_short_ciphertext() { StoreAccountRef::root(Address::from_str("d1e64e5480bfaf733ba7d48712decb8227797a4e").unwrap()), ]); - let message = Default::default(); + let message = [1u8; 32].into(); let s1 = store.sign(&accounts[0], &"foo".into(), &message).unwrap(); let s2 = store.sign(&accounts[1], &"foo".into(), &message).unwrap(); diff --git a/accounts/src/lib.rs b/accounts/src/lib.rs index 1e470b784f9..a2a08cd1a14 100644 --- a/accounts/src/lib.rs +++ b/accounts/src/lib.rs @@ -125,7 +125,7 @@ impl AccountProvider { /// Creates new random account and returns address and public key pub fn new_account_and_public(&self, password: &Password) -> Result<(Address, Public), Error> { - let acc = Random.generate().expect("secp context has generation capabilities; qed"); + let acc = Random.generate(); let public = acc.public().clone(); let secret = acc.secret().clone(); let account = self.sstore.insert_account(SecretVaultRef::Root, secret, password)?; @@ -290,9 +290,10 @@ impl AccountProvider { let secret = self.sstore.raw_secret(&account, &password)?; self.unlocked_secrets.write().insert(account.clone(), secret); } else { - // verify password by signing dump message + // verify password by signing a dummy message // result may be discarded - let _ = self.sstore.sign(&account, &password, &Default::default())?; + let dummy_msg = [1u8;32].into(); + let _ = self.sstore.sign(&account, &password, &dummy_msg)?; } let data = AccountData { unlock, password }; @@ -509,18 +510,19 @@ mod tests { #[test] fn unlock_account_temp() { - let kp = Random.generate().unwrap(); + let kp = Random.generate(); let ap = AccountProvider::transient_provider(); + let dummy_msg = [1u8; 32].into(); assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok()); assert!(ap.unlock_account_temporarily(kp.address(), "test1".into()).is_err()); assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_err()); + assert!(ap.sign(kp.address(), None, dummy_msg).is_ok()); + assert!(ap.sign(kp.address(), None, dummy_msg).is_err()); } #[test] fn derived_account_nosave() { - let kp = Random.generate().unwrap(); + let kp = Random.generate(); let ap = AccountProvider::transient_provider(); assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok()); assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok()); @@ -538,7 +540,7 @@ mod tests { #[test] fn derived_account_save() { - let kp = Random.generate().unwrap(); + let kp = Random.generate(); let ap = AccountProvider::transient_provider(); assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok()); assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok()); @@ -559,7 +561,7 @@ mod tests { #[test] fn derived_account_sign() { - let kp = Random.generate().unwrap(); + let kp = Random.generate(); let ap = AccountProvider::transient_provider(); assert!(ap.insert_account(kp.secret().clone(), &"base".into()).is_ok()); assert!(ap.unlock_account_permanently(kp.address(), "base".into()).is_ok()); @@ -573,7 +575,7 @@ mod tests { ap.unlock_account_permanently(derived_addr, "base".into()) .expect("Should be ok because account is saved and password is valid"); - let msg = Default::default(); + let msg = [2u8; 32].into(); let signed_msg1 = ap.sign(derived_addr, None, msg) .expect("Signing with existing unlocked account should not fail"); let signed_msg2 = ap.sign_derived( @@ -589,44 +591,48 @@ mod tests { #[test] fn unlock_account_perm() { - let kp = Random.generate().unwrap(); + let kp = Random.generate(); let ap = AccountProvider::transient_provider(); + let dummy_msg = [1u8; 32].into(); assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok()); assert!(ap.unlock_account_permanently(kp.address(), "test1".into()).is_err()); assert!(ap.unlock_account_permanently(kp.address(), "test".into()).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); + assert!(ap.sign(kp.address(), None, dummy_msg).is_ok()); + assert!(ap.sign(kp.address(), None, dummy_msg).is_ok()); assert!(ap.unlock_account_temporarily(kp.address(), "test".into()).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); + assert!(ap.sign(kp.address(), None, dummy_msg).is_ok()); + assert!(ap.sign(kp.address(), None, dummy_msg).is_ok()); } #[test] fn unlock_account_timer() { - let kp = Random.generate().unwrap(); + let kp = Random.generate(); let ap = AccountProvider::transient_provider(); + let dummy_msg = [1u8; 32].into(); + assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok()); assert!(ap.unlock_account_timed(kp.address(), "test1".into(), Duration::from_secs(60)).is_err()); assert!(ap.unlock_account_timed(kp.address(), "test".into(), Duration::from_secs(60)).is_ok()); - assert!(ap.sign(kp.address(), None, Default::default()).is_ok()); + assert!(ap.sign(kp.address(), None, dummy_msg).is_ok()); ap.unlocked.write().get_mut(&StoreAccountRef::root(kp.address())).unwrap().unlock = Unlock::Timed(Instant::now()); - assert!(ap.sign(kp.address(), None, Default::default()).is_err()); + assert!(ap.sign(kp.address(), None, dummy_msg).is_err()); } #[test] fn should_sign_and_return_token() { // given - let kp = Random.generate().unwrap(); + let kp = Random.generate(); let ap = AccountProvider::transient_provider(); + let dummy_msg = [1u8; 32].into(); assert!(ap.insert_account(kp.secret().clone(), &"test".into()).is_ok()); // when - let (_signature, token) = ap.sign_with_token(kp.address(), "test".into(), Default::default()).unwrap(); + let (_signature, token) = ap.sign_with_token(kp.address(), "test".into(), dummy_msg).unwrap(); // then - ap.sign_with_token(kp.address(), token.clone(), Default::default()) + ap.sign_with_token(kp.address(), token.clone(), dummy_msg) .expect("First usage of token should be correct."); - assert!(ap.sign_with_token(kp.address(), token, Default::default()).is_err(), "Second usage of the same token should fail."); + assert!(ap.sign_with_token(kp.address(), token, dummy_msg).is_err(), "Second usage of the same token should fail."); } #[test] diff --git a/ethash/Cargo.toml b/ethash/Cargo.toml index 599777b9a42..abf932f2dd2 100644 --- a/ethash/Cargo.toml +++ b/ethash/Cargo.toml @@ -17,7 +17,8 @@ static_assertions = "1.1.0" [dev-dependencies] criterion = "0.3" -rustc-hex = "1.0" +hex-literal = "0.2.1" +rustc-hex = "2.1.0" serde_json = "1.0" tempdir = "0.3" diff --git a/ethash/benches/progpow.rs b/ethash/benches/progpow.rs index fdf8415f1ed..e84bc602ad1 100644 --- a/ethash/benches/progpow.rs +++ b/ethash/benches/progpow.rs @@ -1,15 +1,17 @@ #[macro_use] extern crate criterion; + +#[macro_use] +extern crate hex_literal; + +extern crate common_types; extern crate ethash; -extern crate rustc_hex; extern crate tempdir; -extern crate common_types; use criterion::Criterion; use ethash::progpow; use tempdir::TempDir; -use rustc_hex::FromHex; use ethash::NodeCacheBuilder; use ethash::compute::light_compute; use common_types::engines::OptimizeFor; @@ -18,7 +20,7 @@ fn bench_hashimoto_light(c: &mut Criterion) { let builder = NodeCacheBuilder::new(OptimizeFor::Memory, u64::max_value()); let tempdir = TempDir::new("").unwrap(); let light = builder.light(&tempdir.path(), 1); - let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f").unwrap(); + let h = hex!("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f"); let mut hash = [0; 32]; hash.copy_from_slice(&h); @@ -32,7 +34,7 @@ fn bench_progpow_light(c: &mut Criterion) { let tempdir = TempDir::new("").unwrap(); let cache = builder.new_cache(tempdir.into_path(), 0); - let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f").unwrap(); + let h = hex!("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f"); let mut hash = [0; 32]; hash.copy_from_slice(&h); @@ -56,7 +58,7 @@ fn bench_progpow_optimal_light(c: &mut Criterion) { let cache = builder.new_cache(tempdir.into_path(), 0); let c_dag = progpow::generate_cdag(cache.as_ref()); - let h = FromHex::from_hex("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f").unwrap(); + let h = hex!("c9149cc0386e689d789a1c2f3d5d169a61a6218ed30e74414dc736e442ef3d1f"); let mut hash = [0; 32]; hash.copy_from_slice(&h); diff --git a/ethash/src/lib.rs b/ethash/src/lib.rs index 8fc4510a44a..cb83a0daab8 100644 --- a/ethash/src/lib.rs +++ b/ethash/src/lib.rs @@ -26,6 +26,10 @@ extern crate log; #[macro_use] extern crate static_assertions; +#[cfg(test)] +#[macro_use] +extern crate hex_literal; + #[cfg(test)] extern crate rustc_hex; @@ -35,6 +39,7 @@ extern crate serde_json; #[cfg(test)] extern crate tempdir; + #[cfg(feature = "bench")] pub mod compute; #[cfg(not(feature = "bench"))] diff --git a/ethash/src/progpow.rs b/ethash/src/progpow.rs index 4c18c255a91..b02892a4a4b 100644 --- a/ethash/src/progpow.rs +++ b/ethash/src/progpow.rs @@ -431,7 +431,7 @@ mod test { use super::*; fn h256(hex: &str) -> H256 { - let bytes = FromHex::from_hex(hex).unwrap(); + let bytes: Vec = FromHex::from_hex(hex).unwrap(); let mut res = [0; 32]; res.copy_from_slice(&bytes); res @@ -549,8 +549,8 @@ mod test { &c_dag, ); - let expected_digest = FromHex::from_hex("b3bad9ca6f7c566cf0377d1f8cce29d6516a96562c122d924626281ec948ef02").unwrap(); - let expected_result = FromHex::from_hex("f4ac202715ded4136e72887c39e63a4738331c57fd9eb79f6ec421c281aa8743").unwrap(); + let expected_digest = hex!("b3bad9ca6f7c566cf0377d1f8cce29d6516a96562c122d924626281ec948ef02"); + let expected_result = hex!("f4ac202715ded4136e72887c39e63a4738331c57fd9eb79f6ec421c281aa8743"); assert_eq!( digest.to_vec(), diff --git a/ethcore/Cargo.toml b/ethcore/Cargo.toml index bf8b971bcdd..98ee4cb2986 100644 --- a/ethcore/Cargo.toml +++ b/ethcore/Cargo.toml @@ -36,14 +36,13 @@ kvdb-memorydb = { version = "0.4.0", optional = true } kvdb-sled = { version = "0.1", optional = true } lazy_static = { version = "1.3", optional = true } log = "0.4" -macros = { path = "../util/macros", optional = true } machine = { path = "./machine" } memory-cache = { path = "../util/memory-cache" } parity-bytes = "0.1" parking_lot = "0.10.0" pod = { path = "pod", optional = true } trie-db = "0.20.0" -parity-crypto = { version = "0.4.2", features = ["publickey"], optional = true } +parity-crypto = { version = "0.5.0", features = ["publickey"], optional = true } patricia-trie-ethereum = { path = "../util/patricia-trie-ethereum" } rand = "0.7" rand_xorshift = "0.2" @@ -76,14 +75,13 @@ env_logger = "0.5" ethcore-accounts = { path = "../accounts" } ethcore-builtin = { path = "./builtin" } ethjson = { path = "../json", features = ["test-helpers"] } -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } fetch = { path = "../util/fetch" } kvdb-memorydb = "0.4.0" kvdb-sled = "0.1" lazy_static = "1.3" machine = { path = "./machine", features = ["test-helpers"] } -macros = { path = "../util/macros" } -parity-runtime = { path = "../util/runtime" } +parity-runtime = "0.1.1" serde_json = "1.0" stats = { path = "../util/stats" } pod = { path = "pod" } @@ -91,7 +89,7 @@ tempdir = "0.3" trie-standardmap = "0.15.0" [features] -parity = ["work-notify", "price-info", "stratum", "macros"] +parity = ["work-notify", "price-info", "stratum"] # Large optional features that are enabled by default for Parity, # but might be omitted for other dependent crates. work-notify = ["ethcore-miner/work-notify"] @@ -127,7 +125,6 @@ test-helpers = [ "parity-crypto", "kvdb-memorydb", "kvdb-sled", - "macros", "pod", "tempdir", "basic-authority/test-helpers" diff --git a/ethcore/account-state/src/state.rs b/ethcore/account-state/src/state.rs index 6155b7f09fb..b7e02eef556 100644 --- a/ethcore/account-state/src/state.rs +++ b/ethcore/account-state/src/state.rs @@ -352,7 +352,7 @@ impl State { fn insert_cache(&self, address: &Address, account: AccountEntry) { // Dirty account which is not in the cache means this is a new account. - // It goes directly into the checkpoint as there's nothing to rever to. + // It goes directly into the checkpoint as there's nothing to revert to. // // In all other cases account is read as clean first, and after that made // dirty in and added to the checkpoint with `note_cache`. @@ -434,7 +434,7 @@ impl State { /// Get the nonce of account `a`. pub fn nonce(&self, a: &Address) -> TrieResult { self.ensure_cached(a, RequireCache::None, true, - |a| a.as_ref().map_or(self.account_start_nonce, |account| *account.nonce())) + |a| a.map_or(self.account_start_nonce, |account| *account.nonce())) } /// Whether the base storage root of an account remains unchanged. @@ -759,20 +759,21 @@ impl State { } /// Remove any touched empty or dust accounts. - pub fn kill_garbage(&mut self, touched: &HashSet
, remove_empty_touched: bool, min_balance: &Option, kill_contracts: bool) -> TrieResult<()> { - let to_kill: HashSet<_> = { - self.cache.borrow().iter().filter_map(|(address, ref entry)| - if touched.contains(address) && // Check all touched accounts - ((remove_empty_touched && entry.exists_and_is_null()) // Remove all empty touched accounts. + pub fn kill_garbage(&mut self, touched: &HashSet
, min_balance: &Option, kill_contracts: bool) -> TrieResult<()> { + let to_kill: HashSet<_> = + touched.iter().filter_map(|address| { // Check all touched accounts + self.cache.borrow().get(address).and_then(|entry| { + if entry.exists_and_is_null() // Remove all empty touched accounts. || min_balance.map_or(false, |ref balance| entry.account.as_ref().map_or(false, |account| - (account.is_basic() || kill_contracts) // Remove all basic and optionally contract accounts where balance has been decreased. - && account.balance() < balance && entry.old_balance.as_ref().map_or(false, |b| account.balance() < b)))) { + (account.is_basic() || kill_contracts) // Remove all basic and optionally contract accounts where balance has been decreased. + && account.balance() < balance && entry.old_balance.as_ref().map_or(false, |b| account.balance() < b))) { + Some(address) + } else { None } + }) + }).collect(); - Some(address.clone()) - } else { None }).collect() - }; for address in to_kill { - self.kill_account(&address); + self.kill_account(address) } Ok(()) } @@ -1014,13 +1015,13 @@ impl State { } /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. - pub fn require<'a>(&'a self, a: &Address, require_code: bool) -> TrieResult> { + pub fn require(&self, a: &Address, require_code: bool) -> TrieResult> { self.require_or_from(a, require_code, || Account::new_basic(0u8.into(), self.account_start_nonce), |_| {}) } /// Pull account `a` in our cache from the trie DB. `require_code` requires that the code be cached, too. /// If it doesn't exist, make account equal the evaluation of `default`. - pub fn require_or_from<'a, F, G>(&'a self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult> + pub fn require_or_from(&self, a: &Address, require_code: bool, default: F, not_default: G) -> TrieResult> where F: FnOnce() -> Account, G: FnOnce(&mut Account), { let contains_key = self.cache.borrow().contains_key(a); @@ -1137,8 +1138,8 @@ impl State { } } -//// TODO: cloning for `State` shouldn't be possible in general; Remove this and use -//// checkpoints where possible. +// TODO: cloning for `State` shouldn't be possible in general; Remove this and use +// checkpoints where possible. impl Clone for State { fn clone(&self) -> State { let cache = { diff --git a/ethcore/blockchain/Cargo.toml b/ethcore/blockchain/Cargo.toml index f8b57dfefc7..9531d3664d4 100644 --- a/ethcore/blockchain/Cargo.toml +++ b/ethcore/blockchain/Cargo.toml @@ -24,12 +24,12 @@ parking_lot = "0.10.0" rayon = "1.0" rlp = "0.4.0" rlp_compress = { path = "../../util/rlp-compress" } -rlp_derive = { path = "../../util/rlp-derive" } +rlp-derive = "0.1" triehash-ethereum = { version = "0.2", path = "../../util/triehash-ethereum" } [dev-dependencies] env_logger = "0.5" -parity-crypto = { version = "0.4.2", features = ["publickey"] } -rustc-hex = "1.0" +parity-crypto = { version = "0.5.0", features = ["publickey"] } +rustc-hex = "2.1.0" tempdir = "0.3" kvdb-memorydb = "0.4.0" diff --git a/ethcore/builtin/Cargo.toml b/ethcore/builtin/Cargo.toml index 0ce7f315e99..c252dd3f86f 100644 --- a/ethcore/builtin/Cargo.toml +++ b/ethcore/builtin/Cargo.toml @@ -16,8 +16,8 @@ keccak-hash = "0.4.0" log = "0.4" num = { version = "0.1", default-features = false, features = ["bigint"] } parity-bytes = "0.1" -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } [dev-dependencies] hex-literal = "0.2.1" -macros = { path = "../../util/macros" } +maplit = "1.0.2" diff --git a/ethcore/builtin/src/lib.rs b/ethcore/builtin/src/lib.rs index cd07720ab50..45d95476383 100644 --- a/ethcore/builtin/src/lib.rs +++ b/ethcore/builtin/src/lib.rs @@ -759,18 +759,18 @@ mod tests { PricingAt, AltBn128Pairing as JsonAltBn128PairingPricing, Pricing as JsonPricing, }; use hex_literal::hex; - use macros::map; + use maplit::btreemap; use num::{BigUint, Zero, One}; use parity_bytes::BytesRef; use super::{ - BTreeMap, Builtin, EthereumBuiltin, FromStr, Implementation, Linear, + Builtin, EthereumBuiltin, FromStr, Implementation, Linear, ModexpPricer, modexp as me, Pricing }; #[test] fn blake2f_cost() { let f = Builtin { - pricer: map![0 => Pricing::Blake2F(123)], + pricer: btreemap![0 => Pricing::Blake2F(123)], native: EthereumBuiltin::from_str("blake2_f").unwrap(), }; // 5 rounds @@ -784,7 +784,7 @@ mod tests { #[test] fn blake2f_cost_on_invalid_length() { let f = Builtin { - pricer: map![0 => Pricing::Blake2F(123)], + pricer: btreemap![0 => Pricing::Blake2F(123)], native: EthereumBuiltin::from_str("blake2_f").expect("known builtin"), }; // invalid input (too short) @@ -1031,7 +1031,7 @@ mod tests { #[test] fn modexp() { let f = Builtin { - pricer: map![0 => Pricing::Modexp(ModexpPricer { divisor: 20 })], + pricer: btreemap![0 => Pricing::Modexp(ModexpPricer { divisor: 20 })], native: EthereumBuiltin::from_str("modexp").unwrap(), }; @@ -1141,7 +1141,7 @@ mod tests { fn bn128_add() { let f = Builtin { - pricer: map![0 => Pricing::Linear(Linear { base: 0, word: 0 })], + pricer: btreemap![0 => Pricing::Linear(Linear { base: 0, word: 0 })], native: EthereumBuiltin::from_str("alt_bn128_add").unwrap(), }; @@ -1199,7 +1199,7 @@ mod tests { fn bn128_mul() { let f = Builtin { - pricer: map![0 => Pricing::Linear(Linear { base: 0, word: 0 })], + pricer: btreemap![0 => Pricing::Linear(Linear { base: 0, word: 0 })], native: EthereumBuiltin::from_str("alt_bn128_mul").unwrap(), }; @@ -1238,7 +1238,7 @@ mod tests { fn builtin_pairing() -> Builtin { Builtin { - pricer: map![0 => Pricing::Linear(Linear { base: 0, word: 0 })], + pricer: btreemap![0 => Pricing::Linear(Linear { base: 0, word: 0 })], native: EthereumBuiltin::from_str("alt_bn128_pairing").unwrap(), } } @@ -1317,7 +1317,7 @@ mod tests { fn is_active() { let pricer = Pricing::Linear(Linear { base: 10, word: 20 }); let b = Builtin { - pricer: map![100_000 => pricer], + pricer: btreemap![100_000 => pricer], native: EthereumBuiltin::from_str("identity").unwrap(), }; @@ -1330,7 +1330,7 @@ mod tests { fn from_named_linear() { let pricer = Pricing::Linear(Linear { base: 10, word: 20 }); let b = Builtin { - pricer: map![0 => pricer], + pricer: btreemap![0 => pricer], native: EthereumBuiltin::from_str("identity").unwrap(), }; @@ -1349,7 +1349,7 @@ mod tests { fn from_json() { let b = Builtin::try_from(ethjson::spec::Builtin { name: "identity".to_owned(), - pricing: map![ + pricing: btreemap![ 0 => PricingAt { info: None, price: JsonPricing::Linear(JsonLinearPricing { base: 10, word: 20 }) @@ -1372,7 +1372,7 @@ mod tests { fn bn128_pairing_eip1108_transition() { let b = Builtin::try_from(JsonBuiltin { name: "alt_bn128_pairing".to_owned(), - pricing: map![ + pricing: btreemap![ 10 => PricingAt { info: None, price: JsonPricing::AltBn128Pairing(JsonAltBn128PairingPricing { @@ -1398,7 +1398,7 @@ mod tests { fn bn128_add_eip1108_transition() { let b = Builtin::try_from(JsonBuiltin { name: "alt_bn128_add".to_owned(), - pricing: map![ + pricing: btreemap![ 10 => PricingAt { info: None, price: JsonPricing::Linear(JsonLinearPricing { @@ -1424,7 +1424,7 @@ mod tests { fn bn128_mul_eip1108_transition() { let b = Builtin::try_from(JsonBuiltin { name: "alt_bn128_mul".to_owned(), - pricing: map![ + pricing: btreemap![ 10 => PricingAt { info: None, price: JsonPricing::Linear(JsonLinearPricing { @@ -1451,7 +1451,7 @@ mod tests { fn multimap_use_most_recent_on_activate() { let b = Builtin::try_from(JsonBuiltin { name: "alt_bn128_mul".to_owned(), - pricing: map![ + pricing: btreemap![ 10 => PricingAt { info: None, price: JsonPricing::Linear(JsonLinearPricing { @@ -1489,7 +1489,7 @@ mod tests { fn multimap_use_last_with_same_activate_at() { let b = Builtin::try_from(JsonBuiltin { name: "alt_bn128_mul".to_owned(), - pricing: map![ + pricing: btreemap![ 1 => PricingAt { info: None, price: JsonPricing::Linear(JsonLinearPricing { diff --git a/ethcore/db/Cargo.toml b/ethcore/db/Cargo.toml index e1d04b4bc18..47d71f859a3 100644 --- a/ethcore/db/Cargo.toml +++ b/ethcore/db/Cargo.toml @@ -14,4 +14,4 @@ kvdb = "0.4.0" parity-util-mem = "0.5.1" parking_lot = "0.10.0" rlp = "0.4.0" -rlp_derive = { path = "../../util/rlp-derive" } +rlp-derive = "0.1" diff --git a/ethcore/db/src/cache_manager.rs b/ethcore/db/src/cache_manager.rs index b08f4c0bc26..cd3b037aeae 100644 --- a/ethcore/db/src/cache_manager.rs +++ b/ethcore/db/src/cache_manager.rs @@ -33,9 +33,9 @@ impl CacheManager where T: Eq + Hash { /// Create new cache manager with preferred (heap) sizes. pub fn new(pref_cache_size: usize, max_cache_size: usize, bytes_per_cache_entry: usize) -> Self { CacheManager { - pref_cache_size: pref_cache_size, - max_cache_size: max_cache_size, - bytes_per_cache_entry: bytes_per_cache_entry, + pref_cache_size, + max_cache_size, + bytes_per_cache_entry, cache_usage: (0..COLLECTION_QUEUE_SIZE).into_iter().map(|_| Default::default()).collect(), } } diff --git a/ethcore/engine/Cargo.toml b/ethcore/engine/Cargo.toml index fe527ade8be..d1408be7bff 100644 --- a/ethcore/engine/Cargo.toml +++ b/ethcore/engine/Cargo.toml @@ -13,7 +13,7 @@ bytes = { package = "parity-bytes", version = "0.1.0" } client-traits = { path = "../client-traits" } common-types = { path = "../types" } ethereum-types = "0.8.0" -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } machine = { path = "../machine" } vm = { path = "../vm" } diff --git a/ethcore/engines/authority-round/Cargo.toml b/ethcore/engines/authority-round/Cargo.toml index 4d2292ed4fa..8094447dd39 100644 --- a/ethcore/engines/authority-round/Cargo.toml +++ b/ethcore/engines/authority-round/Cargo.toml @@ -17,7 +17,7 @@ ethabi-contract = "9.0.0" ethabi-derive = "9.0.1" ethereum-types = "0.8.0" ethjson = { path = "../../../json" } -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } engine = { path = "../../engine" } io = { package = "ethcore-io", path = "../../../util/io" } itertools = "0.8.2" @@ -26,7 +26,6 @@ lazy_static = "1.3.0" log = "0.4" lru-cache = "0.1" machine = { path = "../../machine" } -macros = { path = "../../../util/macros" } parity-bytes = "0.1" parking_lot = "0.10.0" rand = "0.7" diff --git a/ethcore/engines/authority-round/src/lib.rs b/ethcore/engines/authority-round/src/lib.rs index 7d4c6e86c0f..588b239873d 100644 --- a/ethcore/engines/authority-round/src/lib.rs +++ b/ethcore/engines/authority-round/src/lib.rs @@ -44,12 +44,10 @@ use client_traits::{EngineClient, ForceUpdateSealing, TransactionRequest}; use engine::{Engine, ConstructedVerifier}; use block_gas_limit::block_gas_limit; use block_reward::{self, BlockRewardContract, RewardKind}; -use ethjson; use machine::{ ExecutedBlock, Machine, }; -use macros::map; use keccak_hash::keccak; use log::{info, debug, error, trace, warn}; use lru_cache::LruCache; @@ -487,8 +485,12 @@ impl EmptyStep { let message = keccak(empty_step_rlp(self.step, &self.parent_hash)); let correct_proposer = step_proposer(validators, &self.parent_hash, self.step); - parity_crypto::publickey::verify_address(&correct_proposer, &self.signature.into(), &message) - .map_err(|e| e.into()) + parity_crypto::publickey::verify_address( + &correct_proposer, + &self.signature.into(), + &message, + ) + .map_err(Into::into) } fn author(&self) -> Result { @@ -531,12 +533,17 @@ impl Decodable for EmptyStep { } } +/// Given a signature and an rlp encoded partial empty step containing the step number and parent +/// block hash), returns an RLP blob containing the signature and the partial empty step. This is +/// the wire encoding of an EmptyStep. pub fn empty_step_full_rlp(signature: &H520, empty_step_rlp: &[u8]) -> Vec { let mut s = RlpStream::new_list(2); s.append(signature).append_raw(empty_step_rlp, 1); s.out() } +/// Given the hash of the parent block and a step number, returns an RLP encoded partial empty step +/// ready to be signed. pub fn empty_step_rlp(step: u64, parent_hash: &H256) -> Vec { let mut s = RlpStream::new_list(2); s.append(&step).append(parent_hash); @@ -711,17 +718,22 @@ fn header_signature(header: &Header, empty_steps_transition: u64) -> Result().map(Into::into) } -// extracts the raw empty steps vec from the header seal. should only be called when there are 3 fields in the seal -// (i.e. header.number() >= self.empty_steps_transition) -fn header_empty_steps_raw(header: &Header) -> &[u8] { - header.seal().get(2).expect("was checked with verify_block_basic; has 3 fields; qed") +// Extracts the RLP bytes of the empty steps from the header seal. Returns data only when there are +// 3 fields in the seal. (i.e. header.number() >= self.empty_steps_transition). +fn header_empty_steps_raw(header: &Header) -> Option<&[u8]> { + header.seal().get(2).map(Vec::as_slice) } -// extracts the empty steps from the header seal. should only be called when there are 3 fields in the seal +// Extracts the empty steps from the header seal. Returns data only when there are 3 fields in the seal // (i.e. header.number() >= self.empty_steps_transition). fn header_empty_steps(header: &Header) -> Result, ::rlp::DecoderError> { - let empty_steps = Rlp::new(header_empty_steps_raw(header)).as_list::()?; - Ok(empty_steps.into_iter().map(|s| EmptyStep::from_sealed(s, header.parent_hash())).collect()) + header_empty_steps_raw(header).map_or(Ok(vec![]), |raw| { + let empty_steps = Rlp::new(raw).as_list::()?; + Ok(empty_steps + .into_iter() + .map(|s| EmptyStep::from_sealed(s, header.parent_hash())) + .collect()) + }) } // gets the signers of empty step messages for the given header, does not include repeated signers @@ -779,7 +791,7 @@ fn verify_external(header: &Header, validators: &dyn ValidatorSet, empty_steps_t let correct_proposer = validators.get(header.parent_hash(), header_step as usize); let is_invalid_proposer = *header.author() != correct_proposer || { let empty_steps_rlp = if header.number() >= empty_steps_transition { - Some(header_empty_steps_raw(header)) + header_empty_steps_raw(header) } else { None }; @@ -862,8 +874,7 @@ impl AuthorityRound { }; durations.push(dur_info); for (time, dur) in our_params.step_durations.iter().skip(1) { - let (step, time) = next_step_time_duration(dur_info, *time) - .ok_or(BlockError::TimestampOverflow)?; + let (step, time) = next_step_time_duration(dur_info, *time).ok_or(BlockError::TimestampOverflow)?; dur_info.transition_step = step; dur_info.transition_timestamp = time; dur_info.step_duration = *dur; @@ -934,6 +945,7 @@ impl AuthorityRound { }) } + /// Return the `EmptyStep`s matching the step interval (non-inclusive) and parent hash. fn empty_steps(&self, from_step: u64, to_step: u64, parent_hash: H256) -> Vec { let from = EmptyStep { step: from_step + 1, @@ -957,45 +969,43 @@ impl AuthorityRound { .collect() } + /// Drops all `EmptySteps` less than or equal to the passed `step`, irregardless of the parent hash. fn clear_empty_steps(&self, step: u64) { - // clear old `empty_steps` messages let mut empty_steps = self.empty_steps.lock(); - *empty_steps = empty_steps.split_off(&EmptyStep { + if empty_steps.is_empty() { + return; + } + let next_empty_steps = empty_steps.split_off(&EmptyStep { step: step + 1, parent_hash: Default::default(), signature: Default::default(), }); + *empty_steps = next_empty_steps } - fn handle_empty_step_message(&self, empty_step: EmptyStep) { + fn store_empty_step(&self, empty_step: EmptyStep) { self.empty_steps.lock().insert(empty_step); } - fn generate_empty_step(&self, parent_hash: &H256) { + /// Build an EmptyStep and broadcast it to the network. + fn emit_empty_step(&self, parent_hash: &H256) { let step = self.step.inner.load(); let empty_step_rlp = empty_step_rlp(step, parent_hash); if let Ok(signature) = self.sign(keccak(&empty_step_rlp)).map(Into::into) { - let message_rlp = empty_step_full_rlp(&signature, &empty_step_rlp); - let parent_hash = *parent_hash; let empty_step = EmptyStep { signature, step, parent_hash }; trace!(target: "engine", "broadcasting empty step message: {:?}", empty_step); - self.broadcast_message(message_rlp); - self.handle_empty_step_message(empty_step); - + if let Ok(c) = self.upgrade_client_or("could not broadcast empty step message") { + self.store_empty_step(empty_step); + c.broadcast_consensus_message(empty_step_full_rlp(&signature, &empty_step_rlp)); + } } else { warn!(target: "engine", "generate_empty_step: FAIL: accounts secret key unavailable"); } } - fn broadcast_message(&self, message: Vec) { - if let Ok(c) = self.upgrade_client_or(None) { - c.broadcast_consensus_message(message); - } - } - fn report_skipped( &self, header: &Header, @@ -1264,22 +1274,20 @@ impl Engine for AuthorityRound { .map(ToString::to_string) .unwrap_or_default(); - let mut info = map![ - "step".into() => step, - "signature".into() => signature - ]; + let mut info = BTreeMap::new(); + info.insert("step".into(), step); + info.insert("signature".into(), signature); if header.number() >= self.empty_steps_transition { - let empty_steps = - if let Ok(empty_steps) = header_empty_steps(header).as_ref() { - format!("[{}]", - empty_steps.iter().fold( - "".to_string(), - |acc, e| if acc.len() > 0 { acc + ","} else { acc } + &e.to_string())) - - } else { - "".into() - }; + let empty_steps = header_empty_steps(header).as_ref().map_or(String::new(), |empty_steps| { + format!("[{}]", empty_steps.iter().fold(String::new(), |mut acc, e| { + if !acc.is_empty() { + acc.push(','); + } + acc.push_str(&e.to_string()); + acc + })) + }); info.insert("emptySteps".into(), empty_steps); } @@ -1362,6 +1370,7 @@ impl Engine for AuthorityRound { SealingState::Ready } + /// Handle incoming EmptyStep messages from the `Client`. fn handle_message(&self, rlp: &[u8]) -> Result<(), EngineError> { fn fmt_err(x: T) -> EngineError { EngineError::MalformedMessage(format!("{:?}", x)) @@ -1373,7 +1382,7 @@ impl Engine for AuthorityRound { if empty_step.verify(&*self.validators).unwrap_or(false) { if self.step.inner.check_future(empty_step.step).is_ok() { trace!(target: "engine", "handle_message: received empty step message {:?}", empty_step); - self.handle_empty_step_message(empty_step); + self.store_empty_step(empty_step); } else { trace!(target: "engine", "handle_message: empty step message from the future {:?}", empty_step); } @@ -1445,8 +1454,8 @@ impl Engine for AuthorityRound { empty_steps.len() < self.maximum_empty_steps { if self.step.can_propose.compare_and_swap(true, false, AtomicOrdering::SeqCst) { - trace!(target: "engine", "generate_seal: generating empty step at step={}, block=#{}", step, header.number()); - self.generate_empty_step(header.parent_hash()); + trace!(target: "engine", "generate_seal: emitting empty step at step={}, block=#{}", step, header.number()); + self.emit_empty_step(header.parent_hash()); } return Seal::None; @@ -1585,9 +1594,11 @@ impl Engine for AuthorityRound { /// Check the number of seal fields. fn verify_block_basic(&self, header: &Header) -> Result<(), Error> { if header.number() >= self.validate_score_transition && *header.difficulty() >= U256::from(U128::max_value()) { - return Err(From::from(BlockError::DifficultyOutOfBounds( - OutOfBounds { min: None, max: Some(U256::from(U128::max_value())), found: *header.difficulty() } - ))); + return Err(Error::Block(BlockError::DifficultyOutOfBounds(OutOfBounds { + min: None, + max: Some(U256::from(U128::max_value())), + found: *header.difficulty() + }))); } match verify_timestamp(&self.step.inner, header_step(header, self.empty_steps_transition)?) { @@ -1608,7 +1619,7 @@ impl Engine for AuthorityRound { self.validators.report_benign(header.author(), set_number, header.number()); } - Err(BlockError::InvalidSeal.into()) + Err(Error::Block(BlockError::InvalidSeal)) } Err(e) => Err(e.into()), Ok(()) => Ok(()), @@ -1714,9 +1725,9 @@ impl Engine for AuthorityRound { if header.number() >= self.validate_score_transition { let expected_difficulty = calculate_score(parent_step.into(), step.into(), empty_steps_len.into()); if header.difficulty() != &expected_difficulty { - return Err(From::from(BlockError::InvalidDifficulty(Mismatch { + return Err(Error::Block(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, - found: header.difficulty().clone() + found: *header.difficulty() }))); } } @@ -2548,7 +2559,7 @@ mod tests { gas: U256::from(53_000), value: U256::from(1), data: vec![], - }.fake_sign(addr2), None).unwrap(); + }.fake_sign(addr2)).unwrap(); let b2 = b2.close_and_lock().unwrap(); // we will now seal a block with 1tx and include the accumulated empty step message @@ -2872,26 +2883,39 @@ mod tests { }); let parent_hash = H256::from_low_u64_be(1); + let parent_hash2 = H256::from_low_u64_be(123); let signature = H520::default(); - let step = |step: u64| EmptyStep { - step, - parent_hash, - signature, - }; + let step = |step, parent_hash| EmptyStep { step, parent_hash, signature }; - engine.handle_empty_step_message(step(1)); - engine.handle_empty_step_message(step(3)); - engine.handle_empty_step_message(step(2)); - engine.handle_empty_step_message(step(1)); + engine.store_empty_step(step(1, parent_hash)); + engine.store_empty_step(step(3, parent_hash2)); + engine.store_empty_step(step(2, parent_hash)); + engine.store_empty_step(step(1, parent_hash2)); + engine.store_empty_step(step(4, parent_hash2)); - assert_eq!(engine.empty_steps(0, 4, parent_hash), vec![step(1), step(2), step(3)]); + assert_eq!( + engine.empty_steps(0, 4, parent_hash), + vec![step(1, parent_hash), step(2, parent_hash)] + ); assert_eq!(engine.empty_steps(2, 3, parent_hash), vec![]); - assert_eq!(engine.empty_steps(2, 4, parent_hash), vec![step(3)]); + assert_eq!(engine.empty_steps(2, 4, parent_hash), vec![]); + assert_eq!( + engine.empty_steps(2, 4, parent_hash2), + vec![step(3, parent_hash2)] + ); + assert_eq!( + engine.empty_steps(2, 5, parent_hash2), + vec![step(3, parent_hash2), step(4, parent_hash2)] + ); engine.clear_empty_steps(2); assert_eq!(engine.empty_steps(0, 3, parent_hash), vec![]); - assert_eq!(engine.empty_steps(0, 4, parent_hash), vec![step(3)]); + assert_eq!(engine.empty_steps(0, 4, parent_hash), vec![]); + assert_eq!( + engine.empty_steps(0, 5, parent_hash2), + vec![step(3, parent_hash2), step(4, parent_hash2)] + ); } #[test] @@ -2995,9 +3019,10 @@ mod tests { let params = AuthorityRoundParams::from(deserialized.params); for ((block_num1, address1), (block_num2, address2)) in params.block_reward_contract_transitions.iter().zip( - [(0u64, BlockRewardContract::new_from_address(Address::from_str("2000000000000000000000000000000000000002").unwrap())), - (7u64, BlockRewardContract::new_from_address(Address::from_str("3000000000000000000000000000000000000003").unwrap())), - (42u64, BlockRewardContract::new_from_address(Address::from_str("4000000000000000000000000000000000000004").unwrap())), + [ + (0u64, BlockRewardContract::new_from_address(Address::from_str("2000000000000000000000000000000000000002").unwrap())), + (7u64, BlockRewardContract::new_from_address(Address::from_str("3000000000000000000000000000000000000003").unwrap())), + (42u64, BlockRewardContract::new_from_address(Address::from_str("4000000000000000000000000000000000000004").unwrap())), ].iter()) { assert_eq!(block_num1, block_num2); diff --git a/ethcore/engines/basic-authority/Cargo.toml b/ethcore/engines/basic-authority/Cargo.toml index e6816d89a3b..7ba7280be31 100644 --- a/ethcore/engines/basic-authority/Cargo.toml +++ b/ethcore/engines/basic-authority/Cargo.toml @@ -12,7 +12,7 @@ common-types = { path = "../../types" } engine = { path = "../../engine" } ethereum-types = "0.8.0" ethjson = { path = "../../../json" } -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } log = "0.4.8" machine = { path = "../../machine" } parking_lot = "0.10.0" diff --git a/ethcore/engines/basic-authority/src/lib.rs b/ethcore/engines/basic-authority/src/lib.rs index f4501f698df..5c5efc88e17 100644 --- a/ethcore/engines/basic-authority/src/lib.rs +++ b/ethcore/engines/basic-authority/src/lib.rs @@ -75,7 +75,7 @@ fn verify_external(header: &Header, validators: &dyn ValidatorSet) -> Result<(), } match validators.contains(header.parent_hash(), &signer) { - false => Err(BlockError::InvalidSeal.into()), + false => Err(Error::Block(BlockError::InvalidSeal)), true => Ok(()) } } @@ -91,7 +91,7 @@ impl BasicAuthority { /// Create a new instance of BasicAuthority engine pub fn new(our_params: BasicAuthorityParams, machine: Machine) -> Self { BasicAuthority { - machine: machine, + machine, signer: RwLock::new(None), validators: new_validator_set(our_params.validators), } diff --git a/ethcore/engines/clique/Cargo.toml b/ethcore/engines/clique/Cargo.toml index a75a1884035..5eedf0c701a 100644 --- a/ethcore/engines/clique/Cargo.toml +++ b/ethcore/engines/clique/Cargo.toml @@ -11,14 +11,13 @@ client-traits = { path = "../../client-traits" } common-types = { path = "../../types" } ethereum-types = "0.8.0" ethjson = { path = "../../../json" } -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } engine = { path = "../../engine" } keccak-hash = "0.4.0" lazy_static = "1.3.0" log = "0.4" lru-cache = "0.1" machine = { path = "../../machine" } -macros = { path = "../../../util/macros" } rand = "0.7" parking_lot = "0.10.0" rlp = "0.4.0" diff --git a/ethcore/engines/clique/src/block_state.rs b/ethcore/engines/clique/src/block_state.rs index a6f8103571e..f082ee8d32b 100644 --- a/ethcore/engines/clique/src/block_state.rs +++ b/ethcore/engines/clique/src/block_state.rs @@ -33,7 +33,7 @@ use unexpected::Mismatch; use crate::{ util::{extract_signers, recover_creator}, - {VoteType, DIFF_INTURN, DIFF_NOTURN, NULL_AUTHOR, SIGNING_DELAY_NOTURN_MS}, + VoteType, DIFF_INTURN, DIFF_NOTURN, NULL_AUTHOR, SIGNING_DELAY_NOTURN_MS, }; /// Type that keeps track of the state for a given vote @@ -137,30 +137,30 @@ impl CliqueBlockState { // The signer is not authorized if !self.signers.contains(&creator) { trace!(target: "engine", "current state: {}", self); - Err(EngineError::NotAuthorized(creator))? + return Err(EngineError::NotAuthorized(creator).into()); } // The signer has signed a block too recently if self.recent_signers.contains(&creator) { trace!(target: "engine", "current state: {}", self); - Err(EngineError::CliqueTooRecentlySigned(creator))? + return Err(EngineError::CliqueTooRecentlySigned(creator).into()); } // Wrong difficulty let inturn = self.is_inturn(header.number(), &creator); if inturn && *header.difficulty() != DIFF_INTURN { - Err(BlockError::InvalidDifficulty(Mismatch { + return Err(Error::Block(BlockError::InvalidDifficulty(Mismatch { expected: DIFF_INTURN, found: *header.difficulty(), - }))? + }))); } if !inturn && *header.difficulty() != DIFF_NOTURN { - Err(BlockError::InvalidDifficulty(Mismatch { + return Err(Error::Block(BlockError::InvalidDifficulty(Mismatch { expected: DIFF_NOTURN, found: *header.difficulty(), - }))? + }))); } Ok(creator) @@ -178,9 +178,10 @@ impl CliqueBlockState { if self.signers != signers { let invalid_signers: Vec = signers.into_iter() .filter(|s| !self.signers.contains(s)) - .map(|s| format!("{}", s)) + .map(|s| s.to_string()) .collect(); - Err(EngineError::CliqueFaultyRecoveredSigners(invalid_signers))? + + return Err(EngineError::CliqueFaultyRecoveredSigners(invalid_signers).into()); }; // TODO(niklasad1): I'm not sure if we should shrink here because it is likely that next epoch @@ -196,11 +197,14 @@ impl CliqueBlockState { if *header.author() != NULL_AUTHOR { let decoded_seal = header.decode_seal::>()?; if decoded_seal.len() != 2 { - Err(BlockError::InvalidSealArity(Mismatch { expected: 2, found: decoded_seal.len() }))? + return Err(Error::Block(BlockError::InvalidSealArity(Mismatch { + expected: 2, + found: decoded_seal.len() + }))); } let nonce = H64::from_slice(decoded_seal[1]); - self.update_signers_on_vote(VoteType::from_nonce(nonce)?, creator, *header.author(), header.number())?; + self.update_signers_on_vote(VoteType::from_nonce(nonce)?, creator, *header.author(), header.number()); } Ok(creator) @@ -212,7 +216,7 @@ impl CliqueBlockState { signer: Address, beneficiary: Address, block_number: u64 - ) -> Result<(), Error> { + ) { trace!(target: "engine", "Attempt vote {:?} {:?}", kind, beneficiary); @@ -239,7 +243,7 @@ impl CliqueBlockState { // If no vote was found for the beneficiary return `early` but don't propogate an error let (votes, vote_kind) = match self.get_current_votes_and_kind(beneficiary) { Some((v, k)) => (v, k), - None => return Ok(()), + None => return, }; let threshold = self.signers.len() / 2; @@ -263,8 +267,6 @@ impl CliqueBlockState { self.rotate_recent_signers(); self.remove_all_votes_from(beneficiary); } - - Ok(()) } /// Calculate the next timestamp for `inturn` and `noturn` fails if any of them can't be represented as @@ -272,7 +274,7 @@ impl CliqueBlockState { // TODO(niklasad1): refactor this method to be in constructor of `CliqueBlockState` instead. // This is a quite bad API because we must mutate both variables even when already `inturn` fails // That's why we can't return early and must have the `if-else` in the end - pub fn calc_next_timestamp(&mut self, timestamp: u64, period: u64) -> Result<(), Error> { + pub fn calc_next_timestamp(&mut self, timestamp: u64, period: u64) -> Result<(), BlockError> { let inturn = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(timestamp.saturating_add(period))); self.next_timestamp_inturn = inturn; @@ -286,7 +288,7 @@ impl CliqueBlockState { if self.next_timestamp_inturn.is_some() && self.next_timestamp_noturn.is_some() { Ok(()) } else { - Err(BlockError::TimestampOverflow)? + Err(BlockError::TimestampOverflow) } } diff --git a/ethcore/engines/clique/src/lib.rs b/ethcore/engines/clique/src/lib.rs index 6638897edb3..8843e07badd 100644 --- a/ethcore/engines/clique/src/lib.rs +++ b/ethcore/engines/clique/src/lib.rs @@ -80,7 +80,6 @@ use machine::{ ExecutedBlock, Machine, }; -use macros::map; use parking_lot::RwLock; use rand::Rng; use unexpected::{Mismatch, OutOfBounds}; @@ -105,7 +104,6 @@ use crate::{ params::CliqueParams, }; - mod params; mod block_state; mod util; @@ -158,7 +156,7 @@ impl VoteType { } else if nonce == NONCE_DROP_VOTE { Ok(VoteType::Remove) } else { - Err(EngineError::CliqueInvalidNonce(nonce))? + Err(EngineError::CliqueInvalidNonce(nonce).into()) } } @@ -251,17 +249,14 @@ impl Clique { } } - fn sign_header(&self, header: &Header) -> Result<(Signature, H256), Error> { - + fn sign_header(&self, header: &Header) -> Result<(Signature, H256), EngineError> { match self.signer.read().as_ref() { - None => { - Err(EngineError::RequiresSigner)? - } + None => Err(EngineError::RequiresSigner), Some(signer) => { let digest = header.hash(); match signer.sign(digest) { Ok(sig) => Ok((sig, digest)), - Err(e) => Err(EngineError::Custom(e.into()))?, + Err(e) => Err(EngineError::Custom(e.to_string())), } } } @@ -297,9 +292,7 @@ impl Clique { } // BlockState is not found in memory, which means we need to reconstruct state from last checkpoint. match self.client.read().as_ref().and_then(|w| w.upgrade()) { - None => { - return Err(EngineError::RequiresClient)?; - } + None => return Err(EngineError::RequiresClient.into()), Some(c) => { let last_checkpoint_number = header.number() - header.number() % self.epoch_length as u64; debug_assert_ne!(last_checkpoint_number, header.number()); @@ -328,7 +321,7 @@ impl Clique { } match c.block_header(BlockId::Hash(last_parent_hash)) { None => { - return Err(BlockError::UnknownParent(last_parent_hash))?; + return Err(Error::Block(BlockError::UnknownParent(last_parent_hash))); } Some(next) => { chain.push_front(next.decode()?); @@ -342,7 +335,7 @@ impl Clique { .parent_hash(); let last_checkpoint_header = match c.block_header(BlockId::Hash(last_checkpoint_hash)) { - None => return Err(EngineError::CliqueMissingCheckpoint(last_checkpoint_hash))?, + None => return Err(EngineError::CliqueMissingCheckpoint(last_checkpoint_hash).into()), Some(header) => header.decode()?, }; @@ -354,15 +347,14 @@ impl Clique { block_state_by_hash.insert(last_checkpoint_header.hash(), last_checkpoint_state.clone()); // Backfill! - let mut new_state = last_checkpoint_state.clone(); + let mut new_state = last_checkpoint_state; for item in &chain { new_state.apply(item, false)?; } new_state.calc_next_timestamp(header.timestamp(), self.period)?; block_state_by_hash.insert(header.hash(), new_state.clone()); - let elapsed = backfill_start.elapsed(); - trace!(target: "engine", "Back-filling succeed, took {} ms.", elapsed.as_millis()); + trace!(target: "engine", "Back-filling succeed, took {} ms.", backfill_start.elapsed().as_millis()); Ok(new_state) } } @@ -379,13 +371,12 @@ impl Engine for Clique { fn extra_info(&self, header: &Header) -> BTreeMap { // clique engine seal fields are the same as ethash seal fields - match EthashSeal::parse_seal(header.seal()) { - Ok(seal) => map![ - "nonce".to_owned() => format!("{:#x}", seal.nonce), - "mixHash".to_owned() => format!("{:#x}", seal.mix_hash) - ], - _ => BTreeMap::default() + let mut engine_info = BTreeMap::new(); + if let Ok(seal) = EthashSeal::parse_seal(header.seal()) { + engine_info.insert("nonce".to_string(), format!("{:#x}", seal.nonce)); + engine_info.insert("mixHash".to_string(), format!("{:#x}", seal.mix_hash)); } + engine_info } fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 0 } @@ -411,9 +402,8 @@ impl Engine for Clique { trace!(target: "engine", "on_seal_block"); let header = &mut block.header; - - let state = self.state_no_backfill(header.parent_hash()) - .ok_or_else(|| BlockError::UnknownParent(*header.parent_hash()))?; + let mut state = self.state_no_backfill(header.parent_hash()) + .ok_or_else(|| Error::Block(BlockError::UnknownParent(*header.parent_hash())))?; let is_checkpoint = header.number() % self.epoch_length == 0; @@ -440,16 +430,15 @@ impl Engine for Clique { } // Work on clique seal. - let mut seal: Vec = Vec::with_capacity(VANITY_LENGTH + SIGNATURE_LENGTH); // At this point, extra_data should only contain miner vanity. if header.extra_data().len() != VANITY_LENGTH { - Err(BlockError::ExtraDataOutOfBounds(OutOfBounds { + return Err(Error::Block(BlockError::ExtraDataOutOfBounds(OutOfBounds { min: Some(VANITY_LENGTH), max: Some(VANITY_LENGTH), found: header.extra_data().len() - }))?; + }))); } // vanity { @@ -469,15 +458,14 @@ impl Engine for Clique { // append signature onto extra_data let (sig, _msg) = self.sign_header(&header)?; seal.extend_from_slice(&sig[..]); - header.set_extra_data(seal.clone()); + header.set_extra_data(seal); header.compute_hash(); // locally sealed block don't go through valid_block_family(), so we have to record state here. - let mut new_state = state.clone(); - new_state.apply(&header, is_checkpoint)?; - new_state.calc_next_timestamp(header.timestamp(), self.period)?; - self.block_state_by_hash.write().insert(header.hash(), new_state); + state.apply(&header, is_checkpoint)?; + state.calc_next_timestamp(header.timestamp(), self.period)?; + self.block_state_by_hash.write().insert(header.hash(), state); trace!(target: "engine", "on_seal_block: finished, final header: {:?}", header); @@ -576,18 +564,18 @@ impl Engine for Clique { // This should succeed under the constraints that the system clock works let limit_as_dur = limit.duration_since(UNIX_EPOCH).map_err(|e| { - Box::new(format!("Converting SystemTime to Duration failed: {}", e)) + format!("Converting SystemTime to Duration failed: {}", e) })?; let hdr = Duration::from_secs(header.timestamp()); if hdr > limit_as_dur { let found = CheckedSystemTime::checked_add(UNIX_EPOCH, hdr).ok_or(BlockError::TimestampOverflow)?; - Err(BlockError::TemporarilyInvalid(OutOfBounds { + return Err(Error::Block(BlockError::TemporarilyInvalid(From::from(OutOfBounds { min: None, max: Some(limit), found, - }.into()))? + })))); } } @@ -602,10 +590,10 @@ impl Engine for Clique { let seal_fields = header.decode_seal::>()?; if seal_fields.len() != 2 { - Err(BlockError::InvalidSealArity(Mismatch { + return Err(Error::Block(BlockError::InvalidSealArity(Mismatch { expected: 2, found: seal_fields.len(), - }))? + }))); } let mixhash = H256::from_slice(seal_fields[0]); @@ -613,58 +601,58 @@ impl Engine for Clique { // Nonce must be 0x00..0 or 0xff..f if nonce != NONCE_DROP_VOTE && nonce != NONCE_AUTH_VOTE { - Err(EngineError::CliqueInvalidNonce(nonce))?; + return Err(EngineError::CliqueInvalidNonce(nonce).into()); } if is_checkpoint && nonce != NULL_NONCE { - Err(EngineError::CliqueInvalidNonce(nonce))?; + return Err(EngineError::CliqueInvalidNonce(nonce).into()); } // Ensure that the mix digest is zero as Clique don't have fork protection currently if mixhash != NULL_MIXHASH { - Err(BlockError::MismatchedH256SealElement(Mismatch { + return Err(Error::Block(BlockError::MismatchedH256SealElement(Mismatch { expected: NULL_MIXHASH, found: mixhash, - }))? + }))); } let extra_data_len = header.extra_data().len(); if extra_data_len < VANITY_LENGTH { - Err(EngineError::CliqueMissingVanity)? + return Err(EngineError::CliqueMissingVanity.into()); } if extra_data_len < VANITY_LENGTH + SIGNATURE_LENGTH { - Err(EngineError::CliqueMissingSignature)? + return Err(EngineError::CliqueMissingSignature.into()); } let signers = extra_data_len - (VANITY_LENGTH + SIGNATURE_LENGTH); // Checkpoint blocks must at least contain one signer if is_checkpoint && signers == 0 { - Err(EngineError::CliqueCheckpointNoSigner)? + return Err(EngineError::CliqueCheckpointNoSigner.into()); } // Addresses must be be divisable by 20 if is_checkpoint && signers % ADDRESS_LENGTH != 0 { - Err(EngineError::CliqueCheckpointInvalidSigners(signers))? + return Err(EngineError::CliqueCheckpointInvalidSigners(signers).into()); } // Ensure that the block doesn't contain any uncles which are meaningless in PoA if *header.uncles_hash() != NULL_UNCLES_HASH { - Err(BlockError::InvalidUnclesHash(Mismatch { + return Err(Error::Block(BlockError::InvalidUnclesHash(Mismatch { expected: NULL_UNCLES_HASH, found: *header.uncles_hash(), - }))? + }))); } // Ensure that the block's difficulty is meaningful (may not be correct at this point) if *header.difficulty() != DIFF_INTURN && *header.difficulty() != DIFF_NOTURN { - Err(BlockError::DifficultyOutOfBounds(OutOfBounds { + return Err(Error::Block(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(DIFF_NOTURN), max: Some(DIFF_INTURN), found: *header.difficulty(), - }))? + }))); } // All basic checks passed, continue to next phase @@ -686,7 +674,7 @@ impl Engine for Clique { // parent sanity check if parent.hash() != *header.parent_hash() || header.number() != parent.number() + 1 { - Err(BlockError::UnknownParent(parent.hash()))? + return Err(Error::Block(BlockError::UnknownParent(parent.hash()))); } // Ensure that the block's timestamp isn't too close to it's parent @@ -696,17 +684,16 @@ impl Engine for Clique { let found = CheckedSystemTime::checked_add(UNIX_EPOCH, Duration::from_secs(limit)) .ok_or(BlockError::TimestampOverflow)?; - Err(BlockError::InvalidTimestamp(OutOfBounds { + return Err(Error::Block(BlockError::InvalidTimestamp(From::from(OutOfBounds { min: None, max, found, - }.into()))? + })))); } // Retrieve the parent state - let parent_state = self.state(&parent)?; // Try to apply current state, apply() will further check signer and recent signer. - let mut new_state = parent_state.clone(); + let mut new_state = self.state(&parent)?; new_state.apply(header, header.number() % self.epoch_length == 0)?; new_state.calc_next_timestamp(header.timestamp(), self.period)?; self.block_state_by_hash.write().insert(header.hash(), new_state); @@ -716,7 +703,7 @@ impl Engine for Clique { fn genesis_epoch_data(&self, header: &Header, _call: &Call) -> Result, String> { let mut state = self.new_checkpoint_state(header).expect("Unable to parse genesis data."); - state.calc_next_timestamp(header.timestamp(), self.period).map_err(|e| format!("{}", e))?; + state.calc_next_timestamp(header.timestamp(), self.period).map_err(|e| e.to_string())?; self.block_state_by_hash.write().insert(header.hash(), state); // no proof. diff --git a/ethcore/engines/ethash/Cargo.toml b/ethcore/engines/ethash/Cargo.toml index 9ea56f08afe..3afc1c951f0 100644 --- a/ethcore/engines/ethash/Cargo.toml +++ b/ethcore/engines/ethash/Cargo.toml @@ -16,7 +16,6 @@ ethjson = { path = "../../../json" } keccak-hash = "0.4.0" log = "0.4.8" machine = { path = "../../machine" } -macros = { path = "../../../util/macros" } unexpected = { path = "../../../util/unexpected" } [dev-dependencies] diff --git a/ethcore/engines/ethash/src/lib.rs b/ethcore/engines/ethash/src/lib.rs index 42ac9140c88..1f9223df78b 100644 --- a/ethcore/engines/ethash/src/lib.rs +++ b/ethcore/engines/ethash/src/lib.rs @@ -33,11 +33,9 @@ use common_types::{ }; use engine::Engine; use ethereum_types::{H256, U256}; -use ethjson; use ethash::{self, quick_get_difficulty, slow_hash_block_number, EthashManager}; -use keccak_hash::{KECCAK_EMPTY_LIST_RLP}; +use keccak_hash::KECCAK_EMPTY_LIST_RLP; use log::trace; -use macros::map; use machine::{ ExecutedBlock, Machine, @@ -181,7 +179,7 @@ fn verify_block_unordered(pow: &Arc, header: &Header) -> Result<( let seal = EthashSeal::parse_seal(header.seal())?; let result = pow.compute_light( - header.number() as u64, + header.number(), &header.bare_hash().0, seal.nonce.to_low_u64_be() ); @@ -195,10 +193,17 @@ fn verify_block_unordered(pow: &Arc, header: &Header) -> Result<( mix = H256(result.mix_hash), res = H256(result.value)); if mix != seal.mix_hash { - return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { expected: mix, found: seal.mix_hash }))); + return Err(From::from(BlockError::MismatchedH256SealElement(Mismatch { + expected: mix, + found: seal.mix_hash + }))); } if &difficulty < header.difficulty() { - return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); + return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { + min: Some(*header.difficulty()), + max: None, + found: difficulty + }))); } Ok(()) } @@ -232,18 +237,17 @@ impl Engine for Ethash { /// Additional engine-specific information for the user/developer concerning `header`. fn extra_info(&self, header: &Header) -> BTreeMap { - match EthashSeal::parse_seal(header.seal()) { - Ok(seal) => map![ - "nonce".to_owned() => format!("0x{:x}", seal.nonce), - "mixHash".to_owned() => format!("0x{:x}", seal.mix_hash) - ], - _ => BTreeMap::default() + let mut engine_info = BTreeMap::new(); + if let Ok(seal) = EthashSeal::parse_seal(header.seal()) { + engine_info.insert("nonce".to_string(), format!("{:#x}", seal.nonce)); + engine_info.insert("mixHash".to_string(), format!("{:#x}", seal.mix_hash)); } + engine_info } fn maximum_uncle_count(&self, _block: BlockNumber) -> usize { 2 } - fn maximum_gas_limit(&self) -> Option { Some(0x7fff_ffff_ffff_ffffu64.into()) } + fn maximum_gas_limit(&self) -> Option { Some(0x7fff_ffff_ffff_ffff_u64.into()) } /// Apply the block reward on finalisation of the block. /// This assumes that all uncles are valid uncles (i.e. of at least one generation before the current). @@ -327,7 +331,11 @@ impl Engine for Ethash { // TODO: consider removing these lines. let min_difficulty = self.ethash_params.minimum_difficulty; if header.difficulty() < &min_difficulty { - return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { min: Some(min_difficulty), max: None, found: header.difficulty().clone() }))) + return Err(From::from(BlockError::DifficultyOutOfBounds(OutOfBounds { + min: Some(min_difficulty), + max: None, + found: *header.difficulty(), + }))) } let difficulty = ethash::boundary_to_difficulty(&H256(quick_get_difficulty( @@ -338,7 +346,11 @@ impl Engine for Ethash { ))); if &difficulty < header.difficulty() { - return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { min: Some(header.difficulty().clone()), max: None, found: difficulty }))); + return Err(From::from(BlockError::InvalidProofOfWork(OutOfBounds { + min: Some(*header.difficulty()), + max: None, + found: difficulty + }))); } Ok(()) @@ -351,13 +363,20 @@ impl Engine for Ethash { fn verify_block_family(&self, header: &Header, parent: &Header) -> Result<(), Error> { // we should not calculate difficulty for genesis blocks if header.number() == 0 { - return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { min: Some(1), max: None, found: header.number() }))); + return Err(From::from(BlockError::RidiculousNumber(OutOfBounds { + min: Some(1), + max: None, + found: header.number() + }))); } // Check difficulty is correct given the two timestamps. let expected_difficulty = self.calculate_difficulty(header, parent); if header.difficulty() != &expected_difficulty { - return Err(From::from(BlockError::InvalidDifficulty(Mismatch { expected: expected_difficulty, found: header.difficulty().clone() }))) + return Err(From::from(BlockError::InvalidDifficulty(Mismatch { + expected: expected_difficulty, + found: *header.difficulty() + }))) } Ok(()) diff --git a/ethcore/engines/validator-set/Cargo.toml b/ethcore/engines/validator-set/Cargo.toml index 30f2ae04d62..aa8e34c4992 100644 --- a/ethcore/engines/validator-set/Cargo.toml +++ b/ethcore/engines/validator-set/Cargo.toml @@ -36,9 +36,9 @@ call-contract = { package = "ethcore-call-contract", path = "../../call-contract engine = { path = "../../engine", features = ["test-helpers"] } env_logger = "0.6.2" ethcore = { path = "../..", features = ["test-helpers"] } -parity-crypto = { version = "0.4.2", features = ["publickey"] } +rustc-hex = "2.1.0" keccak-hash = "0.4.0" -rustc-hex = "1.0" +parity-crypto = { version = "0.5.0", features = ["publickey"] } spec = { path = "../../spec" } [features] diff --git a/ethcore/engines/validator-set/src/safe_contract.rs b/ethcore/engines/validator-set/src/safe_contract.rs index 06bdd0035a3..c0f05620450 100644 --- a/ethcore/engines/validator-set/src/safe_contract.rs +++ b/ethcore/engines/validator-set/src/safe_contract.rs @@ -503,9 +503,10 @@ impl ValidatorSet for ValidatorSafeContract { receipts.iter().map(::rlp::encode) ); if found_root != *old_header.receipts_root() { - return Err(BlockError::InvalidReceiptsRoot( - Mismatch { expected: *old_header.receipts_root(), found: found_root } - ).into()); + return Err(EthcoreError::Block(BlockError::InvalidReceiptsRoot(Mismatch { + expected: *old_header.receipts_root(), + found: found_root + }))); } let bloom = self.expected_bloom(&old_header); diff --git a/ethcore/evm/Cargo.toml b/ethcore/evm/Cargo.toml index d43be54b433..f4f8f2665b5 100644 --- a/ethcore/evm/Cargo.toml +++ b/ethcore/evm/Cargo.toml @@ -17,9 +17,8 @@ parking_lot = "0.10.0" memory-cache = { path = "../../util/memory-cache" } [dev-dependencies] -rustc-hex = "1.0" criterion = "0.3" -hex-literal = "0.2.0" +hex-literal = "0.2.1" [features] evm-debug = [] diff --git a/ethcore/evm/benches/basic.rs b/ethcore/evm/benches/basic.rs index ed3a5b22a04..9a5ccec7b49 100644 --- a/ethcore/evm/benches/basic.rs +++ b/ethcore/evm/benches/basic.rs @@ -18,26 +18,29 @@ #[macro_use] extern crate criterion; + +#[macro_use] +extern crate hex_literal; + extern crate bit_set; extern crate ethereum_types; -extern crate parking_lot; -extern crate parity_util_mem as mem; -extern crate vm; extern crate evm; extern crate keccak_hash as hash; extern crate memory_cache; extern crate parity_bytes as bytes; -extern crate rustc_hex; +extern crate parity_util_mem as mem; +extern crate parking_lot; +extern crate vm; -use criterion::{Criterion, Bencher, black_box}; use std::str::FromStr; use std::sync::Arc; + +use criterion::{Criterion, Bencher, black_box}; use bytes::Bytes; use ethereum_types::{U256, Address}; use vm::{ActionParams, Result, GasLeft, Ext}; use vm::tests::FakeExt; use evm::Factory; -use rustc_hex::FromHex; criterion_group!( basic, @@ -91,9 +94,7 @@ fn simple_loop_log0(gas: U256, b: &mut Bencher) { let mut ext = FakeExt::new(); let address = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); - let code = black_box( - "62ffffff5b600190036000600fa0600357".from_hex().unwrap() - ); + let code = black_box(hex!("62ffffff5b600190036000600fa0600357").to_vec()); b.iter(|| { let mut params = ActionParams::default(); @@ -109,7 +110,7 @@ fn simple_loop_log0(gas: U256, b: &mut Bencher) { fn mem_gas_calculation_same_usize(b: &mut Criterion) { b.bench_function("mem_gas_calculation_same_usize", |b| { - mem_gas_calculation_same(U256::from(::std::usize::MAX), b); + mem_gas_calculation_same(U256::from(std::usize::MAX), b); }); } @@ -127,7 +128,7 @@ fn mem_gas_calculation_same(gas: U256, b: &mut Bencher) { b.iter(|| { let code = black_box( - "6110006001556001546000555b610fff805560016000540380600055600c57".from_hex().unwrap() + hex!("6110006001556001546000555b610fff805560016000540380600055600c57").to_vec() ); let mut params = ActionParams::default(); @@ -143,7 +144,7 @@ fn mem_gas_calculation_same(gas: U256, b: &mut Bencher) { fn mem_gas_calculation_increasing_usize(b: &mut Criterion) { b.bench_function("mem_gas_calculation_increasing_usize", |b| { - mem_gas_calculation_increasing(U256::from(::std::usize::MAX), b); + mem_gas_calculation_increasing(U256::from(std::usize::MAX), b); }); } @@ -161,7 +162,7 @@ fn mem_gas_calculation_increasing(gas: U256, b: &mut Bencher) { b.iter(|| { let code = black_box( - "6110006001556001546000555b610fff60005401805560016000540380600055600c57".from_hex().unwrap() + hex!("6110006001556001546000555b610fff60005401805560016000540380600055600c57").to_vec() ); let mut params = ActionParams::default(); @@ -184,7 +185,7 @@ fn blockhash_mulmod_small(b: &mut Criterion) { b.iter(|| { let code = black_box( - "6080604052348015600f57600080fd5b5060005a90505b60c881111560de5760017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095060017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095060017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095060017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095060017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8009505a90506016565b506035806100ed6000396000f3fe6080604052600080fdfea165627a7a72305820bde4a0ac6d0fac28fc879244baf8a6a0eda514bc95fb7ecbcaaebf2556e2687c0029".from_hex().unwrap() + hex!("6080604052348015600f57600080fd5b5060005a90505b60c881111560de5760017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095060017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095060017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095060017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095060017effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8009505a90506016565b506035806100ed6000396000f3fe6080604052600080fdfea165627a7a72305820bde4a0ac6d0fac28fc879244baf8a6a0eda514bc95fb7ecbcaaebf2556e2687c0029").to_vec() ); let mut params = ActionParams::default(); @@ -208,7 +209,7 @@ fn blockhash_mulmod_large(b: &mut Criterion) { b.iter(|| { let code = black_box( - "608060405234801561001057600080fd5b5060005a90505b60c8811115610177577efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009507efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009507efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009507efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009507efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009505a9050610017565b506035806101866000396000f3fe6080604052600080fdfea165627a7a72305820dcaec306f67bb96f3044fff25c9af2ec66f01d0954d0656964f046f42f2780670029".from_hex().unwrap() + hex!("608060405234801561001057600080fd5b5060005a90505b60c8811115610177577efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009507efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009507efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009507efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009507efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff17efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff08009505a9050610017565b506035806101866000396000f3fe6080604052600080fdfea165627a7a72305820dcaec306f67bb96f3044fff25c9af2ec66f01d0954d0656964f046f42f2780670029").to_vec() ); let mut params = ActionParams::default(); @@ -248,56 +249,56 @@ fn run_code(b: &mut Bencher, code: Bytes) { /// Compute mulmod(U256::MAX, U256::MAX, 1) 500 times. fn mulmod1_500(b: &mut Criterion) { b.bench_function("mulmod modulo 1, 500 times", |b| { - run_code(b, "6101f45b6001900360017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + run_code(b, hex!("6101f45b6001900360017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357").to_vec()); }); } /// Compute mulmod(U256::MAX, U256::MAX, 1) 1000 times. fn mulmod1_1000(b: &mut Criterion) { b.bench_function("mulmod modulo 1, 1000 times", |b| { - run_code(b, "6103e85b6001900360017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + run_code(b, hex!("6103e85b6001900360017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357").to_vec()); }); } /// Compute mulmod(U256::MAX, U256::MAX, 5) 500 times. fn mulmod5_500(b: &mut Criterion) { b.bench_function("mulmod modulo 5, 500 times", |b| { - run_code(b, "6101f45b6001900360057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + run_code(b, hex!("6101f45b6001900360057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357").to_vec()); }); } /// Compute mulmod(U256::MAX, U256::MAX, 5) 1000 times. fn mulmod5_1000(b: &mut Criterion) { b.bench_function("mulmod modulo 5, 1000 times", |b| { - run_code(b, "6103e85b6001900360057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + run_code(b, hex!("6103e85b6001900360057fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357").to_vec()); }); } /// Compute mulmod(U256::MAX, U256::MAX, 11) 500 times. fn mulmod11_500(b: &mut Criterion) { b.bench_function("mulmod modulo 11, 500 times", |b| { - run_code(b, "6101f45b60019003600b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + run_code(b, hex!("6101f45b60019003600b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357").to_vec()); }); } /// Compute mulmod(U256::MAX, U256::MAX, 11) 1000 times. fn mulmod11_1000(b: &mut Criterion) { b.bench_function("mulmod modulo 11, 1000 times", |b| { - run_code(b, "6103e85b60019003600b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + run_code(b, hex!("6103e85b60019003600b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357").to_vec()); }); } /// Compute mulmod(U256::MAX, U256::MAX, 0x58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd591) 500 times. fn mulmod_big_500(b: &mut Criterion) { b.bench_function("mulmod modulo random 256-bit number, 500 times", |b| { - run_code(b, "6101f45b600190037f58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd5917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + run_code(b, hex!("6101f45b600190037f58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd5917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357").to_vec()); }); } /// Compute mulmod(U256::MAX, U256::MAX, 0x58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd591) 1000 times. fn mulmod_big_1000(b: &mut Criterion) { b.bench_function("mulmod modulo random 256-bit number, 1000 times", |b| { - run_code(b, "6103e85b600190037f58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd5917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357".from_hex().unwrap()); + run_code(b, hex!("6103e85b600190037f58bca9711298bc76cd73f173352c8bc1d1640f977c1ec9a849dfde6fdbfbd5917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80095080600357").to_vec()); }); } @@ -321,97 +322,97 @@ fn mulmod_big_1000(b: &mut Criterion) { /// ``` fn mulmod500(b: &mut Criterion) { b.bench_function("mulmod randomly generated ints, 500 times", |b| { - run_code(b, "6101f45b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca095080600357".from_hex().unwrap()); + run_code(b, hex!("6101f45b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca095080600357").to_vec()); }); } /// Compute mulmod(a, b, c) for random 256-bit a, b and c. Iterate 1000 times. fn mulmod1000(b: &mut Criterion) { b.bench_function("mulmod randomly generated ints, 1000 times", |b| { - run_code(b, "6103e85b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca095080600357".from_hex().unwrap()); + run_code(b, hex!("6103e85b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca095080600357").to_vec()); }); } /// Compute addmod(a, b, c) for random 256-bit a, b and c. Iterate 500 times. fn addmod500(b: &mut Criterion) { b.bench_function("addmod randomly generated ints, 500 times", |b| { - run_code(b, "6101f45b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca085080600357".from_hex().unwrap()); + run_code(b, hex!("6101f45b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca085080600357").to_vec()); }); } /// Compute addmod(a, b, c) for random 256-bit a, b and c. Iterate 1000 times. fn addmod1000(b: &mut Criterion) { b.bench_function("addmod randomly generated ints, 1000 times", |b| { - run_code(b, "6103e85b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca085080600357".from_hex().unwrap()); + run_code(b, hex!("6103e85b600190037f5ed6db9489224124a1a4110ec8bec8b01369c8b549a4b8c4388a1796dc35a9377fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca085080600357").to_vec()); }); } /// Compute mul(a, b) for random 256-bit a and b. Iterate 500 times. fn mul500(b: &mut Criterion) { b.bench_function("mul randomly generated ints, 500 times", |b| { - run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca025080600357".from_hex().unwrap()); + run_code(b, hex!("6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca025080600357").to_vec()); }); } /// Compute mul(a, b) for random 256-bit a and b. Iterate 1000 times. fn mul1000(b: &mut Criterion) { b.bench_function("mul randomly generated ints, 1000 times", |b| { - run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca025080600357".from_hex().unwrap()); + run_code(b, hex!("6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca025080600357").to_vec()); }); } /// Compute div(a, b) for random 256-bit a and b. Iterate 500 times. fn div500(b: &mut Criterion) { b.bench_function("div randomly generated ints, 500 times", |b| { - run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca045080600357".from_hex().unwrap()); + run_code(b, hex!("6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca045080600357").to_vec()); }); } /// Compute div(a, b) for random 256-bit a and b. Iterate 1000 times. fn div1000(b: &mut Criterion) { b.bench_function("div randomly generated ints, 1000 times", |b| { - run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca045080600357".from_hex().unwrap()); + run_code(b, hex!("6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca045080600357").to_vec()); }); } /// Compute sdiv(a, b) for random 256-bit a and b. Iterate 500 times. fn sdiv500(b: &mut Criterion) { b.bench_function("sdiv randomly generated ints, 500 times", |b| { - run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca055080600357".from_hex().unwrap()); + run_code(b, hex!("6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca055080600357").to_vec()); }); } /// Compute sdiv(a, b) for random 256-bit a and b. Iterate 1000 times. fn sdiv1000(b: &mut Criterion) { b.bench_function("sdiv randomly generated ints, 1000 times", |b| { - run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca055080600357".from_hex().unwrap()); + run_code(b, hex!("6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca055080600357").to_vec()); }); } /// Compute mod(a, b) for random 256-bit a and b. Iterate 500 times. fn mod500(b: &mut Criterion) { b.bench_function("mod randomly generated ints, 500 times", |b| { - run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca065080600357".from_hex().unwrap()); + run_code(b, hex!("6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca065080600357").to_vec()); }); } /// Compute mod(a, b) for random 256-bit a and b. Iterate 1000 times. fn mod1000(b: &mut Criterion) { b.bench_function("mod randomly generated ints, 1000 times", |b| { - run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca065080600357".from_hex().unwrap()); + run_code(b, hex!("6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca065080600357").to_vec()); }); } /// Compute smod(a, b) for random 256-bit a and b. Iterate 500 times. fn smod500(b: &mut Criterion) { b.bench_function("smod randomly generated ints, 500 times", |b| { - run_code(b, "6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca075080600357".from_hex().unwrap()); + run_code(b, hex!("6101f45b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca075080600357").to_vec()); }); } /// Compute smod(a, b) for random 256-bit a and b. Iterate 1000 times. fn smod1000(b: &mut Criterion) { b.bench_function("smod randomly generated ints, 1000 times", |b| { - run_code(b, "6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca075080600357".from_hex().unwrap()); + run_code(b, hex!("6103e85b600190037fb8e0a2b6b1587398c28bf9e9d34ea24ba34df308eec2acedca363b2fce2c25db7fcc2de1f8ec6cc9a24ed2c48b856637f9e45f0a5feee21a196aa42a290ef454ca075080600357").to_vec()); }); } diff --git a/ethcore/evm/src/interpreter/mod.rs b/ethcore/evm/src/interpreter/mod.rs index 799566eebd9..42b555ebb1a 100644 --- a/ethcore/evm/src/interpreter/mod.rs +++ b/ethcore/evm/src/interpreter/mod.rs @@ -31,7 +31,6 @@ use hash::keccak; use bytes::Bytes; use ethereum_types::{U256, U512, H256, Address, BigEndianHash}; - use vm::{ self, ActionParams, ParamsType, ActionValue, ActionType, MessageCallResult, ContractCreateResult, CreateContractAddress, ReturnData, GasLeft, Schedule, @@ -556,11 +555,11 @@ impl Interpreter { let contract_code = self.mem.read_slice(init_off, init_size); let create_result = ext.create( - &create_gas.as_u256(), - &endowment, - contract_code, - &self.params.code_version, - address_scheme, + &create_gas.as_u256(), + &endowment, + contract_code, + &self.params.code_version, + address_scheme, true, ); return match create_result { @@ -1221,7 +1220,6 @@ fn address_to_u256(value: Address) -> U256 { #[cfg(test)] mod tests { use std::sync::Arc; - use rustc_hex::FromHex; use factory::Factory; use vm::{self, Exec, ActionParams, ActionValue}; use vm::tests::{FakeExt, test_finalize}; @@ -1233,7 +1231,7 @@ mod tests { #[test] fn should_not_fail_on_tracing_mem() { - let code = "7feeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa6020526000620f120660406000601773945304eb96065b2a98b57a48a06ae28d285a71b56101f4f1600055".from_hex().unwrap(); + let code = hex!("7feeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff006000527faaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffaa6020526000620f120660406000601773945304eb96065b2a98b57a48a06ae28d285a71b56101f4f1600055").to_vec(); let mut params = ActionParams::default(); params.address = Address::from_low_u64_be(5); @@ -1256,7 +1254,7 @@ mod tests { #[test] fn should_not_overflow_returndata() { - let code = "6001600160000360003e00".from_hex().unwrap(); + let code = hex!("6001600160000360003e00").to_vec(); let mut params = ActionParams::default(); params.address = Address::from_low_u64_be(5); diff --git a/ethcore/evm/src/interpreter/shared_cache.rs b/ethcore/evm/src/interpreter/shared_cache.rs index 7e6f2c3b572..6d6f0874bd8 100644 --- a/ethcore/evm/src/interpreter/shared_cache.rs +++ b/ethcore/evm/src/interpreter/shared_cache.rs @@ -101,9 +101,8 @@ impl Default for SharedCache { #[test] fn test_find_jump_destinations() { - use rustc_hex::FromHex; // given - let code = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055".from_hex().unwrap(); + let code = hex!("7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff5b01600055"); // when let valid_jump_destinations = SharedCache::find_jump_destinations(&code); diff --git a/ethcore/evm/src/lib.rs b/ethcore/evm/src/lib.rs index 2a1fd39494c..ea412f9920b 100644 --- a/ethcore/evm/src/lib.rs +++ b/ethcore/evm/src/lib.rs @@ -32,8 +32,7 @@ extern crate lazy_static; extern crate log; #[cfg(test)] -extern crate rustc_hex; -#[cfg(test)] +#[macro_use] extern crate hex_literal; pub mod evm; diff --git a/ethcore/executive-state/Cargo.toml b/ethcore/executive-state/Cargo.toml index c8f5912b70d..9c8006402f5 100644 --- a/ethcore/executive-state/Cargo.toml +++ b/ethcore/executive-state/Cargo.toml @@ -6,6 +6,10 @@ authors = ["Parity Technologies "] edition = "2018" license = "GPL-3.0" +[[bench]] +name = "execution" +harness = false + [dependencies] account-db = { path = "../account-db" } account-state = { path = "../account-state" } @@ -24,11 +28,17 @@ vm = { path = "../vm" } [dev-dependencies] env_logger = "0.5" ethcore = { path = "..", features = ["test-helpers"] } -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } evm = { path = "../evm" } keccak-hash = "0.4.0" pod = { path = "../pod" } -rustc-hex = "1.0" +hex-literal = "0.2.1" spec = { path = "../spec" } trie-db = "0.20.0" ethtrie = { package = "patricia-trie-ethereum", path = "../../util/patricia-trie-ethereum" } +# Benchmarks +criterion = "0.3.1" +ethcore-db = { path = "../db" } +journaldb = { path = "../../util/journaldb" } +state-db = { path = "../state-db" } +tempdir = "0.3.7" diff --git a/ethcore/executive-state/benches/8481475.rlp b/ethcore/executive-state/benches/8481475.rlp new file mode 100644 index 00000000000..ac41fd96637 Binary files /dev/null and b/ethcore/executive-state/benches/8481475.rlp differ diff --git a/ethcore/executive-state/benches/9532543.rlp b/ethcore/executive-state/benches/9532543.rlp new file mode 100644 index 00000000000..df0da6cca17 Binary files /dev/null and b/ethcore/executive-state/benches/9532543.rlp differ diff --git a/ethcore/executive-state/benches/execution.rs b/ethcore/executive-state/benches/execution.rs new file mode 100644 index 00000000000..56fe564eeae --- /dev/null +++ b/ethcore/executive-state/benches/execution.rs @@ -0,0 +1,150 @@ +// Copyright 2015-2020 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! Benchmark transaction execution of two blocks from mainnet, both of average +//! size (~35kb as RLP), one with 229 transactions (#8481475, Constantinople era) +//! and the other with 139 transactions (9532543, Istanbul era). Note that the +//! benchmark here is almost completely CPU-bound and does not involve IO at all, +//! so be careful not to draw too many conclusions from the results. + +use std::time::{Duration, Instant}; +use criterion::{Criterion, criterion_group, criterion_main}; + +use account_state::{CleanupMode, State}; +use common_types::{ + header::Header, + transaction::SignedTransaction, + verification::Unverified +}; +use ethcore::test_helpers::new_temp_db; +use ethcore_db as db; +use ethereum_types::U256; +use executive_state::ExecutiveState; +use spec::{new_constantinople_test_machine, new_istanbul_test_machine}; +use state_db::StateDB; +use tempdir::TempDir; + +fn build_state() -> State { + let db_path = TempDir::new("execution-bench").unwrap(); + let db = new_temp_db(&db_path.path()); + let journal_db = journaldb::new(db.key_value().clone(), journaldb::Algorithm::OverlayRecent, db::COL_STATE); + let state_db = StateDB::new(journal_db, 25 * 1024 * 1024); + State::new(state_db, U256::zero(), Default::default()) +} + +fn setup_state_for_block(state: &mut State, block: Unverified) -> Vec { + block.transactions + .into_iter() + .map(|tx| tx.verify_unordered().expect("tx is from known-good block")) + .inspect(|tx| { + // Ensure we have enough cash to execute the transaction + let gas_cost = tx.gas * tx.gas_price; + state.add_balance(&tx.sender(), &(tx.value + gas_cost), CleanupMode::ForceCreate).unwrap(); + // Fix up the nonce such that the state has the expected nonce + if state.nonce(&tx.sender()).unwrap() == U256::zero() { + for _ in 0..tx.nonce.as_usize() { + state.inc_nonce(&tx.sender()).unwrap(); + } + } + }) + .collect::>() + +} +fn build_env_info(header: &Header) -> vm::EnvInfo { + vm::EnvInfo { + number: header.number(), + author: *header.author(), + timestamp: header.timestamp(), + difficulty: *header.difficulty(), + gas_limit: *header.gas_limit() * 10, + last_hashes: std::sync::Arc::new(vec![]), + gas_used: *header.gas_used(), + } +} + +macro_rules! bench_tx_apply { + ($b: expr, $state: expr, $env_info: expr, $machine: expr, $signed_txs: expr, tracing => $tracing: expr ) => { + $b.iter_custom(|iters| { + let mut dur = Duration::new(0, 0); + for _ in 0..iters { + $state.checkpoint(); + let start = Instant::now(); + for tx in &$signed_txs { + let outcome = $state.apply(&$env_info, &$machine, tx, $tracing); + assert!(outcome.is_ok()) + } + dur += start.elapsed(); + $state.revert_to_checkpoint(); + } + dur + }) + } +} + +fn execute_8481475(c: &mut Criterion) { + // Block from the Constantinople era; 202 transactions, 32k RLP + let constantinople_block = Unverified::from_rlp(include_bytes!("./8481475.rlp").to_vec()).unwrap(); + let mut state = build_state(); + let env_info = build_env_info(&constantinople_block.header); + let signed_txs = setup_state_for_block(&mut state, constantinople_block); + + let machine = new_constantinople_test_machine(); + c.bench_function("Block 8481475, apply txs (Costantinople, tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => true); + }); + + c.bench_function("Block 8481475, apply txs (Costantinople, no tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => false); + }); + + let machine = new_istanbul_test_machine(); + c.bench_function("Block 8481475, apply txs (Istanbul, tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => true); + }); + + c.bench_function("Block 8481475, apply txs (Istanbul, no tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => false); + }); +} + +fn execute_9532543(c: &mut Criterion) { + // Block from the Istanbul era; 139 transactions, 38k RLP + let istanbul_block = Unverified::from_rlp(include_bytes!("./9532543.rlp").to_vec()).unwrap(); + let mut state = build_state(); + let env_info = build_env_info(&istanbul_block.header); + let signed_txs = setup_state_for_block(&mut state, istanbul_block); + + let machine = new_constantinople_test_machine(); + c.bench_function("Block 9532543, apply txs (Constantinople, tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => true); + }); + + c.bench_function("Block 9532543, apply txs (Constantinople, no tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => false); + }); + + let machine = new_istanbul_test_machine(); + c.bench_function("Block 9532543, apply txs (Istanbul, tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => true); + }); + + c.bench_function("Block 9532543, apply txs (Istanbul, no tracing)", |b| { + bench_tx_apply!(b, state, env_info, machine, signed_txs, tracing => false); + }); +} + +criterion_group!(benches, execute_8481475, execute_9532543); +criterion_main!(benches); diff --git a/ethcore/executive-state/src/lib.rs b/ethcore/executive-state/src/lib.rs index a2f979b1017..0751aac8ce9 100644 --- a/ethcore/executive-state/src/lib.rs +++ b/ethcore/executive-state/src/lib.rs @@ -154,13 +154,12 @@ pub trait ExecutiveState { /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. /// This will change the state accordingly. - fn apply_with_tracing( + fn apply_with_tracing( &mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, - tracer: T, - vm_tracer: V, + options: TransactOptions, ) -> ApplyResult where T: trace::Tracer, @@ -179,28 +178,26 @@ impl ExecutiveState for State { ) -> ApplyResult { if tracing { let options = TransactOptions::with_tracing(); - self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) + self.apply_with_tracing(env_info, machine, t, options) } else { let options = TransactOptions::with_no_tracing(); - self.apply_with_tracing(env_info, machine, t, options.tracer, options.vm_tracer) + self.apply_with_tracing(env_info, machine, t, options) } } /// Execute a given transaction with given tracer and VM tracer producing a receipt and an optional trace. /// This will change the state accordingly. - fn apply_with_tracing( + fn apply_with_tracing( &mut self, env_info: &EnvInfo, machine: &Machine, t: &SignedTransaction, - tracer: T, - vm_tracer: V, + options: TransactOptions ) -> ApplyResult where T: trace::Tracer, V: trace::VMTracer, { - let options = TransactOptions::new(tracer, vm_tracer); let e = execute(self, env_info, machine, t, options, false)?; let params = machine.params(); @@ -269,17 +266,13 @@ mod tests { use account_state::{Account, CleanupMode}; use common_types::transaction::*; + use ethereum_types::{H256, U256, Address, BigEndianHash}; + use ethcore::test_helpers::{get_temp_state, get_temp_state_db}; + use hex_literal::hex; use keccak_hash::{keccak, KECCAK_NULL_RLP}; use parity_crypto::publickey::Secret; - use ethereum_types::{H256, U256, Address, BigEndianHash}; - use ethcore::{ - test_helpers::{get_temp_state, get_temp_state_db} - }; - use ethtrie; use machine::Machine; - use pod::{self, PodAccount, PodState}; - use rustc_hex::FromHex; - use spec; + use pod::{PodAccount, PodState}; use ::trace::{FlatTrace, TraceError, trace}; use trie_db::{TrieFactory, TrieSpec}; use vm::EnvInfo; @@ -310,7 +303,7 @@ mod tests { gas: 100_000.into(), action: Action::Create, value: 100.into(), - data: FromHex::from_hex("601080600c6000396000f3006000355415600957005b60203560003555").unwrap(), + data: hex!("601080600c6000396000f3006000355415600957005b60203560003555").to_vec(), }.sign(&secret(), None); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); @@ -369,7 +362,7 @@ mod tests { gas: 100_000.into(), action: Action::Create, value: 100.into(), - data: FromHex::from_hex("5b600056").unwrap(), + data: hex!("5b600056").to_vec(), }.sign(&secret(), None); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); @@ -409,7 +402,7 @@ mod tests { data: vec![], }.sign(&secret(), None); - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("6000").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xa), hex!("6000").to_vec()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { @@ -533,7 +526,7 @@ mod tests { data: vec![], }.sign(&secret(), None); - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("600060006000600060006001610be0f1").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xa), hex!("600060006000600060006001610be0f1").to_vec()).unwrap(); let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { @@ -575,8 +568,8 @@ mod tests { data: vec![], }.sign(&secret(), None); - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b611000f2").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("6000").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xa), hex!("60006000600060006000600b611000f2").to_vec()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), hex!("6000").to_vec()).unwrap(); let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { @@ -634,8 +627,8 @@ mod tests { data: vec![], }.sign(&secret(), None); - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("6000600060006000600b618000f4").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("60056000526001601ff3").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xa), hex!("6000600060006000600b618000f4").to_vec()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), hex!("60056000526001601ff3").to_vec()).unwrap(); let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { @@ -692,7 +685,7 @@ mod tests { data: vec![], }.sign(&secret(), None); - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("5b600056").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xa), hex!("5b600056").to_vec()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { @@ -731,8 +724,8 @@ mod tests { data: vec![], }.sign(&secret(), None); - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("6000").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xa), hex!("60006000600060006000600b602b5a03f1").to_vec()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), hex!("6000").to_vec()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); let result = state.apply(&info, &machine, &t, true).unwrap(); @@ -790,7 +783,7 @@ mod tests { data: vec![], }.sign(&secret(), None); - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006045600b6000f1").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xa), hex!("60006000600060006045600b6000f1").to_vec()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { @@ -844,7 +837,7 @@ mod tests { data: vec![], }.sign(&secret(), None); - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("600060006000600060ff600b6000f1").unwrap()).unwrap(); // not enough funds. + state.init_code(&Address::from_low_u64_be(0xa), hex!("600060006000600060ff600b6000f1").to_vec()).unwrap(); // not enough funds. state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { @@ -886,8 +879,8 @@ mod tests { data: vec![],//600480600b6000396000f35b600056 }.sign(&secret(), None); - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("5b600056").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xa), hex!("60006000600060006000600b602b5a03f1").to_vec()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), hex!("5b600056").to_vec()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { @@ -941,9 +934,9 @@ mod tests { data: vec![], }.sign(&secret(), None); - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("60006000600060006000600c602b5a03f1").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xc), FromHex::from_hex("6000").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xa), hex!("60006000600060006000600b602b5a03f1").to_vec()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), hex!("60006000600060006000600c602b5a03f1").to_vec()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xc), hex!("6000").to_vec()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); let result = state.apply(&info, &machine, &t, true).unwrap(); let expected_trace = vec![FlatTrace { @@ -1015,9 +1008,9 @@ mod tests { data: vec![],//600480600b6000396000f35b600056 }.sign(&secret(), None); - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("60006000600060006000600b602b5a03f1").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xb), FromHex::from_hex("60006000600060006000600c602b5a03f1505b601256").unwrap()).unwrap(); - state.init_code(&Address::from_low_u64_be(0xc), FromHex::from_hex("6000").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xa), hex!("60006000600060006000600b602b5a03f1").to_vec()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xb), hex!("60006000600060006000600c602b5a03f1505b601256").to_vec()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xc), hex!("6000").to_vec()).unwrap(); state.add_balance(&t.sender(), &(100.into()), CleanupMode::NoEmpty).unwrap(); let result = state.apply(&info, &machine, &t, true).unwrap(); @@ -1087,7 +1080,7 @@ mod tests { data: vec![], }.sign(&secret(), None); - state.init_code(&Address::from_low_u64_be(0xa), FromHex::from_hex("73000000000000000000000000000000000000000bff").unwrap()).unwrap(); + state.init_code(&Address::from_low_u64_be(0xa), hex!("73000000000000000000000000000000000000000bff").to_vec()).unwrap(); state.add_balance(&Address::from_low_u64_be(0xa), &50.into(), CleanupMode::NoEmpty).unwrap(); state.add_balance(&t.sender(), &100.into(), CleanupMode::NoEmpty).unwrap(); let result = state.apply(&info, &machine, &t, true).unwrap(); @@ -1580,15 +1573,15 @@ mod tests { state.transfer_balance(&b, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance state.transfer_balance(&c, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance state.transfer_balance(&e, &x, &1.into(), CleanupMode::TrackTouched(&mut touched)).unwrap(); // touch an account decreasing its balance - state.kill_garbage(&touched, true, &None, false).unwrap(); + state.kill_garbage(&touched, &None, false).unwrap(); assert!(!state.exists(&a).unwrap()); assert!(state.exists(&b).unwrap()); - state.kill_garbage(&touched, true, &Some(100.into()), false).unwrap(); + state.kill_garbage(&touched,&Some(100.into()), false).unwrap(); assert!(!state.exists(&b).unwrap()); assert!(state.exists(&c).unwrap()); assert!(state.exists(&d).unwrap()); assert!(state.exists(&e).unwrap()); - state.kill_garbage(&touched, true, &Some(100.into()), true).unwrap(); + state.kill_garbage(&touched, &Some(100.into()), true).unwrap(); assert!(state.exists(&c).unwrap()); assert!(state.exists(&d).unwrap()); assert!(!state.exists(&e).unwrap()); diff --git a/ethcore/light/Cargo.toml b/ethcore/light/Cargo.toml index e4bf437f11b..41efd17c4c7 100644 --- a/ethcore/light/Cargo.toml +++ b/ethcore/light/Cargo.toml @@ -30,7 +30,7 @@ vm = { path = "../vm" } fastmap = { path = "../../util/fastmap" } failsafe = { version = "0.3.0", default-features = false, features = ["parking_lot_mutex"] } rlp = "0.4.0" -rlp_derive = { path = "../../util/rlp-derive" } +rlp-derive = "0.1" smallvec = "1.2.0" futures = "0.1" rand = "0.7" diff --git a/ethcore/machine/Cargo.toml b/ethcore/machine/Cargo.toml index 71244eb5fd3..ba2d62d40e8 100644 --- a/ethcore/machine/Cargo.toml +++ b/ethcore/machine/Cargo.toml @@ -43,9 +43,8 @@ criterion = "0.3" ethcore = { path = "../", features = ["test-helpers"] } ethcore-io = { path = "../../util/io" } ethjson = { path = "../../json" } -parity-crypto = { version = "0.4.2", features = ["publickey"] } -macros = { path = "../../util/macros" } -rustc-hex = "1.0" +parity-crypto = { version = "0.5.0", features = ["publickey"] } +hex-literal = "0.2.1" spec = { path = "../spec" } tempdir = "0.3" trace = { path = "../trace" } diff --git a/ethcore/machine/src/executive.rs b/ethcore/machine/src/executive.rs index 9307b0de198..13181722244 100644 --- a/ethcore/machine/src/executive.rs +++ b/ethcore/machine/src/executive.rs @@ -851,7 +851,7 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { return Err(ExecutionError::NotEnoughBaseGas { required: base_gas_required, got: t.gas }); } - if !t.is_unsigned() && check_nonce && schedule.kill_dust != CleanDustMode::Off && !self.state.exists(&sender)? { + if check_nonce && schedule.kill_dust != CleanDustMode::Off && !self.state.exists(&sender)? { return Err(ExecutionError::SenderMustExist); } @@ -884,10 +884,8 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { let mut substate = Substate::new(); - // NOTE: there can be no invalid transactions from this point. - if !schedule.keep_unsigned_nonce || !t.is_unsigned() { - self.state.inc_nonce(&sender)?; - } + self.state.inc_nonce(&sender)?; + self.state.sub_balance( &sender, &U256::try_from(gas_cost).expect("Total cost (value + gas_cost) is lower than max allowed balance (U256); gas_cost has to fit U256; qed"), @@ -1177,9 +1175,17 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { } // perform garbage-collection - let min_balance = if schedule.kill_dust != CleanDustMode::Off { Some(U256::from(schedule.tx_gas).overflowing_mul(t.gas_price).0) } else { None }; - self.state.kill_garbage(&substate.touched, schedule.kill_empty, &min_balance, schedule.kill_dust == CleanDustMode::WithCodeAndStorage)?; - + if schedule.kill_empty { + let (min_balance, kill_contracts) = if schedule.kill_dust != CleanDustMode::Off { + ( + Some(U256::from(schedule.tx_gas).overflowing_mul(t.gas_price).0), + schedule.kill_dust == CleanDustMode::WithCodeAndStorage, + ) + } else { + (None, false) + }; + self.state.kill_garbage(&substate.touched, &min_balance, kill_contracts)?; + } match result { Err(vm::Error::Internal(msg)) => Err(ExecutionError::Internal(msg)), Err(exception) => { @@ -1191,9 +1197,9 @@ impl<'a, B: 'a + StateBackend> Executive<'a, B> { cumulative_gas_used: self.info.gas_used + t.gas, logs: vec![], contracts_created: vec![], - output: output, - trace: trace, - vm_trace: vm_trace, + output, + trace, + vm_trace, state_diff: None, }) }, @@ -1225,17 +1231,15 @@ mod tests { collections::HashSet, }; - use rustc_hex::FromHex; - use ethereum_types::{H256, U256, U512, Address, BigEndianHash}; - use account_state::CleanupMode; use common_types::{ errors::ExecutionError, transaction::{Action, Transaction}, }; - use parity_crypto::publickey::{Generator, Random}; + use ethereum_types::{H256, U256, U512, Address, BigEndianHash}; use evm::{Factory, evm_test, evm_test_ignore}; - use macros::vec_into; + use hex_literal::hex; + use parity_crypto::publickey::{Generator, Random}; use vm::{ActionParams, ActionValue, EnvInfo, CreateContractAddress}; use ::trace::{ trace, @@ -1312,7 +1316,7 @@ mod tests { params.address = address.clone(); params.sender = sender.clone(); params.gas = U256::from(100_000); - params.code = Some(Arc::new("3331600055".from_hex().unwrap())); + params.code = Some(Arc::new(hex!("3331600055").to_vec())); params.value = ActionValue::Transfer(U256::from(0x7)); let mut state = get_temp_state_with_factory(factory); state.add_balance(&sender, &U256::from(0x100u64), CleanupMode::NoEmpty).unwrap(); @@ -1359,7 +1363,8 @@ mod tests { // 60 00 - push 0 // f3 - return - let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); + let code = + hex!("7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055").to_vec(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; @@ -1402,7 +1407,7 @@ mod tests { // 61 ffff - push fff gas // f1 - CALL - let code = "60006000600060006001600361fffff1".from_hex().unwrap(); + let code = hex!("60006000600060006001600361fffff1").to_vec(); let sender = Address::from_str("4444444444444444444444444444444444444444").unwrap(); let address = Address::from_str("5555555555555555555555555555555555555555").unwrap(); @@ -1484,7 +1489,7 @@ mod tests { // 60 00 - push 0 // f3 - return - let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055".from_hex().unwrap(); + let code = hex!("7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0600055").to_vec(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; @@ -1553,28 +1558,28 @@ mod tests { parent_step: 0, code: vec![124, 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85, 96, 0, 82, 96, 29, 96, 3, 96, 23, 240, 96, 0, 85], operations: vec![ - VMOperation { pc: 0, instruction: 124, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec_into![U256::from_dec_str("2589892687202724018173567190521546555304938078595079151649957320078677").unwrap()], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 30, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 32, instruction: 82, gas_cost: 6.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![0, 0, 0, 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85] }), store_diff: None }) }, - VMOperation { pc: 33, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99985.into(), stack_push: vec_into![29], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 35, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99982.into(), stack_push: vec_into![3], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 37, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![23], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 39, instruction: 240, gas_cost: 99979.into(), executed: Some(VMExecutedOperation { gas_used: 64755.into(), stack_push: vec_into![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 40, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 64752.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 42, instruction: 85, gas_cost: 20000.into(), executed: Some(VMExecutedOperation { gas_used: 44752.into(), stack_push: vec_into![], mem_diff: None, store_diff: Some(StorageDiff { location: 0.into(), value: U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap() }) }) } + VMOperation { pc: 0, instruction: 124, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec![U256::from_dec_str("2589892687202724018173567190521546555304938078595079151649957320078677").unwrap()], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 30, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec![U256::from(0)], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 32, instruction: 82, gas_cost: 6.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![0, 0, 0, 96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85] }), store_diff: None }) }, + VMOperation { pc: 33, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99985.into(), stack_push: vec![U256::from(29)], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 35, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99982.into(), stack_push: vec![U256::from(3)], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 37, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec![U256::from(23)], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 39, instruction: 240, gas_cost: 99979.into(), executed: Some(VMExecutedOperation { gas_used: 64755.into(), stack_push: vec![U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap()], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 40, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 64752.into(), stack_push: vec![U256::from(0)], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 42, instruction: 85, gas_cost: 20000.into(), executed: Some(VMExecutedOperation { gas_used: 44752.into(), stack_push: vec![], mem_diff: None, store_diff: Some(StorageDiff { location: 0.into(), value: U256::from_dec_str("1135198453258042933984631383966629874710669425204").unwrap() }) }) } ], subs: vec![ VMTrace { parent_step: 6, code: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], operations: vec![ - VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67976.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67973.into(), stack_push: vec_into![16, 16], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67970.into(), stack_push: vec_into![12], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67967.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 67958.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) }, - VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec_into![], mem_diff: None, store_diff: None }) } + VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67976.into(), stack_push: vec![U256::from(16)], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67973.into(), stack_push: vec![U256::from(16); 2], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67970.into(), stack_push: vec![U256::from(12)], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67967.into(), stack_push: vec![U256::from(0)], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 67958.into(), stack_push: vec![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) }, + VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec![U256::from(0)], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 67955.into(), stack_push: vec![], mem_diff: None, store_diff: None }) } ], subs: vec![] } @@ -1603,7 +1608,7 @@ mod tests { // 60 00 // fd - revert - let code = "6460016000fd6000526005601b6017f0600055".from_hex().unwrap(); + let code = hex!("6460016000fd6000526005601b6017f0600055").to_vec(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; @@ -1676,7 +1681,7 @@ mod tests { // 60 00 - push 0 // f3 - return - let code = "601080600c6000396000f3006000355415600957005b60203560003555".from_hex().unwrap(); + let code = hex!("601080600c6000396000f3006000355415600957005b60203560003555").to_vec(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; @@ -1728,13 +1733,13 @@ mod tests { parent_step: 0, code: vec![96, 16, 128, 96, 12, 96, 0, 57, 96, 0, 243, 0, 96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53, 85], operations: vec![ - VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec_into![16], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec_into![16, 16], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99991.into(), stack_push: vec_into![12], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec_into![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) }, - VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![0], mem_diff: None, store_diff: None }) }, - VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec_into![], mem_diff: None, store_diff: None }) } + VMOperation { pc: 0, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99997.into(), stack_push: vec![U256::from(16)], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 2, instruction: 128, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99994.into(), stack_push: vec![U256::from(16); 2], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 3, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99991.into(), stack_push: vec![U256::from(12)], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 5, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99988.into(), stack_push: vec![U256::from(0)], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 7, instruction: 57, gas_cost: 9.into(), executed: Some(VMExecutedOperation { gas_used: 99979.into(), stack_push: vec![], mem_diff: Some(MemoryDiff { offset: 0, data: vec![96, 0, 53, 84, 21, 96, 9, 87, 0, 91, 96, 32, 53, 96, 0, 53] }), store_diff: None }) }, + VMOperation { pc: 8, instruction: 96, gas_cost: 3.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec![U256::from(0)], mem_diff: None, store_diff: None }) }, + VMOperation { pc: 10, instruction: 243, gas_cost: 0.into(), executed: Some(VMExecutedOperation { gas_used: 99976.into(), stack_push: vec![], mem_diff: None, store_diff: None }) } ], subs: vec![] }; @@ -1765,7 +1770,7 @@ mod tests { // 60 00 - push 0 // f3 - return - let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d600360e6f0600055".from_hex().unwrap(); + let code = hex!("7c601080600c6000396000f3006000355415600957005b60203560003555600052601d600360e6f0600055").to_vec(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; @@ -1818,7 +1823,7 @@ mod tests { // 60 00 - push 0 // f3 - return - let code = "7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0".from_hex().unwrap(); + let code = hex!("7c601080600c6000396000f3006000355415600957005b60203560003555600052601d60036017f0").to_vec(); let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; @@ -1861,7 +1866,7 @@ mod tests { // 58 - get PC // 55 - sstore - let code_a = "6000600060006000601873945304eb96065b2a98b57a48a06ae28d285a71b56103e8f15855".from_hex().unwrap(); + let code_a = hex!("6000600060006000601873945304eb96065b2a98b57a48a06ae28d285a71b56103e8f15855").to_vec(); // 60 00 - push 0 // 60 00 - push 0 @@ -1875,7 +1880,7 @@ mod tests { // 01 - add // 58 - get PC // 55 - sstore - let code_b = "60006000600060006017730f572e5295c57f15886f9b263e2f6d2d6c7b5ec66101f4f16001015855".from_hex().unwrap(); + let code_b = hex!("60006000600060006017730f572e5295c57f15886f9b263e2f6d2d6c7b5ec66101f4f16001015855").to_vec(); let address_a = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let address_b = Address::from_str("945304eb96065b2a98b57a48a06ae28d285a71b5" ).unwrap(); @@ -1936,7 +1941,7 @@ mod tests { // 60 01 - push 1 // 55 - sstore let sender = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); - let code = "600160005401600055600060006000600060003060e05a03f1600155".from_hex().unwrap(); + let code = hex!("600160005401600055600060006000600060003060e05a03f1600155").to_vec(); let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; let mut params = ActionParams::default(); params.address = address.clone(); @@ -1963,11 +1968,11 @@ mod tests { // TODO: fix (preferred) or remove evm_test_ignore!{test_transact_simple: test_transact_simple_int} fn test_transact_simple(factory: Factory) { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let t = Transaction { action: Action::Create, value: U256::from(17), - data: "3331600055".from_hex().unwrap(), + data: hex!("3331600055").to_vec(), gas: U256::from(100_000), gas_price: U256::zero(), nonce: U256::zero() @@ -2002,11 +2007,11 @@ mod tests { evm_test!{test_transact_invalid_nonce: test_transact_invalid_nonce_int} fn test_transact_invalid_nonce(factory: Factory) { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let t = Transaction { action: Action::Create, value: U256::from(17), - data: "3331600055".from_hex().unwrap(), + data: hex!("3331600055").to_vec(), gas: U256::from(100_000), gas_price: U256::zero(), nonce: U256::one() @@ -2035,11 +2040,11 @@ mod tests { evm_test!{test_transact_gas_limit_reached: test_transact_gas_limit_reached_int} fn test_transact_gas_limit_reached(factory: Factory) { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let t = Transaction { action: Action::Create, value: U256::from(17), - data: "3331600055".from_hex().unwrap(), + data: hex!("3331600055").to_vec(), gas: U256::from(80_001), gas_price: U256::zero(), nonce: U256::zero() @@ -2070,11 +2075,11 @@ mod tests { evm_test!{test_not_enough_cash: test_not_enough_cash_int} fn test_not_enough_cash(factory: Factory) { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let t = Transaction { action: Action::Create, value: U256::from(18), - data: "3331600055".from_hex().unwrap(), + data: hex!("3331600055").to_vec(), gas: U256::from(100_000), gas_price: U256::one(), nonce: U256::zero() @@ -2103,7 +2108,7 @@ mod tests { evm_test!{test_keccak: test_keccak_int} fn test_keccak(factory: Factory) { - let code = "6064640fffffffff20600055".from_hex().unwrap(); + let code = hex!("6064640fffffffff20600055").to_vec(); let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); let address = contract_address(CreateContractAddress::FromSenderAndNonce, &sender, &U256::zero(), &[]).0; @@ -2139,8 +2144,8 @@ mod tests { let contract_address = Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap(); let sender = Address::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap(); // EIP-140 test case - let code = "6c726576657274656420646174616000557f726576657274206d657373616765000000000000000000000000000000000000600052600e6000fd".from_hex().unwrap(); - let returns = "726576657274206d657373616765".from_hex().unwrap(); + let code = hex!("6c726576657274656420646174616000557f726576657274206d657373616765000000000000000000000000000000000000600052600e6000fd").to_vec(); + let returns = hex!("726576657274206d657373616765").to_vec(); let mut state = get_temp_state_with_factory(factory.clone()); state.add_balance(&sender, &U256::from_str("152d02c7e14af68000000").unwrap(), CleanupMode::NoEmpty).unwrap(); state.commit().unwrap(); @@ -2180,13 +2185,13 @@ mod tests { let mut state = get_temp_state_with_factory(factory.clone()); state.new_contract(&x1, U256::zero(), U256::from(1), U256::zero()).unwrap(); - state.init_code(&x1, "600160005560006000556001600055".from_hex().unwrap()).unwrap(); + state.init_code(&x1, hex!("600160005560006000556001600055").to_vec()).unwrap(); state.new_contract(&x2, U256::zero(), U256::from(1), U256::zero()).unwrap(); - state.init_code(&x2, "600060005560016000556000600055".from_hex().unwrap()).unwrap(); + state.init_code(&x2, hex!("600060005560016000556000600055").to_vec()).unwrap(); state.new_contract(&y1, U256::zero(), U256::from(1), U256::zero()).unwrap(); - state.init_code(&y1, "600060006000600061100062fffffff4".from_hex().unwrap()).unwrap(); + state.init_code(&y1, hex!("600060006000600061100062fffffff4").to_vec()).unwrap(); state.new_contract(&y2, U256::zero(), U256::from(1), U256::zero()).unwrap(); - state.init_code(&y2, "600060006000600061100162fffffff4".from_hex().unwrap()).unwrap(); + state.init_code(&y2, hex!("600060006000600061100162fffffff4").to_vec()).unwrap(); let info = EnvInfo::default(); let machine = new_constantinople_test_machine(); @@ -2197,7 +2202,7 @@ mod tests { let (FinalizationResult { gas_left, .. }, refund, gas) = { let gas = U256::from(0xffffffffffu64); let mut params = ActionParams::default(); - params.code = Some(Arc::new("6001600055600060006000600061200163fffffffff4".from_hex().unwrap())); + params.code = Some(Arc::new(hex!("6001600055600060006000600061200163fffffffff4").to_vec())); params.gas = gas; let mut substate = Substate::new(); let mut ex = Executive::new(&mut state, &info, &machine, &schedule); @@ -2215,7 +2220,7 @@ mod tests { let (FinalizationResult { gas_left, .. }, refund, gas) = { let gas = U256::from(0xffffffffffu64); let mut params = ActionParams::default(); - params.code = Some(Arc::new("6001600055600060006000600061200263fffffffff4".from_hex().unwrap())); + params.code = Some(Arc::new(hex!("6001600055600060006000600061200263fffffffff4").to_vec())); params.gas = gas; let mut substate = Substate::new(); let mut ex = Executive::new(&mut state, &info, &machine, &schedule); @@ -2231,9 +2236,7 @@ mod tests { fn wasm_sample_code() -> Arc> { Arc::new( - "0061736d01000000010d0360027f7f0060017f0060000002270303656e7603726574000003656e760673656e646572000103656e76066d656d6f727902010110030201020404017000000501000708010463616c6c00020901000ac10101be0102057f017e4100410028020441c0006b22043602042004412c6a41106a220041003602002004412c6a41086a22014200370200200441186a41106a22024100360200200441186a41086a220342003703002004420037022c2004410036021c20044100360218200441186a1001200020022802002202360200200120032903002205370200200441106a2002360200200441086a200537030020042004290318220537022c200420053703002004411410004100200441c0006a3602040b0b0a010041040b0410c00000" - .from_hex() - .unwrap() + hex!("0061736d01000000010d0360027f7f0060017f0060000002270303656e7603726574000003656e760673656e646572000103656e76066d656d6f727902010110030201020404017000000501000708010463616c6c00020901000ac10101be0102057f017e4100410028020441c0006b22043602042004412c6a41106a220041003602002004412c6a41086a22014200370200200441186a41106a22024100360200200441186a41086a220342003703002004420037022c2004410036021c20044100360218200441186a1001200020022802002202360200200120032903002205370200200441106a2002360200200441086a200537030020042004290318220537022c200420053703002004411410004100200441c0006a3602040b0b0a010041040b0410c00000").to_vec() ) } diff --git a/ethcore/machine/src/externalities.rs b/ethcore/machine/src/externalities.rs index 0808ebed2dd..9a102ceed5a 100644 --- a/ethcore/machine/src/externalities.rs +++ b/ethcore/machine/src/externalities.rs @@ -24,7 +24,6 @@ use log::{debug, trace, warn}; use account_state::{Backend as StateBackend, State, CleanupMode}; use common_types::{ - transaction::UNSIGNED_SENDER, log_entry::LogEntry, }; use trace::{Tracer, VMTracer}; @@ -265,11 +264,9 @@ impl<'a, T: 'a, V: 'a, B: 'a> Ext for Externalities<'a, T, V, B> }; if !self.static_flag { - if !self.schedule.keep_unsigned_nonce || params.sender != UNSIGNED_SENDER { - if let Err(e) = self.state.inc_nonce(&self.origin_info.address) { - debug!(target: "ext", "Database corruption encountered: {:?}", e); - return Ok(ContractCreateResult::Failed) - } + if let Err(e) = self.state.inc_nonce(&self.origin_info.address) { + warn!(target: "ext", "Database corruption encountered: {:?}", e); + return Ok(ContractCreateResult::Failed) } } diff --git a/ethcore/machine/src/machine.rs b/ethcore/machine/src/machine.rs index 43151d0a023..61643e5aaad 100644 --- a/ethcore/machine/src/machine.rs +++ b/ethcore/machine/src/machine.rs @@ -407,8 +407,8 @@ fn round_block_gas_limit(gas_limit: U256, lower_limit: U256, upper_limit: U256) mod tests { use std::str::FromStr; use common_types::header::Header; + use hex_literal::hex; use super::*; - use spec; fn get_default_ethash_extensions() -> EthashExtensions { EthashExtensions { @@ -421,21 +421,12 @@ mod tests { #[test] fn should_disallow_unsigned_transactions() { - let rlp = "ea80843b9aca0083015f90948921ebb5f79e9e3920abe571004d0b1d5119c154865af3107a400080038080"; - let transaction: UnverifiedTransaction = ::rlp::decode(&::rustc_hex::FromHex::from_hex(rlp).unwrap()).unwrap(); - let spec = spec::new_ropsten_test(); - let ethparams = get_default_ethash_extensions(); - - let machine = Machine::with_ethash_extensions( - spec.params().clone(), - Default::default(), - ethparams, + let rlp = hex!("ea80843b9aca0083015f90948921ebb5f79e9e3920abe571004d0b1d5119c154865af3107a400080038080").to_vec(); + let transaction: UnverifiedTransaction = rlp::decode(&rlp).unwrap(); + assert_eq!( + transaction::Error::from(transaction.verify_unordered().unwrap_err()), + transaction::Error::InvalidSignature("invalid EC signature".into()), ); - let mut header = Header::new(); - header.set_number(15); - - let res = machine.verify_transaction_basic(&transaction, &header); - assert_eq!(res, Err(transaction::Error::InvalidSignature("invalid EC signature".into()))); } #[test] diff --git a/ethcore/pod/Cargo.toml b/ethcore/pod/Cargo.toml index 1b1fefa3d10..54c6e3b901c 100644 --- a/ethcore/pod/Cargo.toml +++ b/ethcore/pod/Cargo.toml @@ -19,10 +19,10 @@ kvdb = "0.4.0" log = "0.4" parity-bytes = "0.1.0" rlp = "0.4" -rustc-hex = "1.0" +rustc-hex = "2.1.0" serde = { version = "1.0", features = ["derive"] } trie-db = "0.20.0" triehash = { package = "triehash-ethereum", version = "0.2", path = "../../util/triehash-ethereum" } [dev-dependencies] -macros = { path = "../../util/macros" } +maplit = "1.0.2" diff --git a/ethcore/pod/src/account.rs b/ethcore/pod/src/account.rs index 811bb1eb8c7..e0b11be722b 100644 --- a/ethcore/pod/src/account.rs +++ b/ethcore/pod/src/account.rs @@ -27,7 +27,6 @@ use triehash::sec_trie_root; use parity_bytes::Bytes; use trie_db::TrieFactory; use ethtrie::Layout; -use ethjson; use common_types::account_diff::*; use rlp::{self, RlpStream}; use serde::{Serializer, Serialize}; @@ -54,7 +53,7 @@ pub struct PodAccount { fn opt_bytes_to_hex(opt_bytes: &Option, serializer: S) -> Result where S: Serializer { - let readable = opt_bytes.as_ref().map(|b| b.to_hex()).unwrap_or_default(); + let readable: String = opt_bytes.as_ref().map(|b| b.to_hex()).unwrap_or_default(); serializer.collect_str(&format_args!("0x{}", readable)) } @@ -101,7 +100,7 @@ impl From for PodAccount { } } -/// Determine difference between two optionally existant `Account`s. Returns None +/// Determine difference between two optionally existent `Account`s. Returns None /// if they are the same. pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option { match (pre, post) { @@ -146,55 +145,54 @@ pub fn diff_pod(pre: Option<&PodAccount>, post: Option<&PodAccount>) -> Option H256::from_low_u64_be(1), H256::from_low_u64_be(2) => H256::from_low_u64_be(2), H256::from_low_u64_be(3) => H256::from_low_u64_be(3), @@ -219,7 +217,7 @@ mod test { balance: 0.into(), nonce: 0.into(), code: Some(vec![]), - storage: map![ + storage: btreemap![ H256::from_low_u64_be(1) => H256::from_low_u64_be(1), H256::from_low_u64_be(2) => H256::from_low_u64_be(3), H256::from_low_u64_be(3) => H256::from_low_u64_be(0), @@ -234,7 +232,7 @@ mod test { balance: Diff::Same, nonce: Diff::Same, code: Diff::Same, - storage: map![ + storage: btreemap![ H256::from_low_u64_be(2) => Diff::new(H256::from_low_u64_be(2), H256::from_low_u64_be(3)), H256::from_low_u64_be(3) => Diff::new(H256::from_low_u64_be(3), H256::from_low_u64_be(0)), H256::from_low_u64_be(4) => Diff::new(H256::from_low_u64_be(4), H256::from_low_u64_be(0)), diff --git a/ethcore/pod/src/state.rs b/ethcore/pod/src/state.rs index d249813f972..9ce8f5aea7a 100644 --- a/ethcore/pod/src/state.rs +++ b/ethcore/pod/src/state.rs @@ -20,7 +20,6 @@ use std::collections::BTreeMap; use ethereum_types::{H256, Address}; use triehash::sec_trie_root; use common_types::state_diff::StateDiff; -use ethjson; use serde::Serialize; use crate::account::PodAccount; @@ -74,7 +73,6 @@ pub fn diff_pod(pre: &PodState, post: &PodState) -> StateDiff { #[cfg(test)] mod test { - use std::collections::BTreeMap; use common_types::{ account_diff::{AccountDiff, Diff}, state_diff::StateDiff, @@ -82,122 +80,122 @@ mod test { use ethereum_types::Address; use crate::account::PodAccount; use super::PodState; - use macros::map; + use maplit::btreemap; #[test] fn create_delete() { - let a = PodState::from(map![ + let a = PodState::from(btreemap![ Address::from_low_u64_be(1) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), - storage: map![], + storage: btreemap![], version: 0.into(), } ]); - assert_eq!(super::diff_pod(&a, &PodState::default()), StateDiff { raw: map![ + assert_eq!(super::diff_pod(&a, &PodState::default()), StateDiff { raw: btreemap![ Address::from_low_u64_be(1) => AccountDiff{ balance: Diff::Died(69.into()), nonce: Diff::Died(0.into()), code: Diff::Died(vec![]), - storage: map![], + storage: btreemap![], } ]}); - assert_eq!(super::diff_pod(&PodState::default(), &a), StateDiff { raw: map![ + assert_eq!(super::diff_pod(&PodState::default(), &a), StateDiff { raw: btreemap![ Address::from_low_u64_be(1) => AccountDiff{ balance: Diff::Born(69.into()), nonce: Diff::Born(0.into()), code: Diff::Born(vec![]), - storage: map![], + storage: btreemap![], } ]}); } #[test] fn create_delete_with_unchanged() { - let a = PodState::from(map![ + let a = PodState::from(btreemap![ Address::from_low_u64_be(1) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), - storage: map![], + storage: btreemap![], version: 0.into(), } ]); - let b = PodState::from(map![ + let b = PodState::from(btreemap![ Address::from_low_u64_be(1) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), - storage: map![], + storage: btreemap![], version: 0.into(), }, Address::from_low_u64_be(2) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), - storage: map![], + storage: btreemap![], version: 0.into(), } ]); - assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![ + assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: btreemap![ Address::from_low_u64_be(2) => AccountDiff { balance: Diff::Born(69.into()), nonce: Diff::Born(0.into()), code: Diff::Born(vec![]), - storage: map![], + storage: btreemap![], } ]}); - assert_eq!(super::diff_pod(&b, &a), StateDiff { raw: map![ + assert_eq!(super::diff_pod(&b, &a), StateDiff { raw: btreemap![ Address::from_low_u64_be(2) => AccountDiff { balance: Diff::Died(69.into()), nonce: Diff::Died(0.into()), code: Diff::Died(vec![]), - storage: map![], + storage: btreemap![], } ]}); } #[test] fn change_with_unchanged() { - let a = PodState::from(map![ + let a = PodState::from(btreemap![ Address::from_low_u64_be(1) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), - storage: map![], + storage: btreemap![], version: 0.into(), }, Address::from_low_u64_be(2) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), - storage: map![], + storage: btreemap![], version: 0.into(), } ]); - let b = PodState::from(map![ + let b = PodState::from(btreemap![ Address::from_low_u64_be(1) => PodAccount { balance: 69.into(), nonce: 1.into(), code: Some(Vec::new()), - storage: map![], + storage: btreemap![], version: 0.into(), }, Address::from_low_u64_be(2) => PodAccount { balance: 69.into(), nonce: 0.into(), code: Some(Vec::new()), - storage: map![], + storage: btreemap![], version: 0.into(), } ]); - assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: map![ + assert_eq!(super::diff_pod(&a, &b), StateDiff { raw: btreemap![ Address::from_low_u64_be(1) => AccountDiff { balance: Diff::Same, nonce: Diff::Changed(0.into(), 1.into()), code: Diff::Same, - storage: map![], + storage: btreemap![], } ]}); } diff --git a/ethcore/private-tx/Cargo.toml b/ethcore/private-tx/Cargo.toml index 2c42a522d7c..8a8104a97ef 100644 --- a/ethcore/private-tx/Cargo.toml +++ b/ethcore/private-tx/Cargo.toml @@ -31,14 +31,14 @@ log = "0.4" machine = { path = "../machine" } journaldb = { path = "../../util/journaldb" } parity-bytes = "0.1" -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } parking_lot = "0.10.0" trie-db = "0.20.0" patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" } registrar = { path = "../../util/registrar" } rlp = "0.4.0" -rlp_derive = { path = "../../util/rlp-derive" } -rustc-hex = "1.0" +rlp-derive = "0.1" +rustc-hex = "2.1.0" serde = "1.0" serde_derive = "1.0" serde_json = "1.0" diff --git a/ethcore/private-tx/tests/private_contract.rs b/ethcore/private-tx/tests/private_contract.rs index 6989d905250..2748a89ac2a 100644 --- a/ethcore/private-tx/tests/private_contract.rs +++ b/ethcore/private-tx/tests/private_contract.rs @@ -105,13 +105,13 @@ fn private_contract() { query_tx.nonce = 1.into(); let query_tx = query_tx.sign(&key1.secret(), chain_id); let result = pm.private_call(BlockId::Latest, &query_tx).unwrap(); - assert_eq!(&result.output[..], &("0000000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()[..])); + assert_eq!(&result.output[..], &("0000000000000000000000000000000000000000000000000000000000000000".from_hex::>().unwrap()[..])); assert_eq!(pm.get_validators(BlockId::Latest, &address).unwrap(), validators); trace!("Modifying private state"); let mut private_tx = Transaction::default(); private_tx.action = Action::Call(address.clone()); - private_tx.data = "bc64b76d2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap(); //setX(42) + private_tx.data = "bc64b76d2a00000000000000000000000000000000000000000000000000000000000000".from_hex::>().unwrap(); //setX(42) private_tx.gas = 120000.into(); private_tx.nonce = 1.into(); let private_tx = private_tx.sign(&key1.secret(), None); @@ -132,7 +132,7 @@ fn private_contract() { query_tx.nonce = 2.into(); let query_tx = query_tx.sign(&key1.secret(), chain_id); let result = pm.private_call(BlockId::Latest, &query_tx).unwrap(); - assert_eq!(&result.output[..], &("2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()[..])); + assert_eq!(&result.output[..], &("2a00000000000000000000000000000000000000000000000000000000000000".from_hex::>().unwrap()[..])); assert_eq!(pm.get_validators(BlockId::Latest, &address).unwrap(), validators); // Now try modification with just one signature @@ -159,7 +159,7 @@ fn private_contract() { query_tx.nonce = 3.into(); let query_tx = query_tx.sign(&key1.secret(), chain_id); let result = pm.private_call(BlockId::Latest, &query_tx).unwrap(); - assert_eq!(result.output, "2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()); + assert_eq!(result.output, "2a00000000000000000000000000000000000000000000000000000000000000".from_hex::>().unwrap()); } #[test] @@ -241,7 +241,7 @@ fn call_other_private_contract() { trace!("Creating private contract B"); // Build constructor data let mut deploy_data = "6060604052341561000f57600080fd5b6040516020806101c583398101604052808051906020019091905050806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505061014a8061007b6000396000f300606060405260043610610041576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680635197c7aa14610046575b600080fd5b341561005157600080fd5b61005961006f565b6040518082815260200191505060405180910390f35b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16630c55699c6000604051602001526040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381600087803b15156100fe57600080fd5b6102c65a03f1151561010f57600080fd5b505050604051805190509050905600a165627a7a723058207f8994e02725b47d76ec73e5c54a338d27b306dd1c830276bff2d75fcd1a5c920029000000000000000000000000".to_string(); - deploy_data.push_str(&address_a.as_bytes().to_vec().to_hex()); + deploy_data.push_str(&address_a.as_bytes().to_hex::()); let private_contract_b_test = deploy_data.from_hex().unwrap(); let mut private_create_tx2 = Transaction::default(); private_create_tx2.action = Action::Create; @@ -283,5 +283,5 @@ fn call_other_private_contract() { query_tx.nonce = 3.into(); let query_tx = query_tx.sign(&key1.secret(), chain_id); let result = pm.private_call(BlockId::Latest, &query_tx).unwrap(); - assert_eq!(&result.output[..], &("2a00000000000000000000000000000000000000000000000000000000000000".from_hex().unwrap()[..])); + assert_eq!(&result.output[..], &("2a00000000000000000000000000000000000000000000000000000000000000".from_hex::>().unwrap()[..])); } diff --git a/ethcore/res/ethereum/foundation.json b/ethcore/res/ethereum/foundation.json index ba988e5bf40..69a37701f77 100644 --- a/ethcore/res/ethereum/foundation.json +++ b/ethcore/res/ethereum/foundation.json @@ -4777,7 +4777,9 @@ "enode://8499da03c47d637b20eee24eec3c356c9a2e6148d6fe25ca195c7949ab8ec2c03e3556126b0d7ed644675e78c4318b08691b7b57de10e5f0d40d05b09238fa0a@52.187.207.27:30303", "enode://103858bdb88756c71f15e9b5e09b56dc1be52f0a5021d46301dbbfb7e130029cc9d0d6f73f693bc29b665770fff7da4d34f3c6379fe12721b5d7a0bcb5ca1fc1@191.234.162.198:30303", "enode://715171f50508aba88aecd1250af392a45a330af91d7b90701c436b618c86aaa1589c9184561907bebbb56439b8f8787bc01f49a7c77276c58c1b09822d75e8e8@52.231.165.108:30303", - "enode://5d6d7cd20d6da4bb83a1d28cadb5d409b64edf314c0335df658c1a54e32c7c4a7ab7823d57c39b6a757556e68ff1df17c748b698544a55cb488b52479a92b60f@104.42.217.25:30303" + "enode://5d6d7cd20d6da4bb83a1d28cadb5d409b64edf314c0335df658c1a54e32c7c4a7ab7823d57c39b6a757556e68ff1df17c748b698544a55cb488b52479a92b60f@104.42.217.25:30303", + "enode://68f46370191198b71a1595dd453c489bbfe28036a9951fc0397fabd1b77462930b3c5a5359b20e99677855939be47b39fc8edcf1e9ff2522a922b86d233bf2df@144.217.153.76:30303", + "enode://ffed6382e05ee42854d862f08e4e39b8452c50a5a5d399072c40f9a0b2d4ad34b0eb5312455ad8bcf0dcb4ce969dc89a9a9fd00183eaf8abf46bbcc59dc6e9d5@51.195.3.238:30303" ], "accounts": { "0x0000000000000000000000000000000000000001": { diff --git a/ethcore/res/ethereum/poasokol.json b/ethcore/res/ethereum/poasokol.json index db0e3d94688..2dd0f52c1dd 100644 --- a/ethcore/res/ethereum/poasokol.json +++ b/ethcore/res/ethereum/poasokol.json @@ -25,7 +25,10 @@ } }, "blockRewardContractAddress": "0x3145197AD50D7083D0222DE4fCCf67d9BD05C30D", - "blockRewardContractTransition": 4639000 + "blockRewardContractTransition": 4639000, + "randomnessContractAddress": { + "13391641": "0x8f2b78169B0970F11a762e56659Db52B59CBCf1B" + } } } }, diff --git a/ethcore/snapshot/Cargo.toml b/ethcore/snapshot/Cargo.toml index 54d4ed57c71..1fae7b04e09 100644 --- a/ethcore/snapshot/Cargo.toml +++ b/ethcore/snapshot/Cargo.toml @@ -36,7 +36,7 @@ rand = "0.7" rand_xorshift = "0.2" parking_lot = "0.10.0" rlp = "0.4.2" -rlp_derive = { path = "../../util/rlp-derive" } +rlp-derive = "0.1" scopeguard = "1.0.0" snappy = { package = "parity-snappy", version ="0.1.0" } state-db = { path = "../state-db" } diff --git a/ethcore/snapshot/snapshot-tests/Cargo.toml b/ethcore/snapshot/snapshot-tests/Cargo.toml index 2166eaca4c4..152bc390ef4 100644 --- a/ethcore/snapshot/snapshot-tests/Cargo.toml +++ b/ethcore/snapshot/snapshot-tests/Cargo.toml @@ -27,7 +27,7 @@ kvdb = "0.4.0" kvdb-sled = "0.1" log = "0.4.8" parking_lot = "0.10.0" -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } rand = "0.7" rand_xorshift = "0.2" rlp = "0.4.2" diff --git a/ethcore/spec/Cargo.toml b/ethcore/spec/Cargo.toml index 8fa2c73872a..ef815824588 100644 --- a/ethcore/spec/Cargo.toml +++ b/ethcore/spec/Cargo.toml @@ -28,6 +28,7 @@ keccak-hash = "0.4.0" kvdb-memorydb = "0.4.0" log = "0.4.8" machine = { path = "../machine" } +maplit = "1" null-engine = { path = "../engines/null-engine" } pod = { path = "../pod" } rlp = "0.4.2" diff --git a/ethcore/spec/src/spec.rs b/ethcore/spec/src/spec.rs index 1116fc626a5..da45c3b8c70 100644 --- a/ethcore/spec/src/spec.rs +++ b/ethcore/spec/src/spec.rs @@ -17,7 +17,7 @@ //! Parameters for a block chain. use std::{ - collections::BTreeMap, + collections::{BTreeMap, BTreeSet}, convert::TryFrom, fmt, io::Read, @@ -47,6 +47,7 @@ use instant_seal::{InstantSeal, InstantSealParams}; use keccak_hash::{KECCAK_NULL_RLP, keccak}; use log::{trace, warn}; use machine::{executive::Executive, Machine, substate::Substate}; +use maplit::btreeset; use null_engine::NullEngine; use pod::PodState; use rlp::{Rlp, RlpStream}; @@ -218,6 +219,8 @@ pub struct Spec { pub seal_rlp: Bytes, /// Hardcoded synchronization. Allows the light client to immediately jump to a specific block. pub hardcoded_sync: Option, + /// List of hard forks in the network. + pub hard_forks: BTreeSet, /// Contract constructors to be executed on genesis. pub constructors: Vec<(Address, Bytes)>, /// May be pre-populated if we know this in advance. @@ -280,7 +283,7 @@ fn load_from(spec_params: SpecParams, s: ethjson::spec::Spec) -> Result Result, - ) -> Arc { + ) -> (Arc, BTreeSet) { + let mut hard_forks = btreeset![ + params.eip150_transition, + params.eip160_transition, + params.eip161abc_transition, + params.eip161d_transition, + params.eip98_transition, + params.eip658_transition, + params.eip155_transition, + params.validate_receipts_transition, + params.validate_chain_id_transition, + params.eip140_transition, + params.eip210_transition, + params.eip211_transition, + params.eip214_transition, + params.eip145_transition, + params.eip1052_transition, + params.eip1283_transition, + params.eip1283_disable_transition, + params.eip1283_reenable_transition, + params.eip1014_transition, + params.eip1706_transition, + params.eip1344_transition, + params.eip1884_transition, + params.eip2028_transition, + params.eip2200_advance_transition, + params.dust_protection_transition, + params.wasm_activation_transition, + params.kip4_transition, + params.kip6_transition, + params.max_code_size_transition, + params.transaction_permission_contract_transition, + ]; + // BUG: Rinkeby has homestead transition at block 1 but we can't reflect that in specs for non-Ethash networks + if params.network_id == 0x4 { + hard_forks.insert(1); + } + let machine = Self::machine(&engine_spec, params, builtins); - match engine_spec { + let engine: Arc = match engine_spec { ethjson::spec::Engine::Null(null) => Arc::new(NullEngine::new(null.params.into(), machine)), - ethjson::spec::Engine::Ethash(ethash) => Arc::new(Ethash::new(spec_params.cache_dir, ethash.params.into(), machine, spec_params.optimization_setting)), + ethjson::spec::Engine::Ethash(ethash) => { + // Specific transitions for Ethash-based networks + for block in &[ethash.params.homestead_transition, ethash.params.dao_hardfork_transition] { + if let Some(block) = *block { + hard_forks.insert(block.into()); + } + } + + // Ethereum's difficulty bomb delay is a fork too + if let Some(delays) = ðash.params.difficulty_bomb_delays { + for delay in delays.keys().copied() { + hard_forks.insert(delay.into()); + } + } + Arc::new(Ethash::new(spec_params.cache_dir, ethash.params.into(), machine, spec_params.optimization_setting)) + }, ethjson::spec::Engine::InstantSeal(Some(instant_seal)) => Arc::new(InstantSeal::new(instant_seal.params.into(), machine)), ethjson::spec::Engine::InstantSeal(None) => Arc::new(InstantSeal::new(InstantSealParams::default(), machine)), ethjson::spec::Engine::BasicAuthority(basic_authority) => Arc::new(BasicAuthority::new(basic_authority.params.into(), machine)), @@ -360,7 +416,12 @@ impl Spec { .expect("Failed to start Clique consensus engine."), ethjson::spec::Engine::AuthorityRound(authority_round) => AuthorityRound::new(authority_round.params.into(), machine) .expect("Failed to start AuthorityRound consensus engine."), - } + }; + + // Dummy value is a filler for non-existent transitions + hard_forks.remove(&BlockNumber::max_value()); + + (engine, hard_forks) } /// Get common blockchain parameters. diff --git a/ethcore/src/block.rs b/ethcore/src/block.rs index 381e2187550..992437a1455 100644 --- a/ethcore/src/block.rs +++ b/ethcore/src/block.rs @@ -35,7 +35,7 @@ use std::{cmp, ops}; use std::sync::Arc; use bytes::Bytes; -use ethereum_types::{H256, U256, Address, Bloom}; +use ethereum_types::{U256, Address, Bloom}; use engine::Engine; use trie_vm_factories::Factories; @@ -102,7 +102,7 @@ pub trait Drain { impl<'x> OpenBlock<'x> { /// Create a new `OpenBlock` ready for transaction pushing. - pub fn new<'a>( + pub fn new( engine: &'x dyn Engine, factories: Factories, tracing: bool, @@ -168,7 +168,7 @@ impl<'x> OpenBlock<'x> { /// Push a transaction into the block. /// /// If valid, it will be executed, and archived together with the receipt. - pub fn push_transaction(&mut self, t: SignedTransaction, h: Option) -> Result<&Receipt, Error> { + pub fn push_transaction(&mut self, t: SignedTransaction) -> Result<&Receipt, Error> { if self.block.transactions_set.contains(&t.hash()) { return Err(TransactionError::AlreadyImported.into()); } @@ -176,7 +176,7 @@ impl<'x> OpenBlock<'x> { let env_info = self.block.env_info(); let outcome = self.block.state.apply(&env_info, self.engine.machine(), &t, self.block.traces.is_enabled())?; - self.block.transactions_set.insert(h.unwrap_or_else(||t.hash())); + self.block.transactions_set.insert(t.hash()); self.block.transactions.push(t.into()); if let Tracing::Enabled(ref mut traces) = self.block.traces { traces.push(outcome.trace.into()); @@ -189,7 +189,7 @@ impl<'x> OpenBlock<'x> { #[cfg(not(feature = "slow-blocks"))] fn push_transactions(&mut self, transactions: Vec) -> Result<(), Error> { for t in transactions { - self.push_transaction(t, None)?; + self.push_transaction(t)?; } Ok(()) } @@ -203,13 +203,12 @@ impl<'x> OpenBlock<'x> { for t in transactions { let hash = t.hash(); let start = time::Instant::now(); - self.push_transaction(t, None)?; - let took = start.elapsed(); - let took_ms = took.as_secs() * 1000 + took.subsec_nanos() as u64 / 1000000; - if took > time::Duration::from_millis(slow_tx) { - warn!("Heavy ({} ms) transaction in block {:?}: {:?}", took_ms, self.block.header.number(), hash); + self.push_transaction(t)?; + let elapsed_millis = start.elapsed().as_millis(); + if elapsed_millis > slow_tx { + warn!("Heavy ({} ms) transaction in block {:?}: {:?}", elapsed_millis, self.block.header.number(), hash); } - debug!(target: "tx", "Transaction {:?} took: {} ms", hash, took_ms); + debug!(target: "tx", "Transaction {:?} took: {} ms", hash, elapsed_millis); } Ok(()) @@ -222,6 +221,10 @@ impl<'x> OpenBlock<'x> { self.block.header.set_timestamp(header.timestamp()); self.block.header.set_uncles_hash(*header.uncles_hash()); self.block.header.set_transactions_root(*header.transactions_root()); + // For Aura-based chains, the seal may contain EmptySteps which are used to bestow rewards; + // such rewards affect the state and the state root (see + // https://github.com/paritytech/parity-ethereum/pull/11475). + self.block.header.set_seal(header.seal().to_vec()); // TODO: that's horrible. set only for backwards compatibility if header.extra_data().len() > self.engine.maximum_extra_data_size() { warn!("Couldn't set extradata. Ignoring."); @@ -343,10 +346,10 @@ impl LockedBlock { let expected_seal_fields = engine.seal_fields(&self.header); let mut s = self; if seal.len() != expected_seal_fields { - Err(BlockError::InvalidSealArity(Mismatch { + return Err(Error::Block(BlockError::InvalidSealArity(Mismatch { expected: expected_seal_fields, found: seal.len() - }))?; + }))); } s.block.header.set_seal(seal); @@ -500,7 +503,6 @@ mod tests { verification::Unverified, }; use hash_db::EMPTY_PREFIX; - use spec; /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header fn enact_bytes( @@ -554,7 +556,7 @@ mod tests { b.close_and_lock() } - /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block aferwards + /// Enact the block given by `block_bytes` using `engine` on the database `db` with given `parent` block header. Seal the block afterwards fn enact_and_seal( block_bytes: Vec, engine: &dyn Engine, diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index 9ceeff78e6e..55e0b1889df 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -742,7 +742,7 @@ impl Client { let chain = Arc::new(BlockChain::new(config.blockchain.clone(), &gb, db.clone())); let tracedb = RwLock::new(TraceDB::new(config.tracing.clone(), db.clone(), chain.clone())); - trace!("Cleanup journal: DB Earliest = {:?}, Latest = {:?}", state_db.journal_db().earliest_era(), state_db.journal_db().latest_era()); + debug!(target: "client", "Cleanup journal: DB Earliest = {:?}, Latest = {:?}", state_db.journal_db().earliest_era(), state_db.journal_db().latest_era()); let history = if config.history < MIN_HISTORY_SIZE { info!(target: "client", "Ignoring pruning history parameter of {}\ @@ -942,7 +942,6 @@ impl Client { let (result, items) = self.prove_transaction(tx, id) .ok_or_else(|| "Unable to make call. State unavailable?".to_string())?; - let items = items.into_iter().map(|x| x.to_vec()).collect(); Ok((result, items)) }; @@ -1070,6 +1069,7 @@ impl Client { // early exit for pruned blocks if db.is_prunable() && self.pruning_info().earliest_state > block_number { + trace!(target: "client", "State for block #{} is pruned. Earliest state: {:?}", block_number, self.pruning_info().earliest_state); return None; } @@ -2388,7 +2388,7 @@ impl ImportSealedBlock for Client { // Do a super duper basic verification to detect potential bugs if let Err(e) = self.engine.verify_block_basic(&header) { self.importer.bad_blocks.report( - block.rlp_bytes(), + raw, format!("Detected an issue with locally sealed block: {}", e), ); return Err(e); diff --git a/ethcore/src/client/config.rs b/ethcore/src/client/config.rs index 690596c3323..40980061986 100644 --- a/ethcore/src/client/config.rs +++ b/ethcore/src/client/config.rs @@ -78,7 +78,7 @@ pub struct ClientConfig { pub spec_name: String, /// Type of block verifier used by client. pub verifier_type: VerifierType, - /// State db cache-size. + /// State db cache-size. Default: 25Mb. pub state_cache_size: usize, /// EVM jump-tables cache size. pub jump_table_size: usize, diff --git a/ethcore/src/json_tests/chain.rs b/ethcore/src/json_tests/chain.rs index ab6935025ea..51eb4429a71 100644 --- a/ethcore/src/json_tests/chain.rs +++ b/ethcore/src/json_tests/chain.rs @@ -25,8 +25,7 @@ use io::IoChannel; use test_helpers::{self, EvmTestClient}; use types::verification::Unverified; use verification::{VerifierType, queue::kind::BlockLike}; -use super::SKIP_TESTS; -use super::HookType; +use super::{HookType, SKIP_TESTS}; #[allow(dead_code)] fn skip_test(name: &String) -> bool { @@ -56,7 +55,7 @@ pub fn json_chain_test(path: &Path, json_data: &[u8], let mut fail_unless = |cond: bool| { if !cond && !fail { failed.push(name.clone()); - flushln!("FAIL"); + flushed_writeln!("FAIL"); fail = true; true } else { @@ -64,7 +63,7 @@ pub fn json_chain_test(path: &Path, json_data: &[u8], } }; - flush!(" - {}...", name); + flushed_write!(" - {}...", name); let spec = { let mut spec = match EvmTestClient::fork_spec_from_json(&blockchain.network) { @@ -123,9 +122,9 @@ pub fn json_chain_test(path: &Path, json_data: &[u8], } if !fail { - flushln!("ok"); + flushed_writeln!("OK"); } else { - flushln!("fail"); + flushed_writeln!("FAILED"); } start_stop_hook(&name, HookType::OnStop); diff --git a/ethcore/src/json_tests/difficulty.rs b/ethcore/src/json_tests/difficulty.rs index fccd9b4eba4..15626f48def 100644 --- a/ethcore/src/json_tests/difficulty.rs +++ b/ethcore/src/json_tests/difficulty.rs @@ -37,8 +37,7 @@ pub fn json_difficulty_test( for (name, test) in tests.into_iter() { start_stop_hook(&name, HookType::OnStart); - flush!(" - {}...", name); - println!(" - {}...", name); + flushed_writeln!(" - {}...", name); let mut parent_header = Header::new(); let block_number: u64 = test.current_block_number.into(); @@ -53,7 +52,7 @@ pub fn json_difficulty_test( engine.populate_from_parent(&mut header, &parent_header); let expected_difficulty: U256 = test.current_difficulty.into(); assert_eq!(header.difficulty(), &expected_difficulty); - flushln!("ok"); + flushed_writeln!("OK"); start_stop_hook(&name, HookType::OnStop); } diff --git a/ethcore/src/json_tests/macros.rs b/ethcore/src/json_tests/macros.rs new file mode 100644 index 00000000000..8735352f086 --- /dev/null +++ b/ethcore/src/json_tests/macros.rs @@ -0,0 +1,86 @@ +//! Helper macros for running the `JSON tests` + +/// Declares a test: +/// +/// declare_test!(test_name, "path/to/folder/with/tests"); +/// +/// Declares a test but skip the named test files inside the folder (no extension): +/// +/// declare_test!(skip => ["a-test-file", "other-test-file"], test_name, "path/to/folder/with/tests"); +/// +/// NOTE: a skipped test is considered a passing test as far as `cargo test` is concerned. Normally +/// one test corresponds to a folder full of test files, each of which may contain many tests. +#[macro_export] +macro_rules! declare_test { + (skip => $arr: expr, $id: ident, $name: expr) => { + #[cfg(test)] + #[test] + #[allow(non_snake_case)] + fn $id() { + test!($name, $arr); + } + }; + (ignore => $id: ident, $name: expr) => { + #[cfg(test)] + #[ignore] + #[test] + #[allow(non_snake_case)] + fn $id() { + test!($name, []); + } + }; + (heavy => $id: ident, $name: expr) => { + #[cfg(test)] + #[cfg(feature = "test-heavy")] + #[test] + #[allow(non_snake_case)] + fn $id() { + test!($name, []); + } + }; + ($id: ident, $name: expr) => { + #[cfg(test)] + #[test] + #[allow(non_snake_case)] + fn $id() { + test!($name, []); + } + } +} + +#[cfg(test)] +macro_rules! test { + ($name: expr, $skip: expr) => { + $crate::json_tests::test_common::run_test_path( + std::path::Path::new(concat!("res/ethereum/tests/", $name)), + &$skip, + do_json_test, + &mut |_, _| () + ); + } +} + +/// Similar to `print!` but flushes stdout in order to ensure the output is emitted immediately. +#[macro_export] +macro_rules! flushed_write { + ($arg:expr) => ($crate::json_tests::macros::write_and_flush($arg.into())); + ($($arg:tt)*) => ($crate::json_tests::macros::write_and_flush(format!("{}", format_args!($($arg)*)))); +} + +/// Similar to `println!` but flushes stdout in order to ensure the output is emitted immediately. +#[macro_export] +macro_rules! flushed_writeln { + ($fmt:expr) => (flushed_write!(concat!($fmt, "\n"))); + ($fmt:expr, $($arg:tt)*) => (flushed_write!(concat!($fmt, "\n"), $($arg)*)); +} + +/// Write to stdout and flush (ignores errors) +#[doc(hidden)] +pub fn write_and_flush(s: String) { + if let Err(err) = std::io::Write::write_all(&mut std::io::stdout(), s.as_bytes()) { + error!(target: "json_tests", "io::Write::write_all to stdout failed because of: {:?}", err); + } + if let Err(err) = std::io::Write::flush(&mut std::io::stdout()) { + error!(target: "json_tests", "io::Write::flush stdout failed because of: {:?}", err); + } +} diff --git a/ethcore/src/json_tests/mod.rs b/ethcore/src/json_tests/mod.rs index 8a5d13d779d..04b94b23fc4 100644 --- a/ethcore/src/json_tests/mod.rs +++ b/ethcore/src/json_tests/mod.rs @@ -14,23 +14,24 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -//! Helpers and tests for operating on jsontests. +//! Helpers and tests for operating on `JSON` tests. #[macro_use] -mod test_common; +mod macros; -mod transaction; +mod chain; mod executive; +mod skip; mod state; -mod chain; +mod test_common; +mod transaction; mod trie; -mod skip; #[cfg(test)] mod difficulty; -pub use self::test_common::HookType; pub use self::executive::run_test_path as run_executive_test_path; pub use self::executive::run_test_file as run_executive_test_file; +pub use self::test_common::HookType; use self::skip::SKIP_TESTS; diff --git a/ethcore/src/json_tests/state.rs b/ethcore/src/json_tests/state.rs index 177afb949d4..6f8886ea1c0 100644 --- a/ethcore/src/json_tests/state.rs +++ b/ethcore/src/json_tests/state.rs @@ -17,13 +17,10 @@ use std::path::Path; use super::test_common::*; use pod::PodState; -use trace; -use ethjson; use test_helpers::{EvmTestClient, EvmTestError, TransactErr, TransactSuccess}; use types::transaction::SignedTransaction; use vm::EnvInfo; use super::SKIP_TESTS; -use super::HookType; #[allow(dead_code)] fn skip_test(subname: &str, chain: &String, number: usize) -> bool { @@ -84,25 +81,25 @@ pub fn json_chain_test(path: &Path, json_data: &[u8], match result() { Err(err) => { println!("{} !!! Unexpected internal error: {:?}", info, err); - flushln!("{} fail", info); + flushed_writeln!("{} fail", info); failed.push(name.clone()); }, Ok(Ok(TransactSuccess { state_root, .. })) if state_root != post_root => { println!("{} !!! State mismatch (got: {}, expect: {}", info, state_root, post_root); - flushln!("{} fail", info); + flushed_writeln!("{} fail", info); failed.push(name.clone()); }, Ok(Err(TransactErr { state_root, ref error, .. })) if state_root != post_root => { println!("{} !!! State mismatch (got: {}, expect: {}", info, state_root, post_root); println!("{} !!! Execution error: {:?}", info, error); - flushln!("{} fail", info); + flushed_writeln!("{} fail", info); failed.push(name.clone()); }, Ok(Err(TransactErr { error, .. })) => { - flushln!("{} ok ({:?})", info, error); + flushed_writeln!("{} ok ({:?})", info, error); }, Ok(_) => { - flushln!("{} ok", info); + flushed_writeln!("{} ok", info); }, } } diff --git a/ethcore/src/json_tests/test_common.rs b/ethcore/src/json_tests/test_common.rs index c6e99953359..d6812fd0d55 100644 --- a/ethcore/src/json_tests/test_common.rs +++ b/ethcore/src/json_tests/test_common.rs @@ -19,6 +19,7 @@ use std::io::Read; use std::fs::{File, read_dir}; use std::path::Path; use std::ffi::OsString; + pub use ethereum_types::{H256, U256, Address}; /// Indicate when to run the hook passed to test functions. @@ -41,7 +42,7 @@ pub fn run_test_path( if !skip.is_empty() { // todo[dvdplm] it's really annoying to have to use flushln here. Should be `info!(target: // "json-tests", …)`. Issue https://github.com/paritytech/parity-ethereum/issues/11084 - flushln!("[run_test_path] Skipping tests in {}: {:?}", path.display(), skip); + flushed_writeln!("[run_test_path] Skipping tests in {}: {:?}", path.display(), skip); } let mut errors = Vec::new(); run_test_path_inner(path, skip, runner, start_stop_hook, &mut errors); @@ -121,63 +122,3 @@ pub fn run_test_file( let empty: [String; 0] = []; assert_eq!(results, empty); } - -#[cfg(test)] -macro_rules! test { - ($name: expr, $skip: expr) => { - ::json_tests::test_common::run_test_path( - ::std::path::Path::new(concat!("res/ethereum/tests/", $name)), - &$skip, - do_json_test, - &mut |_, _| () - ); - } -} - -/// Declares a test: -/// -/// declare_test!(test_name, "path/to/folder/with/tests"); -/// -/// Declares a test but skip the named test files inside the folder (no extension): -/// -/// declare_test!(skip => ["a-test-file", "other-test-file"], test_name, "path/to/folder/with/tests"); -/// -/// NOTE: a skipped test is considered a passing test as far as `cargo test` is concerned. Normally -/// one test corresponds to a folder full of test files, each of which may contain many tests. -#[macro_export] -macro_rules! declare_test { - (skip => $arr: expr, $id: ident, $name: expr) => { - #[cfg(test)] - #[test] - #[allow(non_snake_case)] - fn $id() { - test!($name, $arr); - } - }; - (ignore => $id: ident, $name: expr) => { - #[cfg(test)] - #[ignore] - #[test] - #[allow(non_snake_case)] - fn $id() { - test!($name, []); - } - }; - (heavy => $id: ident, $name: expr) => { - #[cfg(test)] - #[cfg(feature = "test-heavy")] - #[test] - #[allow(non_snake_case)] - fn $id() { - test!($name, []); - } - }; - ($id: ident, $name: expr) => { - #[cfg(test)] - #[test] - #[allow(non_snake_case)] - fn $id() { - test!($name, []); - } - } -} diff --git a/ethcore/src/lib.rs b/ethcore/src/lib.rs index 0744deadee1..b66d3f0135a 100644 --- a/ethcore/src/lib.rs +++ b/ethcore/src/lib.rs @@ -63,8 +63,6 @@ extern crate vm; extern crate account_db; #[cfg(test)] extern crate ethcore_accounts as accounts; -#[cfg(test)] -extern crate stats; #[cfg(feature = "stratum")] extern crate ethcore_stratum; @@ -83,9 +81,6 @@ extern crate kvdb_sled; #[cfg(feature = "json-tests")] #[macro_use] extern crate lazy_static; -#[cfg(any(test, feature = "json-tests"))] -#[macro_use] -extern crate macros; #[cfg(any(test, feature = "test-helpers"))] extern crate pod; #[cfg(any(test, feature = "blooms-db"))] diff --git a/ethcore/src/miner/miner.rs b/ethcore/src/miner/miner.rs index ddddf5a1846..ff209e05baf 100644 --- a/ethcore/src/miner/miner.rs +++ b/ethcore/src/miner/miner.rs @@ -535,7 +535,7 @@ impl Miner { let result = client.verify_for_pending_block(&transaction, &open_block.header) .map_err(|e| e.into()) .and_then(|_| { - open_block.push_transaction(transaction, None) + open_block.push_transaction(transaction) }); let took = start.elapsed(); @@ -716,36 +716,34 @@ impl Miner { }, }; - let sealing_result = - match self.engine.generate_seal(&block, &parent_header) { - // Directly import a regular sealed block. - Seal::Regular(seal) => { - trace!(target: "miner", "Block #{}: Received a Regular seal.", block_number); - { - let mut sealing = self.sealing.lock(); - sealing.next_mandatory_reseal = Instant::now() + self.options.reseal_max_period; - } + match self.engine.generate_seal(&block, &parent_header) { + // Directly import a regular sealed block. + Seal::Regular(seal) => { + trace!(target: "miner", "Block #{}: Received a Regular seal.", block_number); + { + let mut sealing = self.sealing.lock(); + sealing.next_mandatory_reseal = Instant::now() + self.options.reseal_max_period; + } - block - .lock() - .seal(&*self.engine, seal) - .map(|sealed| { - match chain.import_sealed_block(sealed) { - Ok(_) => true, - Err(e) => { - error!(target: "miner", "Block #{}: seal_and_import_block_internally: import_sealed_block returned {:?}", block_number, e); - false - } + block + .lock() + .seal(&*self.engine, seal) + .map(|sealed| { + match chain.import_sealed_block(sealed) { + Ok(_) => true, + Err(e) => { + error!(target: "miner", "Block #{}: seal_and_import_block_internally: import_sealed_block returned {:?}", block_number, e); + false } - }) - .unwrap_or_else(|e| { - warn!("ERROR: Block #{}, importing sealed block failed when given internally generated seal: {}", block_number, e); - false - }) - }, - Seal::None => false, - }; - sealing_result + } + }) + .unwrap_or_else(|e| { + warn!("ERROR: Block #{}, importing sealed block failed when given internally generated seal: {}", block_number, e); + false + }) + }, + Seal::None => false, + } } /// Prepares work which has to be done to seal. @@ -1276,7 +1274,6 @@ impl miner::MinerService for Miner { if self.seal_and_import_block_internally(chain, block) { trace!(target: "miner", "update_sealing: imported internally sealed block"); } - return }, SealingState::NotReady => unreachable!("We returned right after sealing_state was computed. qed."), SealingState::External => { @@ -1569,7 +1566,7 @@ mod tests { } fn transaction_with_chain_id(chain_id: u64) -> SignedTransaction { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); Transaction { action: Action::Create, value: U256::zero(), @@ -1669,7 +1666,7 @@ mod tests { #[test] fn should_treat_unfamiliar_locals_selectively() { // given - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let client = TestBlockChainClient::default(); let mut local_accounts = ::std::collections::HashSet::new(); local_accounts.insert(keypair.address()); @@ -1813,7 +1810,7 @@ mod tests { let addr = tap.insert_account(keccak("1").into(), &"".into()).unwrap(); let client = generate_dummy_client_with_spec(spec); let engine_signer = Box::new((tap.clone(), addr, "".into())); - let msg = Default::default(); + let msg = [1u8; 32].into(); assert!(client.engine().sign(msg).is_err()); // should set engine signer and miner author diff --git a/ethcore/src/test_helpers/evm_test_client.rs b/ethcore/src/test_helpers/evm_test_client.rs index 252de7dbc28..1f225828059 100644 --- a/ethcore/src/test_helpers/evm_test_client.rs +++ b/ethcore/src/test_helpers/evm_test_client.rs @@ -271,7 +271,8 @@ impl<'a> EvmTestClient<'a> { } // Apply transaction - let result = self.state.apply_with_tracing(&env_info, self.spec.engine.machine(), &transaction, tracer, vm_tracer); + let trace_opts = executive::TransactOptions::new(tracer, vm_tracer); + let result = self.state.apply_with_tracing(&env_info, self.spec.engine.machine(), &transaction, trace_opts); let scheme = CreateContractAddress::FromSenderAndNonce; // Touch the coinbase at the end of the test to simulate @@ -285,12 +286,13 @@ impl<'a> EvmTestClient<'a> { }).ok(); // Touching also means that we should remove the account if it's within eip161 // conditions. - self.state.kill_garbage( - &vec![env_info.author].into_iter().collect(), - schedule.kill_empty, - &None, - false - ).ok(); + if schedule.kill_empty { + self.state.kill_garbage( + &vec![env_info.author].into_iter().collect(), + &None, + false + ).ok(); + } self.state.commit().ok(); diff --git a/ethcore/src/test_helpers/mod.rs b/ethcore/src/test_helpers/mod.rs index 9686387ee1e..7f0b71efeb1 100644 --- a/ethcore/src/test_helpers/mod.rs +++ b/ethcore/src/test_helpers/mod.rs @@ -190,7 +190,7 @@ pub fn generate_dummy_client_with_spec_and_data( action: Action::Create, data: vec![], value: U256::zero(), - }.sign(kp.secret(), Some(test_spec.chain_id())), None).unwrap(); + }.sign(kp.secret(), Some(test_spec.chain_id()))).unwrap(); n += 1; } @@ -247,7 +247,7 @@ pub fn push_block_with_transactions(client: &Arc, transactions: &[Signed b.set_timestamp(block_number * 10); for t in transactions { - b.push_transaction(t.clone(), None).unwrap(); + b.push_transaction(t.clone()).unwrap(); } let b = b.close_and_lock().unwrap().seal(test_engine, vec![]).unwrap(); diff --git a/ethcore/src/test_helpers/test_client.rs b/ethcore/src/test_helpers/test_client.rs index ae5bb5b7319..46cd854b3ec 100644 --- a/ethcore/src/test_helpers/test_client.rs +++ b/ethcore/src/test_helpers/test_client.rs @@ -295,7 +295,7 @@ impl TestBlockChainClient { _ => 1, }; let mut txs = RlpStream::new_list(num_transactions); - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let mut nonce = U256::zero(); for _ in 0..num_transactions { @@ -364,7 +364,7 @@ impl TestBlockChainClient { /// Inserts a transaction with given gas price to miners transactions queue. pub fn insert_transaction_with_gas_price_to_queue(&self, gas_price: U256) -> H256 { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let tx = Transaction { action: Action::Create, value: U256::from(100), diff --git a/ethcore/src/tests/client.rs b/ethcore/src/tests/client.rs index 0173097abfa..36aa72eece5 100644 --- a/ethcore/src/tests/client.rs +++ b/ethcore/src/tests/client.rs @@ -38,8 +38,6 @@ use client_traits::{ BlockInfo, BlockChainClient, BlockChainReset, ChainInfo, ImportExportBlocks, Tick, ImportBlock }; -use spec; -use stats; use machine::executive::{Executive, TransactOptions}; use miner::{Miner, PendingOrdering, MinerService}; use account_state::{State, CleanupMode, backend}; @@ -51,6 +49,14 @@ use test_helpers::{ use rustc_hex::ToHex; use registrar::RegistrarClient; +fn into_u256_vec<'a, T, I>(iter: I) -> Vec +where + I: IntoIterator, + T: Into + Clone + 'a, +{ + iter.into_iter().cloned().map(Into::into).collect() +} + #[test] fn imports_from_empty() { let db = test_helpers::new_db(); @@ -204,32 +210,32 @@ fn can_collect_garbage() { #[test] fn can_generate_gas_price_median() { - let client = generate_dummy_client_with_data(3, 1, slice_into![1, 2, 3]); + let client = generate_dummy_client_with_data(3, 1, &into_u256_vec(&[1, 2, 3])); assert_eq!(Some(&U256::from(2)), client.gas_price_corpus(3).median()); - let client = generate_dummy_client_with_data(4, 1, slice_into![1, 4, 3, 2]); + let client = generate_dummy_client_with_data(4, 1, &into_u256_vec(&[1, 4, 3, 2])); assert_eq!(Some(&U256::from(3)), client.gas_price_corpus(3).median()); } #[test] fn can_generate_gas_price_histogram() { - let client = generate_dummy_client_with_data(20, 1, slice_into![6354,8593,6065,4842,7845,7002,689,4958,4250,6098,5804,4320,643,8895,2296,8589,7145,2000,2512,1408]); + let client = generate_dummy_client_with_data(20, 1, &into_u256_vec(&[6354,8593,6065,4842,7845,7002,689,4958,4250,6098,5804,4320,643,8895,2296,8589,7145,2000,2512,1408])); let hist = client.gas_price_corpus(20).histogram(5).unwrap(); - let correct_hist = stats::Histogram { bucket_bounds: vec_into![643, 2294, 3945, 5596, 7247, 8898], counts: vec![4,2,4,6,4] }; + let correct_hist = stats::Histogram { bucket_bounds: into_u256_vec(&[643, 2294, 3945, 5596, 7247, 8898]), counts: vec![4,2,4,6,4] }; assert_eq!(hist, correct_hist); } #[test] fn empty_gas_price_histogram() { - let client = generate_dummy_client_with_data(20, 0, slice_into![]); + let client = generate_dummy_client_with_data(20, 0, &[]); assert!(client.gas_price_corpus(20).histogram(5).is_none()); } #[test] fn corpus_is_sorted() { - let client = generate_dummy_client_with_data(2, 1, slice_into![U256::from_str("11426908979").unwrap(), U256::from_str("50426908979").unwrap()]); + let client = generate_dummy_client_with_data(2, 1, &[U256::from_str("11426908979").unwrap(), U256::from_str("50426908979").unwrap()]); let corpus = client.gas_price_corpus(20); assert!(corpus[0] < corpus[1]); } diff --git a/ethcore/src/tests/trace.rs b/ethcore/src/tests/trace.rs index cc4bb096db0..e426eeae40f 100644 --- a/ethcore/src/tests/trace.rs +++ b/ethcore/src/tests/trace.rs @@ -159,7 +159,7 @@ fn can_trace_block_and_uncle_reward() { action: Action::Create, data: vec![], value: U256::zero(), - }.sign(kp.secret(), Some(spec.network_id())), None).unwrap(); + }.sign(kp.secret(), Some(spec.network_id()))).unwrap(); n += 1; } diff --git a/ethcore/sync/Cargo.toml b/ethcore/sync/Cargo.toml index f530469125e..58898303c60 100644 --- a/ethcore/sync/Cargo.toml +++ b/ethcore/sync/Cargo.toml @@ -16,6 +16,7 @@ devp2p = { package = "ethcore-network-devp2p", path = "../../util/network-devp2p enum_primitive = "0.1.1" ethcore-io = { path = "../../util/io" } ethcore-private-tx = { path = "../private-tx" } +ethereum-forkid = "0.1" ethereum-types = "0.8.0" fastmap = { path = "../../util/fastmap" } futures = "0.1" @@ -23,10 +24,9 @@ indexmap = "1.3.0" keccak-hash = "0.4.0" light = { package = "ethcore-light", path = "../light" } log = "0.4" -macros = { path = "../../util/macros" } network = { package = "ethcore-network", path = "../../util/network" } -parity-runtime = { path = "../../util/runtime" } -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-runtime = "0.1.1" +parity-crypto = { version = "0.5.0", features = ["publickey"] } parity-util-mem = "0.5.1" rand = "0.7" parking_lot = "0.10.0" @@ -43,5 +43,5 @@ ethcore-io = { path = "../../util/io", features = ["mio"] } kvdb-memorydb = "0.4.0" machine = { path = "../machine" } rand_xorshift = "0.2" -rustc-hex = "1.0" +rustc-hex = "2.1.0" spec = { path = "../spec" } diff --git a/ethcore/sync/src/api.rs b/ethcore/sync/src/api.rs index 61b5f34f296..433393b34e0 100644 --- a/ethcore/sync/src/api.rs +++ b/ethcore/sync/src/api.rs @@ -15,7 +15,7 @@ // along with Parity Ethereum. If not, see . use std::sync::{Arc, mpsc, atomic}; -use std::collections::{HashMap, BTreeMap}; +use std::collections::{BTreeSet, HashMap, BTreeMap}; use std::io; use std::ops::RangeInclusive; use std::time::Duration; @@ -27,10 +27,11 @@ use crate::sync_io::NetSyncIo; use crate::light_sync::{self, SyncInfo}; use crate::private_tx::PrivateTxHandler; use crate::chain::{ + fork_filter::ForkFilterApi, sync_packet::SyncPacket::{PrivateTransactionPacket, SignedPrivateTransactionPacket}, - ChainSyncApi, SyncState, SyncStatus as EthSyncStatus, ETH_PROTOCOL_VERSION_62, - ETH_PROTOCOL_VERSION_63, PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, - PAR_PROTOCOL_VERSION_3, PAR_PROTOCOL_VERSION_4, + ChainSyncApi, SyncState, SyncStatus as EthSyncStatus, + ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, + PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3, PAR_PROTOCOL_VERSION_4, }; use bytes::Bytes; @@ -49,7 +50,6 @@ use light::net::{ Capabilities, Handler as LightHandler, EventContext, SampleStore, }; use log::{trace, warn}; -use macros::hash_map; use network::{ client_version::ClientVersion, NetworkProtocolHandler, NetworkContext, PeerId, ProtocolId, @@ -269,6 +269,8 @@ pub struct Params { pub executor: Executor, /// Blockchain client. pub chain: Arc, + /// Forks. + pub forks: BTreeSet, /// Snapshot service. pub snapshot_service: Arc, /// Private tx service. @@ -349,10 +351,13 @@ impl EthSync { }) }; + let fork_filter = ForkFilterApi::new(&*params.chain, params.forks); + let (priority_tasks_tx, priority_tasks_rx) = mpsc::channel(); let sync = ChainSyncApi::new( params.config, &*params.chain, + fork_filter, params.private_tx_handler.as_ref().cloned(), priority_tasks_rx, ); @@ -606,7 +611,7 @@ impl ChainNotify for EthSync { _ => {}, } - self.network.register_protocol(self.eth_handler.clone(), self.subprotocol_name, &[ETH_PROTOCOL_VERSION_62, ETH_PROTOCOL_VERSION_63]) + self.network.register_protocol(self.eth_handler.clone(), self.subprotocol_name, &[ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64]) .unwrap_or_else(|e| warn!("Error registering ethereum protocol: {:?}", e)); // register the warp sync subprotocol self.network.register_protocol(self.eth_handler.clone(), WARP_SYNC_PROTOCOL_ID, &[PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_2, PAR_PROTOCOL_VERSION_3, PAR_PROTOCOL_VERSION_4]) @@ -795,7 +800,11 @@ impl NetworkConfiguration { max_peers: self.max_peers, min_peers: self.min_peers, max_handshakes: self.max_pending_peers, - reserved_protocols: hash_map![WARP_SYNC_PROTOCOL_ID => self.snapshot_peers], + reserved_protocols: { + let mut reserved = HashMap::new(); + reserved.insert(WARP_SYNC_PROTOCOL_ID, self.snapshot_peers); + reserved + }, reserved_nodes: self.reserved_nodes, ip_filter: self.ip_filter, non_reserved_mode: if self.allow_non_reserved { NonReservedPeerMode::Accept } else { NonReservedPeerMode::Deny }, diff --git a/ethcore/sync/src/block_sync.rs b/ethcore/sync/src/block_sync.rs index e7b01a260f8..47d62ad3ca9 100644 --- a/ethcore/sync/src/block_sync.rs +++ b/ethcore/sync/src/block_sync.rs @@ -577,7 +577,7 @@ impl BlockDownloader { }, Ok(_) => { trace_sync!(self, "Block queued {:?}", h); - imported.insert(h.clone()); + imported.insert(h); self.block_imported(&h, number, &parent); }, Err(EthcoreError::Block(BlockError::UnknownParent(_))) if allow_out_of_order => { @@ -671,7 +671,7 @@ mod tests { } fn dummy_signed_tx() -> SignedTransaction { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); Transaction::default().sign(keypair.secret(), None) } diff --git a/ethcore/sync/src/chain/fork_filter.rs b/ethcore/sync/src/chain/fork_filter.rs new file mode 100644 index 00000000000..4a4c706dfed --- /dev/null +++ b/ethcore/sync/src/chain/fork_filter.rs @@ -0,0 +1,143 @@ +// Copyright 2015-2019 Parity Technologies (UK) Ltd. +// This file is part of Parity Ethereum. + +// Parity Ethereum is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Parity Ethereum is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Parity Ethereum. If not, see . + +//! This module contains a wrapper that connects this codebase with `ethereum-forkid` crate which provides `FORK_ID` +//! to support Ethereum network protocol, version 64 and above. + +// Re-export ethereum-forkid crate contents here. +pub use ethereum_forkid::{BlockNumber, ForkId, RejectReason}; + +use client_traits::ChainInfo; +use ethereum_forkid::ForkFilter; +use parity_util_mem::MallocSizeOf; + +/// Wrapper around fork filter that provides integration with `ForkFilter`. +#[derive(MallocSizeOf)] +pub struct ForkFilterApi { + inner: ForkFilter, +} + +impl ForkFilterApi { + /// Create `ForkFilterApi` from `ChainInfo` and an `Iterator` over the hard forks. + pub fn new>(client: &C, forks: I) -> Self { + let chain_info = client.chain_info(); + Self { + inner: ForkFilter::new(chain_info.best_block_number, chain_info.genesis_hash, forks), + } + } + + #[cfg(test)] + /// Dummy version of ForkFilterApi with no forks. + pub fn new_dummy(client: &C) -> Self { + let chain_info = client.chain_info(); + Self { + inner: ForkFilter::new(chain_info.best_block_number, chain_info.genesis_hash, vec![]), + } + } + + fn update_head(&mut self, client: &C) { + self.inner.set_head(client.chain_info().best_block_number); + } + + /// Wrapper for `ForkFilter::current` + pub fn current(&mut self, client: &C) -> ForkId { + self.update_head(client); + self.inner.current() + } + + /// Wrapper for `ForkFilter::is_compatible` + pub fn is_compatible(&mut self, client: &C, fork_id: ForkId) -> Result<(), RejectReason> { + self.update_head(client); + self.inner.is_compatible(fork_id) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use spec::Spec; + use ethcore::test_helpers::TestBlockChainClient; + + fn test_spec Spec>(spec_builder: F, forks: Vec) { + let spec = (spec_builder)(); + let genesis_hash = spec.genesis_header().hash(); + let spec_forks = spec.hard_forks.clone(); + let client = TestBlockChainClient::new_with_spec(spec); + + assert_eq!( + ForkFilterApi::new(&client, spec_forks).inner, + ForkFilter::new(0, genesis_hash, forks) + ); + } + + #[test] + fn ethereum_spec() { + test_spec( + || spec::new_foundation(&String::new()), + vec![ + 1_150_000, + 1_920_000, + 2_463_000, + 2_675_000, + 4_370_000, + 7_280_000, + 9_069_000, + 9_200_000, + ], + ) + } + + #[test] + fn ropsten_spec() { + test_spec( + || spec::new_ropsten(&String::new()), + vec![ + 10, + 1_700_000, + 4_230_000, + 4_939_394, + 6_485_846, + 7_117_117, + ], + ) + } + + #[test] + fn rinkeby_spec() { + test_spec( + || spec::new_rinkeby(&String::new()), + vec![ + 1, + 2, + 3, + 1_035_301, + 3_660_663, + 4_321_234, + 5_435_345, + ], + ) + } + + #[test] + fn goerli_spec() { + test_spec( + || spec::new_goerli(&String::new()), + vec![ + 1_561_651, + ], + ) + } +} diff --git a/ethcore/sync/src/chain/handler.rs b/ethcore/sync/src/chain/handler.rs index 9716db1514f..2067c2c9e06 100644 --- a/ethcore/sync/src/chain/handler.rs +++ b/ethcore/sync/src/chain/handler.rs @@ -20,7 +20,7 @@ use std::{mem, cmp}; use crate::{ snapshot_sync::ChunkType, sync_io::SyncIo, - api::WARP_SYNC_PROTOCOL_ID, + api::{ETH_PROTOCOL, WARP_SYNC_PROTOCOL_ID}, block_sync::{BlockDownloaderImportError as DownloaderImportError, DownloadAction}, chain::{ sync_packet::{ @@ -32,7 +32,7 @@ use crate::{ } }, BlockSet, ChainSync, ForkConfirmation, PacketDecodeError, PeerAsking, PeerInfo, SyncRequester, - SyncState, ETH_PROTOCOL_VERSION_62, ETH_PROTOCOL_VERSION_63, MAX_NEW_BLOCK_AGE, MAX_NEW_HASHES, + SyncState, ETH_PROTOCOL_VERSION_63, ETH_PROTOCOL_VERSION_64, MAX_NEW_BLOCK_AGE, MAX_NEW_HASHES, PAR_PROTOCOL_VERSION_1, PAR_PROTOCOL_VERSION_3, PAR_PROTOCOL_VERSION_4, } }; @@ -562,17 +562,34 @@ impl SyncHandler { /// Called by peer to report status fn on_peer_status(sync: &mut ChainSync, io: &mut dyn SyncIo, peer_id: PeerId, r: &Rlp) -> Result<(), DownloaderImportError> { + let mut r = r.iter(); sync.handshaking_peers.remove(&peer_id); - let protocol_version: u8 = r.val_at(0)?; + let protocol_version: u8 = r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?; + let eth_protocol_version = io.protocol_version(Ð_PROTOCOL, peer_id); let warp_protocol_version = io.protocol_version(&WARP_SYNC_PROTOCOL_ID, peer_id); let warp_protocol = warp_protocol_version != 0; let private_tx_protocol = warp_protocol_version >= PAR_PROTOCOL_VERSION_3.0; + let network_id = r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?; + let difficulty = Some(r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?); + let latest_hash = r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?; + let genesis = r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?; + let forkid_validation_error = { + if eth_protocol_version >= ETH_PROTOCOL_VERSION_64.0 { + let fork_id = r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?; + sync.fork_filter.is_compatible(io.chain(), fork_id).err().map(|e| (fork_id, e)) + } else { + None + } + }; + let snapshot_hash = if warp_protocol { Some(r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?) } else { None }; + let snapshot_number = if warp_protocol { Some(r.next().ok_or(rlp::DecoderError::RlpIsTooShort)?.as_val()?) } else { None }; + let private_tx_enabled = if private_tx_protocol { r.next().and_then(|v| v.as_val().ok()).unwrap_or(false) } else { false }; let peer = PeerInfo { protocol_version, - network_id: r.val_at(1)?, - difficulty: Some(r.val_at(2)?), - latest_hash: r.val_at(3)?, - genesis: r.val_at(4)?, + network_id, + difficulty, + latest_hash, + genesis, asking: PeerAsking::Nothing, asking_blocks: Vec::new(), asking_hash: None, @@ -583,10 +600,10 @@ impl SyncHandler { expired: false, confirmation: if sync.fork_block.is_none() { ForkConfirmation::Confirmed } else { ForkConfirmation::Unconfirmed }, asking_snapshot_data: None, - snapshot_hash: if warp_protocol { Some(r.val_at(5)?) } else { None }, - snapshot_number: if warp_protocol { Some(r.val_at(6)?) } else { None }, + snapshot_hash, + snapshot_number, block_set: None, - private_tx_enabled: if private_tx_protocol { r.val_at(7).unwrap_or(false) } else { false }, + private_tx_enabled, client_version: ClientVersion::from(io.peer_version(peer_id)), }; @@ -627,10 +644,14 @@ impl SyncHandler { trace!(target: "sync", "Peer {} network id mismatch (ours: {}, theirs: {})", peer_id, sync.network_id, peer.network_id); return Err(DownloaderImportError::Invalid); } + if let Some((fork_id, reason)) = forkid_validation_error { + trace!(target: "sync", "Peer {} incompatible fork id (fork id: {:#x}/{}, error: {:?})", peer_id, fork_id.hash.0, fork_id.next, reason); + return Err(DownloaderImportError::Invalid); + } if false || (warp_protocol && (peer.protocol_version < PAR_PROTOCOL_VERSION_1.0 || peer.protocol_version > PAR_PROTOCOL_VERSION_4.0)) - || (!warp_protocol && (peer.protocol_version < ETH_PROTOCOL_VERSION_62.0 || peer.protocol_version > ETH_PROTOCOL_VERSION_63.0)) + || (!warp_protocol && (peer.protocol_version < ETH_PROTOCOL_VERSION_63.0 || peer.protocol_version > ETH_PROTOCOL_VERSION_64.0)) { trace!(target: "sync", "Peer {} unsupported eth protocol ({})", peer_id, peer.protocol_version); return Err(DownloaderImportError::Invalid); diff --git a/ethcore/sync/src/chain/mod.rs b/ethcore/sync/src/chain/mod.rs index 2b506c04f2e..998a56ccdac 100644 --- a/ethcore/sync/src/chain/mod.rs +++ b/ethcore/sync/src/chain/mod.rs @@ -92,6 +92,7 @@ mod propagator; mod requester; mod supplier; +pub mod fork_filter; pub mod sync_packet; use std::sync::{Arc, mpsc}; @@ -100,9 +101,10 @@ use std::cmp; use std::time::{Duration, Instant}; use crate::{ - EthProtocolInfo as PeerInfoDigest, PriorityTask, SyncConfig, WarpSync, WARP_SYNC_PROTOCOL_ID, + ETH_PROTOCOL, EthProtocolInfo as PeerInfoDigest, PriorityTask, SyncConfig, WarpSync, WARP_SYNC_PROTOCOL_ID, api::{Notification, PRIORITY_TIMER_INTERVAL}, block_sync::{BlockDownloader, DownloadAction}, + chain::fork_filter::ForkFilterApi, sync_io::SyncIo, snapshot_sync::Snapshot, transactions_stats::{TransactionsStats, Stats as TransactionStats}, @@ -147,10 +149,10 @@ malloc_size_of_is_0!(PeerInfo); pub type PacketDecodeError = DecoderError; -/// 63 version of Ethereum protocol. +/// Version 64 of the Ethereum protocol and number of packet IDs reserved by the protocol (packet count). +pub const ETH_PROTOCOL_VERSION_64: (u8, u8) = (64, 0x11); +/// Version 63 of the Ethereum protocol and number of packet IDs reserved by the protocol (packet count). pub const ETH_PROTOCOL_VERSION_63: (u8, u8) = (63, 0x11); -/// 62 version of Ethereum protocol. -pub const ETH_PROTOCOL_VERSION_62: (u8, u8) = (62, 0x11); /// 1 version of Parity protocol and the packet count. pub const PAR_PROTOCOL_VERSION_1: (u8, u8) = (1, 0x15); /// 2 version of Parity protocol (consensus messages added). @@ -427,11 +429,12 @@ impl ChainSyncApi { pub fn new( config: SyncConfig, chain: &dyn BlockChainClient, + fork_filter: ForkFilterApi, private_tx_handler: Option>, priority_tasks: mpsc::Receiver, ) -> Self { ChainSyncApi { - sync: RwLock::new(ChainSync::new(config, chain, private_tx_handler)), + sync: RwLock::new(ChainSync::new(config, chain, fork_filter, private_tx_handler)), priority_tasks: Mutex::new(priority_tasks), } } @@ -660,6 +663,8 @@ pub struct ChainSync { last_sent_block_number: BlockNumber, /// Network ID network_id: u64, + /// Fork filter + fork_filter: ForkFilterApi, /// Optional fork block to check fork_block: Option<(BlockNumber, H256)>, /// Snapshot downloader. @@ -688,6 +693,7 @@ impl ChainSync { pub fn new( config: SyncConfig, chain: &dyn BlockChainClient, + fork_filter: ForkFilterApi, private_tx_handler: Option>, ) -> Self { let chain_info = chain.chain_info(); @@ -705,6 +711,7 @@ impl ChainSync { old_blocks: None, last_sent_block_number: 0, network_id: config.network_id, + fork_filter, fork_block: config.fork_block, download_old_blocks: config.download_old_blocks, snapshot: Snapshot::new(), @@ -723,7 +730,7 @@ impl ChainSync { let last_imported_number = self.new_blocks.last_imported_block_number(); SyncStatus { state: self.state.clone(), - protocol_version: ETH_PROTOCOL_VERSION_63.0, + protocol_version: ETH_PROTOCOL_VERSION_64.0, network_id: self.network_id, start_block_number: self.starting_block, last_imported_block_number: Some(last_imported_number), @@ -1240,10 +1247,11 @@ impl ChainSync { /// Send Status message fn send_status(&mut self, io: &mut dyn SyncIo, peer: PeerId) -> Result<(), network::Error> { + let eth_protocol_version = io.protocol_version(Ð_PROTOCOL, peer); let warp_protocol_version = io.protocol_version(&WARP_SYNC_PROTOCOL_ID, peer); let warp_protocol = warp_protocol_version != 0; let private_tx_protocol = warp_protocol_version >= PAR_PROTOCOL_VERSION_3.0; - let protocol = if warp_protocol { warp_protocol_version } else { ETH_PROTOCOL_VERSION_63.0 }; + let protocol = if warp_protocol { warp_protocol_version } else { eth_protocol_version }; trace!(target: "sync", "Sending status to {}, protocol version {}", peer, protocol); let mut packet = RlpStream::new(); packet.begin_unbounded_list(); @@ -1253,6 +1261,9 @@ impl ChainSync { packet.append(&chain.total_difficulty); packet.append(&chain.best_block_hash); packet.append(&chain.genesis_hash); + if eth_protocol_version >= ETH_PROTOCOL_VERSION_64.0 { + packet.append(&self.fork_filter.current(io.chain())); + } if warp_protocol { let manifest = io.snapshot_service().manifest(); let block_number = manifest.as_ref().map_or(0, |m| m.block_number); @@ -1499,6 +1510,7 @@ pub mod tests { use crate::{ api::SyncConfig, + chain::ForkFilterApi, tests::{helpers::TestIo, snapshot::TestSnapshotService}, }; @@ -1594,8 +1606,7 @@ pub mod tests { } pub fn dummy_sync_with_peer(peer_latest_hash: H256, client: &dyn BlockChainClient) -> ChainSync { - - let mut sync = ChainSync::new(SyncConfig::default(), client, None,); + let mut sync = ChainSync::new(SyncConfig::default(), client, ForkFilterApi::new_dummy(client), None,); insert_dummy_peer(&mut sync, 0, peer_latest_hash); sync } diff --git a/ethcore/sync/src/chain/propagator.rs b/ethcore/sync/src/chain/propagator.rs index 2066b7f6574..1f5667acae0 100644 --- a/ethcore/sync/src/chain/propagator.rs +++ b/ethcore/sync/src/chain/propagator.rs @@ -338,7 +338,10 @@ mod tests { use crate::{ api::SyncConfig, - chain::{ChainSync, ForkConfirmation, PeerAsking, PeerInfo}, + chain::{ + fork_filter::ForkFilterApi, + ChainSync, ForkConfirmation, PeerAsking, PeerInfo + }, tests::{helpers::TestIo, snapshot::TestSnapshotService}, }; @@ -423,7 +426,7 @@ mod tests { client.add_blocks(2, EachBlockWith::Uncle); let queue = RwLock::new(VecDeque::new()); let block = client.block(BlockId::Latest).unwrap().into_inner(); - let mut sync = ChainSync::new(SyncConfig::default(), &client, None); + let mut sync = ChainSync::new(SyncConfig::default(), &client, ForkFilterApi::new_dummy(&client), None); sync.peers.insert(0, PeerInfo { // Messaging protocol @@ -514,7 +517,7 @@ mod tests { client.add_blocks(100, EachBlockWith::Uncle); client.insert_transaction_to_queue(); // Sync with no peers - let mut sync = ChainSync::new(SyncConfig::default(), &client, None); + let mut sync = ChainSync::new(SyncConfig::default(), &client, ForkFilterApi::new_dummy(&client), None); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); let mut io = TestIo::new(&mut client, &ss, &queue, None, None); @@ -584,7 +587,7 @@ mod tests { let mut client = TestBlockChainClient::new(); client.insert_transaction_with_gas_price_to_queue(U256::zero()); let block_hash = client.block_hash_delta_minus(1); - let mut sync = ChainSync::new(SyncConfig::default(), &client, None); + let mut sync = ChainSync::new(SyncConfig::default(), &client, ForkFilterApi::new_dummy(&client), None); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); let mut io = TestIo::new(&mut client, &ss, &queue, None, None); @@ -614,7 +617,7 @@ mod tests { let tx1_hash = client.insert_transaction_to_queue(); let tx2_hash = client.insert_transaction_with_gas_price_to_queue(U256::zero()); let block_hash = client.block_hash_delta_minus(1); - let mut sync = ChainSync::new(SyncConfig::default(), &client, None); + let mut sync = ChainSync::new(SyncConfig::default(), &client, ForkFilterApi::new_dummy(&client), None); let queue = RwLock::new(VecDeque::new()); let ss = TestSnapshotService::new(); let mut io = TestIo::new(&mut client, &ss, &queue, None, None); diff --git a/ethcore/sync/src/sync_io.rs b/ethcore/sync/src/sync_io.rs index b92e0cd2960..f3fcfa1eec1 100644 --- a/ethcore/sync/src/sync_io.rs +++ b/ethcore/sync/src/sync_io.rs @@ -54,8 +54,6 @@ pub trait SyncIo { fn peer_enode(&self, peer_id: PeerId) -> Option; /// Returns information on p2p session fn peer_session_info(&self, peer_id: PeerId) -> Option; - /// Maximum mutually supported ETH protocol version - fn eth_protocol_version(&self, peer_id: PeerId) -> u8; /// Maximum mutually supported version of a gien protocol. fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8; /// Returns if the chain block queue empty @@ -141,10 +139,6 @@ impl<'s> SyncIo for NetSyncIo<'s> { self.network.session_info(peer_id) } - fn eth_protocol_version(&self, peer_id: PeerId) -> u8 { - self.network.protocol_version(self.network.subprotocol_name(), peer_id).unwrap_or(0) - } - fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 { self.network.protocol_version(*protocol, peer_id).unwrap_or(0) } diff --git a/ethcore/sync/src/tests/helpers.rs b/ethcore/sync/src/tests/helpers.rs index 02839f6877d..f6afa3e0ad6 100644 --- a/ethcore/sync/src/tests/helpers.rs +++ b/ethcore/sync/src/tests/helpers.rs @@ -20,11 +20,12 @@ use std::sync::Arc; use crate::{ api::{SyncConfig, WARP_SYNC_PROTOCOL_ID}, chain::{ + fork_filter::ForkFilterApi, sync_packet::{ PacketInfo, SyncPacket::{self, PrivateTransactionPacket, SignedPrivateTransactionPacket} }, - ChainSync, SyncSupplier, ETH_PROTOCOL_VERSION_63, PAR_PROTOCOL_VERSION_4 + ChainSync, SyncSupplier, ETH_PROTOCOL_VERSION_64, PAR_PROTOCOL_VERSION_4 }, private_tx::SimplePrivateTxHandler, sync_io::SyncIo, @@ -155,12 +156,8 @@ impl<'p, C> SyncIo for TestIo<'p, C> where C: FlushingBlockChainClient, C: 'p { None } - fn eth_protocol_version(&self, _peer: PeerId) -> u8 { - ETH_PROTOCOL_VERSION_63.0 - } - - fn protocol_version(&self, protocol: &ProtocolId, peer_id: PeerId) -> u8 { - if protocol == &WARP_SYNC_PROTOCOL_ID { PAR_PROTOCOL_VERSION_4.0 } else { self.eth_protocol_version(peer_id) } + fn protocol_version(&self, protocol: &ProtocolId, _peer_id: PeerId) -> u8 { + if protocol == &WARP_SYNC_PROTOCOL_ID { PAR_PROTOCOL_VERSION_4.0 } else { ETH_PROTOCOL_VERSION_64.0 } } fn is_expired(&self) -> bool { @@ -384,7 +381,7 @@ impl TestNet> { let chain = TestBlockChainClient::new(); let ss = Arc::new(TestSnapshotService::new()); let private_tx_handler = Arc::new(SimplePrivateTxHandler::default()); - let sync = ChainSync::new(config.clone(), &chain, Some(private_tx_handler.clone())); + let sync = ChainSync::new(config.clone(), &chain, ForkFilterApi::new_dummy(&chain), Some(private_tx_handler.clone())); net.peers.push(Arc::new(EthPeer { sync: RwLock::new(sync), snapshot_service: ss, @@ -435,10 +432,11 @@ impl TestNet> { miner.clone(), channel.clone() ).unwrap(); + let fork_filter = ForkFilterApi::new(&*client, spec.hard_forks.clone()); let private_tx_handler = Arc::new(SimplePrivateTxHandler::default()); let ss = Arc::new(TestSnapshotService::new()); - let sync = ChainSync::new(config, &*client, Some(private_tx_handler.clone())); + let sync = ChainSync::new(config, &*client, fork_filter, Some(private_tx_handler.clone())); let peer = Arc::new(EthPeer { sync: RwLock::new(sync), snapshot_service: ss, diff --git a/ethcore/sync/src/transactions_stats.rs b/ethcore/sync/src/transactions_stats.rs index 443f5bfaba6..125d3c1b586 100644 --- a/ethcore/sync/src/transactions_stats.rs +++ b/ethcore/sync/src/transactions_stats.rs @@ -93,7 +93,6 @@ impl TransactionsStats { mod tests { use std::collections::{HashMap, HashSet}; use super::{Stats, TransactionsStats, NodeId, H256}; - use macros::hash_map; #[test] fn should_keep_track_of_propagations() { @@ -112,10 +111,12 @@ mod tests { let stats = stats.get(&hash); assert_eq!(stats, Some(&Stats { first_seen: 5, - propagated_to: hash_map![ - enodeid1 => 2, - enodeid2 => 1 - ], + propagated_to: { + let mut map = HashMap::new(); + map.insert(enodeid1, 2); + map.insert(enodeid2, 1); + map + }, })); } diff --git a/ethcore/trace/Cargo.toml b/ethcore/trace/Cargo.toml index 66d0c7b1b90..a4b85ced689 100644 --- a/ethcore/trace/Cargo.toml +++ b/ethcore/trace/Cargo.toml @@ -17,7 +17,7 @@ parity-bytes = "0.1.0" parity-util-mem = "0.5.1" parking_lot = "0.10.0" rlp = "0.4.0" -rlp_derive = { path = "../../util/rlp-derive" } +rlp-derive = "0.1" vm = { path = "../vm" } [dev-dependencies] diff --git a/ethcore/trace/src/config.rs b/ethcore/trace/src/config.rs index 59630369749..5073a88afbe 100644 --- a/ethcore/trace/src/config.rs +++ b/ethcore/trace/src/config.rs @@ -22,9 +22,9 @@ pub struct Config { /// Indicates if tracing should be enabled or not. /// If it's None, it will be automatically configured. pub enabled: bool, - /// Preferred cache-size. + /// Preferred cache-size (default: 15Mb). pub pref_cache_size: usize, - /// Max cache-size. + /// Max cache-size (default: 20Mb). pub max_cache_size: usize, } diff --git a/ethcore/trace/src/db.rs b/ethcore/trace/src/db.rs index 4de60272ce8..ac887ebbe55 100644 --- a/ethcore/trace/src/db.rs +++ b/ethcore/trace/src/db.rs @@ -239,13 +239,15 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { let enacted_blooms: Vec<_> = request.enacted .iter() // all traces are expected to be found here. That's why `expect` has been used - // instead of `filter_map`. If some traces haven't been found, it meens that + // instead of `filter_map`. If some traces haven't been found, it means that // traces database is corrupted or incomplete. - .map(|block_hash| if block_hash == &request.block_hash { - request.traces.bloom() - } else { - self.traces(block_hash).expect("Traces database is incomplete.").bloom() - }) + .map(|block_hash| + if block_hash == &request.block_hash { + request.traces.bloom() + } else { + self.traces(block_hash).expect("Traces database is incomplete.").bloom() + } + ) .collect(); self.db.trace_blooms() @@ -298,18 +300,19 @@ impl TraceDatabase for TraceDB where T: DatabaseExtras { let tx_hash = self.extras.transaction_hash(block_number, tx_position) .expect("Expected to find transaction hash. Database is probably corrupted"); - traces.into_iter() - .map(|trace| LocalizedTrace { - action: trace.action, - result: trace.result, - subtraces: trace.subtraces, - trace_address: trace.trace_address.into_iter().collect(), - transaction_number: Some(tx_position), - transaction_hash: Some(tx_hash.clone()), - block_number, - block_hash, - }) - .collect() + traces + .into_iter() + .map(|trace| LocalizedTrace { + action: trace.action, + result: trace.result, + subtraces: trace.subtraces, + trace_address: trace.trace_address.into_iter().collect(), + transaction_number: Some(tx_position), + transaction_hash: Some(tx_hash.clone()), + block_number, + block_hash, + }) + .collect() }) ) } diff --git a/ethcore/types/Cargo.toml b/ethcore/types/Cargo.toml index 2011c81cfbf..5916d80ea3d 100644 --- a/ethcore/types/Cargo.toml +++ b/ethcore/types/Cargo.toml @@ -12,12 +12,12 @@ ethereum-types = "0.8.0" ethjson = { path = "../../json" } keccak-hash = "0.4.0" parity-bytes = "0.1" -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } parity-util-mem = "0.5.1" parity-snappy = "0.1" patricia-trie-ethereum = { path = "../../util/patricia-trie-ethereum" } rlp = "0.4.0" -rlp_derive = { path = "../../util/rlp-derive" } +rlp-derive = "0.1" unexpected = { path = "../../util/unexpected" } vm = { path = "../vm"} diff --git a/ethcore/types/src/engines/mod.rs b/ethcore/types/src/engines/mod.rs index 0aca91c777b..c1ce9807825 100644 --- a/ethcore/types/src/engines/mod.rs +++ b/ethcore/types/src/engines/mod.rs @@ -18,11 +18,13 @@ use ethereum_types::{Address, H256, H64}; use bytes::Bytes; -use ethjson; use rlp::Rlp; use unexpected::Mismatch; -use crate::{BlockNumber, errors::{BlockError, EthcoreError}}; +use crate::{ + BlockNumber, + errors::{BlockError, EthcoreError} +}; pub mod epoch; pub mod params; @@ -56,21 +58,18 @@ impl EthashSeal { /// Tries to parse rlp encoded bytes as an Ethash/Clique seal. pub fn parse_seal>(seal: &[T]) -> Result { if seal.len() != 2 { - return Err(BlockError::InvalidSealArity( - Mismatch { - expected: 2, - found: seal.len() - } - ).into()); + Err(EthcoreError::Block(BlockError::InvalidSealArity(Mismatch { + expected: 2, + found: seal.len() + }))) + } else { + let mix_hash = Rlp::new(seal[0].as_ref()).as_val::()?; + let nonce = Rlp::new(seal[1].as_ref()).as_val::()?; + Ok(EthashSeal { mix_hash, nonce }) } - - let mix_hash = Rlp::new(seal[0].as_ref()).as_val::()?; - let nonce = Rlp::new(seal[1].as_ref()).as_val::()?; - Ok(EthashSeal { mix_hash, nonce }) } } - /// Seal type. #[derive(Debug, PartialEq, Eq)] pub enum Seal { @@ -96,7 +95,7 @@ pub const MAX_UNCLE_AGE: u64 = 6; /// Default EIP-210 contract code. /// As defined in https://github.com/ethereum/EIPs/pull/210 -pub const DEFAULT_BLOCKHASH_CONTRACT: &'static [u8] = &[ +pub const DEFAULT_BLOCKHASH_CONTRACT: &[u8] = &[ 0x73, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x33, 0x14, 0x15, 0x61, 0x00, 0x6a, 0x57, 0x60, 0x01, 0x43, 0x03, 0x60, 0x00, 0x35, 0x61, 0x01, 0x00, 0x82, 0x07, 0x55, 0x61, 0x01, 0x00, 0x81, 0x07, 0x15, 0x15, diff --git a/ethcore/types/src/engines/params.rs b/ethcore/types/src/engines/params.rs index 9750c87a610..f936bfb6a6d 100644 --- a/ethcore/types/src/engines/params.rs +++ b/ethcore/types/src/engines/params.rs @@ -188,6 +188,7 @@ impl CommonParams { schedule.tx_data_non_zero_gas = 16; } if block_number >= self.eip2200_advance_transition { + schedule.sload_gas = 800; schedule.sstore_dirty_gas = Some(800); } if block_number >= self.eip210_transition { diff --git a/ethcore/types/src/transaction/transaction.rs b/ethcore/types/src/transaction/transaction.rs index 0fe4bb67aa4..4e6de575488 100644 --- a/ethcore/types/src/transaction/transaction.rs +++ b/ethcore/types/src/transaction/transaction.rs @@ -316,11 +316,6 @@ impl UnverifiedTransaction { self } - /// Checks if the signature is empty. - pub fn is_unsigned(&self) -> bool { - self.r.is_zero() && self.s.is_zero() - } - /// Returns transaction receiver, if any pub fn receiver(&self) -> Option
{ match self.unsigned.action { @@ -357,7 +352,6 @@ impl UnverifiedTransaction { /// The chain ID, or `None` if this is a global transaction. pub fn chain_id(&self) -> Option { match self.v { - v if self.is_unsigned() => Some(v), v if v >= 35 => Some((v - 35) / 2), _ => None, } @@ -391,9 +385,6 @@ impl UnverifiedTransaction { /// Verify basic signature params. Does not attempt sender recovery. pub fn verify_basic(&self, check_low_s: bool, chain_id: Option) -> Result<(), error::Error> { - if self.is_unsigned() { - return Err(parity_crypto::publickey::Error::InvalidSignature.into()); - } if check_low_s { self.check_low_s()?; } @@ -439,9 +430,6 @@ impl From for UnverifiedTransaction { impl SignedTransaction { /// Try to verify transaction and recover sender. pub fn new(transaction: UnverifiedTransaction) -> Result { - if transaction.is_unsigned() { - return Err(parity_crypto::publickey::Error::InvalidSignature); - } let public = transaction.recover_public()?; let sender = public_to_address(&public); Ok(SignedTransaction { @@ -461,11 +449,6 @@ impl SignedTransaction { self.public } - /// Checks is signature is empty. - pub fn is_unsigned(&self) -> bool { - self.transaction.is_unsigned() - } - /// Deconstructs this transaction back into `UnverifiedTransaction` pub fn deconstruct(self) -> (UnverifiedTransaction, Address, Option) { (self.transaction, self.sender, self.public) @@ -494,9 +477,6 @@ impl LocalizedTransaction { if let Some(sender) = self.cached_sender { return sender; } - if self.is_unsigned() { - return UNSIGNED_SENDER.clone(); - } let sender = public_to_address(&self.recover_public() .expect("LocalizedTransaction is always constructed from transaction from blockchain; Blockchain only stores verified transactions; qed")); self.cached_sender = Some(sender); @@ -589,7 +569,7 @@ mod tests { fn signing_eip155_zero_chainid() { use parity_crypto::publickey::{Random, Generator}; - let key = Random.generate().unwrap(); + let key = Random.generate(); let t = Transaction { action: Action::Create, nonce: U256::from(42), @@ -610,7 +590,7 @@ mod tests { fn signing() { use parity_crypto::publickey::{Random, Generator}; - let key = Random.generate().unwrap(); + let key = Random.generate(); let t = Transaction { action: Action::Create, nonce: U256::from(42), @@ -662,7 +642,7 @@ mod tests { #[test] fn should_recover_from_chain_specific_signing() { use parity_crypto::publickey::{Random, Generator}; - let key = Random.generate().unwrap(); + let key = Random.generate(); let t = Transaction { action: Action::Create, nonce: U256::from(42), diff --git a/ethcore/verification/Cargo.toml b/ethcore/verification/Cargo.toml index 3be22a377a0..568afbc1728 100644 --- a/ethcore/verification/Cargo.toml +++ b/ethcore/verification/Cargo.toml @@ -32,9 +32,10 @@ triehash = { package = "triehash-ethereum", version = "0.2", path = "../../util unexpected = { path = "../../util/unexpected" } [dev-dependencies] +common-types = { path = "../types", features = ["test-helpers"] } criterion = "0.3" ethcore = { path = "../", features = ["test-helpers"] } -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } machine = { path = "../machine" } null-engine = { path = "../engines/null-engine" } spec = { path = "../spec" } diff --git a/ethcore/verification/benches/verification.rs b/ethcore/verification/benches/verification.rs index 23bcf4c6853..81477cc48c0 100644 --- a/ethcore/verification/benches/verification.rs +++ b/ethcore/verification/benches/verification.rs @@ -122,34 +122,20 @@ fn block_verification(c: &mut Criterion) { let preverified = verification::verify_block_unordered(block, ðash, true).expect(PROOF); let parent = Unverified::from_rlp(rlp_8481475.clone()).expect(PROOF); - // "partial" means we skip uncle and tx verification - c.bench_function("verify_block_family (partial)", |b| { - b.iter(|| { - if let Err(e) = verification::verify_block_family::( - &preverified.header, - &parent.header, - ðash, - None - ) { - panic!("verify_block_family (partial) ERROR: {:?}", e); - } - }); - }); - let mut block_provider = TestBlockChain::new(); block_provider.insert(rlp_8481476.clone()); // block to verify block_provider.insert(rlp_8481475.clone()); // parent block_provider.insert(rlp_8481474.clone()); // uncle's parent let client = TestBlockChainClient::default(); - c.bench_function("verify_block_family (full)", |b| { + c.bench_function("verify_block_family", |b| { b.iter(|| { let full = FullFamilyParams { block: &preverified, block_provider: &block_provider, client: &client }; if let Err(e) = verification::verify_block_family::( &preverified.header, &parent.header, ðash, - Some(full), + full, ) { panic!("verify_block_family (full) ERROR: {:?}", e) } diff --git a/ethcore/verification/src/verification.rs b/ethcore/verification/src/verification.rs index 6c21004d83b..b67cc127b23 100644 --- a/ethcore/verification/src/verification.rs +++ b/ethcore/verification/src/verification.rs @@ -163,8 +163,8 @@ fn verify_uncles(block: &PreverifiedBlock, bc: &dyn BlockProvider, engine: &dyn let mut excluded = HashSet::new(); excluded.insert(header.hash()); - let mut hash = header.parent_hash().clone(); - excluded.insert(hash.clone()); + let mut hash = *header.parent_hash(); + excluded.insert(hash); for _ in 0..MAX_UNCLE_AGE { match bc.block_details(&hash) { Some(details) => { @@ -213,7 +213,7 @@ fn verify_uncles(block: &PreverifiedBlock, bc: &dyn BlockProvider, engine: &dyn // cB.p^6 -----------/ 6 // cB.p^7 -------------/ // cB.p^8 - let mut expected_uncle_parent = header.parent_hash().clone(); + let mut expected_uncle_parent = *header.parent_hash(); let uncle_parent = bc.block_header_data(&uncle.parent_hash()) .ok_or_else(|| BlockError::UnknownUncleParent(*uncle.parent_hash()))?; for _ in 0..depth { @@ -440,7 +440,6 @@ mod tests { use keccak_hash::keccak; use engine::Engine; use parity_crypto::publickey::{Random, Generator}; - use spec; use ethcore::test_helpers::{ create_test_block_with_data, create_test_block, TestBlockChainClient }; @@ -449,7 +448,6 @@ mod tests { errors::BlockError::*, transaction::{SignedTransaction, Transaction, UnverifiedTransaction, Action}, }; - use rlp; use triehash::ordered_trie_root; use machine::Machine; use null_engine::NullEngine; @@ -556,7 +554,7 @@ mod tests { good.set_timestamp(40); good.set_number(10); - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let tr1 = Transaction { action: Action::Create, @@ -650,10 +648,6 @@ mod tests { let mut bad_header = good.clone(); bad_header.set_transactions_root(eip86_transactions_root.clone()); bad_header.set_uncles_hash(good_uncles_hash.clone()); - match basic_test(&create_test_block_with_data(&bad_header, &eip86_transactions, &good_uncles), engine) { - Err(Error::Transaction(ref e)) if e == &parity_crypto::publickey::Error::InvalidSignature.into() => (), - e => panic!("Block verification failed.\nExpected: Transaction Error (Invalid Signature)\nGot: {:?}", e), - } let mut header = good.clone(); header.set_transactions_root(good_transactions_root.clone()); @@ -763,7 +757,7 @@ mod tests { let mut header = Header::default(); header.set_number(1); - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let bad_transactions: Vec<_> = (0..3).map(|i| Transaction { action: Action::Create, value: U256::zero(), diff --git a/ethcore/vm/src/schedule.rs b/ethcore/vm/src/schedule.rs index 58d4b815257..a11e3ed326c 100644 --- a/ethcore/vm/src/schedule.rs +++ b/ethcore/vm/src/schedule.rs @@ -136,8 +136,6 @@ pub struct Schedule { pub eip1283: bool, /// Enable EIP-1706 rules pub eip1706: bool, - /// VM execution does not increase null signed address nonce if this field is true. - pub keep_unsigned_nonce: bool, /// Latest VM version for contract creation transaction. pub latest_version: U256, /// All supported non-legacy VM versions. @@ -279,7 +277,6 @@ impl Schedule { kill_dust: CleanDustMode::Off, eip1283: false, eip1706: false, - keep_unsigned_nonce: false, latest_version: U256::zero(), versions: HashMap::new(), wasm: None, @@ -371,7 +368,6 @@ impl Schedule { kill_dust: CleanDustMode::Off, eip1283: false, eip1706: false, - keep_unsigned_nonce: false, latest_version: U256::zero(), versions: HashMap::new(), wasm: None, diff --git a/ethcore/wasm/run/Cargo.toml b/ethcore/wasm/run/Cargo.toml index 31a43746c3f..4d32950e4b3 100644 --- a/ethcore/wasm/run/Cargo.toml +++ b/ethcore/wasm/run/Cargo.toml @@ -14,7 +14,7 @@ vm = { path = "../../vm" } wasm = { path = "../" } clap = "2.24" env_logger = "0.5" -rustc-hex = "1.0" +rustc-hex = "2.1.0" [features] default = ["ethereum-types/std"] diff --git a/ethcore/wasm/run/src/runner.rs b/ethcore/wasm/run/src/runner.rs index fe80415dd89..07b7254b707 100644 --- a/ethcore/wasm/run/src/runner.rs +++ b/ethcore/wasm/run/src/runner.rs @@ -73,9 +73,9 @@ impl fmt::Display for Fail { write!( f, "Expected to return result: 0x{} ({} bytes), but got 0x{} ({} bytes)", - expected.to_hex(), + expected.to_hex::(), expected.len(), - actual.to_hex(), + actual.to_hex::(), actual.len() ), @@ -95,17 +95,17 @@ impl fmt::Display for Fail { write!( f, "Storage key {} value mismatch, expected {}, got: {}", - key.as_bytes().to_vec().to_hex(), - expected.as_bytes().to_vec().to_hex(), - actual.as_bytes().to_vec().to_hex(), + key.as_bytes().to_hex::(), + expected.as_bytes().to_hex::(), + actual.as_bytes().to_hex::(), ), StorageMismatch { ref key, ref expected, actual: None} => write!( f, "No expected storage value for key {} found, expected {}", - key.as_bytes().to_vec().to_hex(), - expected.as_bytes().to_vec().to_hex(), + key.as_bytes().to_hex::(), + expected.as_bytes().to_hex::(), ), Nonconformity(SpecNonconformity::Address) => diff --git a/evmbin/Cargo.toml b/evmbin/Cargo.toml index 92ada4ef64a..a4566640ec3 100644 --- a/evmbin/Cargo.toml +++ b/evmbin/Cargo.toml @@ -21,7 +21,7 @@ evm = { path = "../ethcore/evm" } panic_hook = { path = "../util/panic-hook" } parity-bytes = "0.1" pod = { path = "../ethcore/pod" } -rustc-hex = "1.0" +rustc-hex = "2.1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" spec = { path = "../ethcore/spec" } @@ -30,6 +30,7 @@ vm = { path = "../ethcore/vm" } [dev-dependencies] criterion = "0.3" +hex-literal = "0.2.1" tempdir = "0.3" [features] diff --git a/evmbin/benches/mod.rs b/evmbin/benches/mod.rs index 593abcd6b48..743df1e8253 100644 --- a/evmbin/benches/mod.rs +++ b/evmbin/benches/mod.rs @@ -22,10 +22,13 @@ #[macro_use] extern crate criterion; + +#[macro_use] +extern crate hex_literal; + extern crate ethcore; extern crate evm; extern crate ethereum_types; -extern crate rustc_hex; extern crate vm; use std::sync::Arc; @@ -33,7 +36,6 @@ use criterion::{Criterion, black_box}; use ethereum_types::U256; use evm::Factory; -use rustc_hex::FromHex; use vm::tests::FakeExt; use vm::{ActionParams, Ext}; @@ -56,7 +58,7 @@ fn bench_simple_loop_u256(c: &mut Criterion) { fn simple_loop(gas: U256, c: &mut Criterion, bench_id: &str) { let code = black_box( - "606060405260005b620042408112156019575b6001016007565b600081905550600680602b6000396000f3606060405200".from_hex().unwrap() + hex!("606060405260005b620042408112156019575b6001016007565b600081905550600680602b6000396000f3606060405200").to_vec() ); c.bench_function(bench_id, move |b| { @@ -82,7 +84,7 @@ fn bench_rng_u256(c: &mut Criterion) { fn rng(gas: U256, c: &mut Criterion, bench_id: &str) { let code = black_box( - "6060604052600360056007600b60005b62004240811215607f5767ffe7649d5eca84179490940267f47ed85c4b9a6379019367f8e5dd9a5c994bba9390930267f91d87e4b8b74e55019267ff97f6f3b29cda529290920267f393ada8dd75c938019167fe8d437c45bb3735830267f47d9a7b5428ffec019150600101600f565b838518831882186000555050505050600680609a6000396000f3606060405200".from_hex().unwrap() + hex!("6060604052600360056007600b60005b62004240811215607f5767ffe7649d5eca84179490940267f47ed85c4b9a6379019367f8e5dd9a5c994bba9390930267f91d87e4b8b74e55019267ff97f6f3b29cda529290920267f393ada8dd75c938019167fe8d437c45bb3735830267f47d9a7b5428ffec019150600101600f565b838518831882186000555050505050600680609a6000396000f3606060405200").to_vec() ); c.bench_function(bench_id, move |b| { diff --git a/json/Cargo.toml b/json/Cargo.toml index 5c7ee10727f..0975632890c 100644 --- a/json/Cargo.toml +++ b/json/Cargo.toml @@ -7,12 +7,12 @@ edition = "2018" [dependencies] ethereum-types = "0.8.0" -rustc-hex = "1.0" +rustc-hex = "2.1.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" [dev-dependencies] -macros = { path = "../util/macros" } +maplit = "1.0.2" [features] test-helpers = [] diff --git a/json/src/spec/builtin.rs b/json/src/spec/builtin.rs index ad9a8fb159b..4b95258ddc6 100644 --- a/json/src/spec/builtin.rs +++ b/json/src/spec/builtin.rs @@ -139,8 +139,8 @@ pub struct PricingAt { #[cfg(test)] mod tests { - use super::{Builtin, BuiltinCompat, BTreeMap, Pricing, PricingAt, Linear, Modexp, AltBn128ConstOperations}; - use macros::map; + use super::{Builtin, BuiltinCompat, Pricing, PricingAt, Linear, Modexp, AltBn128ConstOperations}; + use maplit::btreemap; #[test] fn builtin_deserialization() { @@ -150,7 +150,7 @@ mod tests { }"#; let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); assert_eq!(builtin.name, "ecrecover"); - assert_eq!(builtin.pricing, map![ + assert_eq!(builtin.pricing, btreemap![ 0 => PricingAt { info: None, price: Pricing::Linear(Linear { base: 3000, word: 0 }) @@ -174,7 +174,7 @@ mod tests { }"#; let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); assert_eq!(builtin.name, "ecrecover"); - assert_eq!(builtin.pricing, map![ + assert_eq!(builtin.pricing, btreemap![ 0 => PricingAt { info: None, price: Pricing::Linear(Linear { base: 3000, word: 0 }) @@ -195,7 +195,7 @@ mod tests { }"#; let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); assert_eq!(builtin.name, "blake2_f"); - assert_eq!(builtin.pricing, map![ + assert_eq!(builtin.pricing, btreemap![ 0xffffff => PricingAt { info: None, price: Pricing::Blake2F { gas_per_round: 123 } @@ -215,10 +215,10 @@ mod tests { }"#; let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); assert_eq!(builtin.name, "alt_bn128_mul"); - assert_eq!(builtin.pricing, map![ + assert_eq!(builtin.pricing, btreemap![ 100500 => PricingAt { info: None, - price: Pricing::AltBn128ConstOperations(AltBn128ConstOperations { + price: Pricing::AltBn128ConstOperations(AltBn128ConstOperations { price: 123, }), } @@ -235,7 +235,7 @@ mod tests { let builtin: Builtin = serde_json::from_str::(s).unwrap().into(); assert_eq!(builtin.name, "late_start"); - assert_eq!(builtin.pricing, map![ + assert_eq!(builtin.pricing, btreemap![ 100_000 => PricingAt { info: None, price: Pricing::Modexp(Modexp { divisor: 5 }) diff --git a/json/src/vm.rs b/json/src/vm.rs index d6150b0d958..048fe58f6d0 100644 --- a/json/src/vm.rs +++ b/json/src/vm.rs @@ -121,15 +121,12 @@ pub struct Env { #[cfg(test)] mod tests { - use std::{ - collections::BTreeMap, - str::FromStr - }; + use std::str::FromStr; use super::{Address, Bytes, Call, Env, H256, MaybeEmpty, State, Transaction, Uint, Vm}; use crate::spec::{Account, HashOrMap}; use ethereum_types::{U256, H160 as Hash160, H256 as Hash256}; - use macros::map; + use maplit::btreemap; use rustc_hex::FromHex; const TEST_CODE: &str = "7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff01600055"; @@ -207,14 +204,14 @@ mod tests { assert_eq!(vm.output, Some(Bytes::new(Vec::new()))); assert_eq!(vm.pre_state, State( HashOrMap::Map( - map![ + btreemap![ Address(Hash160::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap()) => Account { builtin: None, balance: Some(Uint(0x0de0b6b3a7640000_u64.into())), code: Some(Bytes::new(TEST_CODE.from_hex().unwrap())), constructor: None, nonce: Some(Uint(0.into())), - storage: Some(map![]), + storage: Some(btreemap![]), version: None, } ])) @@ -222,14 +219,14 @@ mod tests { assert_eq!(vm.post_state, Some( State( HashOrMap::Map( - map![ + btreemap![ Address(Hash160::from_str("0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6").unwrap()) => Account { builtin: None, balance: Some(Uint(0x0de0b6b3a7640000_u64.into())), code: Some(Bytes::new(TEST_CODE.from_hex().unwrap())), constructor: None, nonce: Some(Uint(0.into())), - storage: Some(map![ + storage: Some(btreemap![ Uint(0.into()) => Uint(U256::from_str("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe").unwrap()) ]), version: None, diff --git a/miner/Cargo.toml b/miner/Cargo.toml index ea619412d34..91edeb1d712 100644 --- a/miner/Cargo.toml +++ b/miner/Cargo.toml @@ -26,7 +26,7 @@ parity-util-mem = "0.5.1" keccak-hash = "0.4.0" linked-hash-map = "0.5" log = "0.4" -parity-runtime = { path = "../util/runtime" } +parity-runtime = "0.1.1" parking_lot = "0.10.0" price-info = { path = "./price-info", optional = true } registrar = { path = "../util/registrar" } @@ -39,8 +39,8 @@ transaction-pool = "2.0.1" [dev-dependencies] env_logger = "0.5" -parity-crypto = { version = "0.4.2", features = ["publickey"] } -rustc-hex = "1.0" +parity-crypto = { version = "0.5.0", features = ["publickey"] } +rustc-hex = "2.1.0" [features] work-notify = ["ethash", "fetch", "hyper", "url"] diff --git a/miner/local-store/Cargo.toml b/miner/local-store/Cargo.toml index 49680cee66d..6b4591034b4 100644 --- a/miner/local-store/Cargo.toml +++ b/miner/local-store/Cargo.toml @@ -17,5 +17,5 @@ serde_json = "1.0" [dev-dependencies] ethkey = { path = "../../accounts/ethkey" } -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } kvdb-memorydb = "0.4.0" diff --git a/miner/local-store/src/lib.rs b/miner/local-store/src/lib.rs index b9f6e792954..ec2c757f53b 100644 --- a/miner/local-store/src/lib.rs +++ b/miner/local-store/src/lib.rs @@ -229,7 +229,7 @@ mod tests { #[test] fn with_condition() { - let keypair = Brain::new("abcd".into()).generate().unwrap(); + let keypair = Brain::new("abcd".into()).generate(); let transactions: Vec<_> = (0..10u64).map(|nonce| { let mut tx = Transaction::default(); tx.nonce = nonce.into(); @@ -264,7 +264,7 @@ mod tests { #[test] fn skips_bad_transactions() { - let keypair = Brain::new("abcd".into()).generate().unwrap(); + let keypair = Brain::new("abcd".into()).generate(); let mut transactions: Vec<_> = (0..10u64).map(|nonce| { let mut tx = Transaction::default(); tx.nonce = nonce.into(); diff --git a/miner/price-info/Cargo.toml b/miner/price-info/Cargo.toml index 23f69e37c20..fa3cd8aeec6 100644 --- a/miner/price-info/Cargo.toml +++ b/miner/price-info/Cargo.toml @@ -11,8 +11,9 @@ edition = "2018" fetch = { path = "../../util/fetch" } futures = "0.1" log = "0.4" -parity-runtime = { path = "../../util/runtime" } +parity-runtime = "0.1.1" serde_json = "1.0" [dev-dependencies] fake-fetch = { path = "../../util/fake-fetch" } +parity-runtime = { version = "0.1.1", features = ["test-helpers"] } diff --git a/miner/src/pool/local_transactions.rs b/miner/src/pool/local_transactions.rs index 811023f2e1c..b90517864e8 100644 --- a/miner/src/pool/local_transactions.rs +++ b/miner/src/pool/local_transactions.rs @@ -304,7 +304,7 @@ mod tests { } fn new_tx>(nonce: T) -> Arc { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let signed = transaction::Transaction { action: transaction::Action::Create, value: U256::from(100), diff --git a/miner/src/pool/replace.rs b/miner/src/pool/replace.rs index 853f228b753..99513817b0c 100644 --- a/miner/src/pool/replace.rs +++ b/miner/src/pool/replace.rs @@ -148,7 +148,7 @@ mod tests { let replace = ReplaceByScoreAndReadiness::new(scoring, client); // same sender txs - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let same_sender_tx1 = local_tx_verified(Tx { nonce: 1, @@ -169,14 +169,14 @@ mod tests { }, &keypair); // different sender txs - let sender1 = Random.generate().unwrap(); + let sender1 = Random.generate(); let different_sender_tx1 = local_tx_verified(Tx { nonce: 2, gas_price: 1, ..Default::default() }, &sender1); - let sender2 = Random.generate().unwrap(); + let sender2 = Random.generate(); let different_sender_tx2 = local_tx_verified(Tx { nonce: 1, gas_price: 10, @@ -221,7 +221,7 @@ mod tests { ..Default::default() }; - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let txs = vec![tx1, tx2, tx3, tx4].into_iter().map(|tx| { tx.unsigned().sign(keypair.secret(), None).verified() }).collect::>(); @@ -322,7 +322,7 @@ mod tests { let client = TestClient::new().with_nonce(1); let replace = ReplaceByScoreAndReadiness::new(scoring, client); - let old_sender = Random.generate().unwrap(); + let old_sender = Random.generate(); let tx_old_ready_1 = { let tx = Tx { nonce: 1, @@ -387,7 +387,7 @@ mod tests { tx.signed().verified() }; - let new_sender = Random.generate().unwrap(); + let new_sender = Random.generate(); let tx_new_ready_1 = { let tx = Tx { nonce: 1, @@ -443,7 +443,7 @@ mod tests { tx.signed().verified() }; - let new_sender = Random.generate().unwrap(); + let new_sender = Random.generate(); let tx_new_ready_1 = local_tx_verified(Tx { nonce: 1, gas_price: 1, @@ -485,7 +485,7 @@ mod tests { tx.signed().verified() }; - let new_sender = Random.generate().unwrap(); + let new_sender = Random.generate(); let tx_new_ready_1 = local_tx_verified(Tx { nonce: 1, gas_price: 2, diff --git a/miner/src/pool/tests/tx.rs b/miner/src/pool/tests/tx.rs index 4425aafd9c7..631b4cfac4a 100644 --- a/miner/src/pool/tests/tx.rs +++ b/miner/src/pool/tests/tx.rs @@ -47,7 +47,7 @@ impl Tx { } pub fn signed(self) -> SignedTransaction { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); self.unsigned().sign(keypair.secret(), None) } @@ -57,7 +57,7 @@ impl Tx { } pub fn signed_triple(mut self) -> (SignedTransaction, SignedTransaction, SignedTransaction) { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let tx1 = self.clone().unsigned().sign(keypair.secret(), None); self.nonce += 1; let tx2 = self.clone().unsigned().sign(keypair.secret(), None); @@ -68,7 +68,7 @@ impl Tx { } pub fn signed_replacement(mut self) -> (SignedTransaction, SignedTransaction) { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let tx1 = self.clone().unsigned().sign(keypair.secret(), None); self.gas_price += 1; let tx2 = self.unsigned().sign(keypair.secret(), None); @@ -88,7 +88,7 @@ impl Tx { } pub fn big_one(self) -> SignedTransaction { - let keypair = Random.generate().unwrap(); + let keypair = Random.generate(); let tx = Transaction { action: transaction::Action::Create, value: U256::from(100), diff --git a/miner/using-queue/src/lib.rs b/miner/using-queue/src/lib.rs index 59347a6a503..b4cff65bbc5 100644 --- a/miner/using-queue/src/lib.rs +++ b/miner/using-queue/src/lib.rs @@ -49,7 +49,7 @@ impl UsingQueue { /// Return a reference to the item at the top of the queue (or `None` if the queue is empty); /// it doesn't constitute noting that the item is used. pub fn peek_last_ref(&self) -> Option<&T> { - self.pending.as_ref().or(self.in_use.last()) + self.pending.as_ref().or_else(|| self.in_use.last()) } /// Return a reference to the item at the top of the queue (or `None` if the queue is empty); @@ -72,7 +72,7 @@ impl UsingQueue { } /// Is there anything in the queue currently? - pub fn is_in_use(&self) -> bool { self.in_use.len() > 0 } + pub fn is_in_use(&self) -> bool { !self.in_use.is_empty() } /// Clears everything; the queue is entirely reset. pub fn reset(&mut self) { @@ -113,7 +113,7 @@ impl UsingQueue { None } } else { - self.in_use.last().into_iter().filter(|x| predicate(x)).next().cloned() + self.in_use.last().into_iter().find(|x| predicate(x)).cloned() } } } diff --git a/parity/account_utils.rs b/parity/account_utils.rs index 9fc748a43a7..1261ab2de29 100644 --- a/parity/account_utils.rs +++ b/parity/account_utils.rs @@ -61,7 +61,7 @@ mod accounts { mod accounts { use super::*; use upgrade::upgrade_key_location; - use ethereum_types::H160; + use ethereum_types::{H160, H256}; use std::str::FromStr; pub use accounts::AccountProvider; @@ -132,9 +132,17 @@ mod accounts { LocalAccounts(account_provider) } - pub fn miner_author(spec: &SpecType, dirs: &Directories, account_provider: &Arc, engine_signer: Address, passwords: &[Password]) -> Result, String> { + pub fn miner_author( + spec: &SpecType, + dirs: &Directories, + account_provider: &Arc, + engine_signer: Address, + passwords: &[Password] + ) -> Result, String> { use engine::signer::EngineSigner; + const SECP_TEST_MESSAGE: H256 = H256([1_u8; 32]); + // Check if engine signer exists if !account_provider.has_account(engine_signer) { return Err(format!("Consensus signer account not found for the current chain. {}", build_create_account_hint(spec, &dirs.keys))); @@ -145,25 +153,27 @@ mod accounts { return Err(format!("No password found for the consensus signer {}. {}", engine_signer, VERIFY_PASSWORD_HINT)); } - let mut author = None; - for password in passwords { + let mut invalid_reasons = std::collections::HashSet::new(); + for (idx, password) in passwords.iter().enumerate() { let signer = parity_rpc::signer::EngineSigner::new( account_provider.clone(), engine_signer, password.clone(), ); - if signer.sign(Default::default()).is_ok() { - author = Some(::ethcore::miner::Author::Sealer(Box::new(signer))); + if let Err(e) = signer.sign(SECP_TEST_MESSAGE) { + debug!(target: "account", "Signing test of `EngineSigner ({})` with password index: {} failed because of: {:?}", engine_signer, idx, e); + invalid_reasons.insert(e.to_string()); + } else { + return Ok(Some(ethcore::miner::Author::Sealer(Box::new(signer)))); } } - if author.is_none() { - return Err(format!("No valid password for the consensus signer {}. {}", engine_signer, VERIFY_PASSWORD_HINT)); - } - Ok(author) + Err(format!( + "No valid password found for EngineSigner {}, the following errors were found during testing: {:?}. {}", + engine_signer, invalid_reasons, VERIFY_PASSWORD_HINT + )) } - mod private_tx { use super::*; use parity_crypto::publickey::{Signature, Message}; diff --git a/parity/cache.rs b/parity/cache.rs index 5b1ee048c23..e91efd6632e 100644 --- a/parity/cache.rs +++ b/parity/cache.rs @@ -52,7 +52,9 @@ impl Default for CacheConfig { } impl CacheConfig { - /// Creates new cache config with cumulative size equal `total`. + /// Creates new cache config with cumulative size equal to `total`, distributed as follows: 70% + /// to rocksdb, 10% to the blockchain cache and 20% to the state cache. The transaction queue + /// cache size is set to 40Mb and the trace cache to 20Mb. pub fn new_with_total_cache_size(total: u32) -> Self { CacheConfig { db: total * 7 / 10, @@ -63,14 +65,14 @@ impl CacheConfig { } } - /// Creates new cache config with gitven details. + /// Creates new cache config with given details. pub fn new(db: u32, blockchain: u32, queue: u32, state: u32) -> Self { CacheConfig { - db: db, - blockchain: blockchain, - queue: queue, + db, + blockchain, + queue, traces: DEFAULT_TRACE_CACHE_SIZE, - state: state, + state, } } diff --git a/parity/lib.rs b/parity/lib.rs index c3876fa9bee..c2e78a5b354 100644 --- a/parity/lib.rs +++ b/parity/lib.rs @@ -84,7 +84,7 @@ extern crate log as rlog; extern crate ethcore_accounts as accounts; #[cfg(feature = "secretstore")] -extern crate ethcore_secretstore; +extern crate parity_secretstore; #[cfg(feature = "secretstore")] extern crate ethabi; @@ -133,18 +133,11 @@ use configuration::{Cmd, Execute}; use deprecated::find_deprecated; use hash::keccak_buffer; -#[cfg(feature = "memory_profiling")] -use std::alloc::System; - pub use self::configuration::Configuration; pub use self::run::RunningClient; pub use parity_rpc::PubSubSession; pub use ethcore_logger::{Config as LoggerConfig, setup_log, RotatingLogger}; -#[cfg(feature = "memory_profiling")] -#[global_allocator] -static A: System = System; - fn print_hash_of(maybe_file: Option) -> Result { if let Some(file) = maybe_file { let mut f = BufReader::new(File::open(&file).map_err(|_| "Unable to open file".to_owned())?); diff --git a/parity/modules.rs b/parity/modules.rs index 264f79202d1..efd663e7d46 100644 --- a/parity/modules.rs +++ b/parity/modules.rs @@ -14,9 +14,10 @@ // You should have received a copy of the GNU General Public License // along with Parity Ethereum. If not, see . -use std::sync::{Arc, mpsc}; +use std::{collections::BTreeSet, sync::{Arc, mpsc}}; use client_traits::{BlockChainClient, ChainNotify}; +use types::BlockNumber; use sync::{self, SyncConfig, NetworkConfiguration, Params, ConnectionFilter}; use snapshot::SnapshotService; use ethcore_private_tx::PrivateStateDB; @@ -38,6 +39,7 @@ pub fn sync( executor: Executor, network_config: NetworkConfiguration, chain: Arc, + forks: BTreeSet, snapshot_service: Arc, private_tx_handler: Option>, private_state: Option>, @@ -49,6 +51,7 @@ pub fn sync( config, executor, chain, + forks, provider, snapshot_service, private_tx_handler, diff --git a/parity/run.rs b/parity/run.rs index b8157872ed9..ac2f339a023 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -490,6 +490,7 @@ fn execute_impl( let fetch = fetch::Client::new(FETCH_FULL_NUM_DNS_THREADS).map_err(|e| format!("Error starting fetch client: {:?}", e))?; let txpool_size = cmd.miner_options.pool_limits.max_count; + // create miner let miner = Arc::new(Miner::new( cmd.miner_options, @@ -500,6 +501,7 @@ fn execute_impl( account_utils::miner_local_accounts(account_provider.clone()), ) )); + miner.set_author(miner::Author::External(cmd.miner_extras.author)); miner.set_gas_range_target(cmd.miner_extras.gas_range_target); miner.set_extra_data(cmd.miner_extras.extra_data); @@ -571,6 +573,7 @@ fn execute_impl( cmd.private_encryptor_conf, ).map_err(|e| format!("Client service error: {:?}", e))?; + let forks = spec.hard_forks.clone(); let connection_filter_address = spec.params().node_permission_contract; // drop the spec to free up genesis state. drop(spec); @@ -646,6 +649,7 @@ fn execute_impl( runtime.executor(), net_conf.clone().into(), client.clone(), + forks, snapshot_service.clone(), private_tx_sync, private_state, @@ -897,9 +901,7 @@ impl RunningClient { trace!(target: "shutdown", "Informant dropped"); drop(client); trace!(target: "shutdown", "Client dropped"); - // This may help when debugging ref cycles. Requires nightly-only `#![feature(weak_counts)]` - // trace!(target: "shutdown", "Waiting for refs to Client to shutdown, strong_count={:?}, weak_count={:?}", weak_client.strong_count(), weak_client.weak_count()); - trace!(target: "shutdown", "Waiting for refs to Client to shutdown"); + trace!(target: "shutdown", "Waiting for refs to Client to shutdown, strong_count={:?}, weak_count={:?}", weak_client.strong_count(), weak_client.weak_count()); wait_for_drop(weak_client); } } @@ -957,11 +959,7 @@ fn wait_for_drop(w: Weak) { thread::sleep(SLEEP_DURATION); - // When debugging shutdown issues on a nightly build it can help to enable this with the - // `#![feature(weak_counts)]` added to lib.rs (TODO: enable when - // https://github.com/rust-lang/rust/issues/57977 is stable) - // trace!(target: "shutdown", "Waiting for client to drop, strong_count={:?}, weak_count={:?}", w.strong_count(), w.weak_count()); - trace!(target: "shutdown", "Waiting for client to drop"); + trace!(target: "shutdown", "Waiting for client to drop, strong_count={:?}, weak_count={:?}", w.strong_count(), w.weak_count()); } warn!("Shutdown timeout reached, exiting uncleanly."); diff --git a/parity/secretstore/blockchain.rs b/parity/secretstore/blockchain.rs index 4d628de1974..18187b513e8 100644 --- a/parity/secretstore/blockchain.rs +++ b/parity/secretstore/blockchain.rs @@ -37,7 +37,7 @@ use ethcore::miner::{Miner, MinerService}; use parity_crypto::publickey::Error as EthKeyError; use sync::SyncProvider; use registrar::RegistrarClient; -use ethcore_secretstore::{BlockId, BlockNumber, SecretStoreChain, NewBlocksNotify, SigningKeyPair, ContractAddress, Filter}; +use parity_secretstore::{BlockId, BlockNumber, SecretStoreChain, NewBlocksNotify, SigningKeyPair, ContractAddress, Filter}; // TODO: Instead of a constant, make this based on consensus finality. /// Number of confirmations required before request can be processed. diff --git a/parity/secretstore/nodekeypair.rs b/parity/secretstore/nodekeypair.rs index ef1b3cce6fc..932e56b59dc 100644 --- a/parity/secretstore/nodekeypair.rs +++ b/parity/secretstore/nodekeypair.rs @@ -22,7 +22,7 @@ use ethkey::Password; use parity_crypto::publickey::public_to_address; use ethereum_types::{H256, Address, Public}; use parity_crypto::publickey::{Signature, Error as EthKeyError}; -use ethcore_secretstore::SigningKeyPair; +use parity_secretstore::SigningKeyPair; pub struct KeyStoreNodeKeyPair { account_provider: Arc, diff --git a/parity/secretstore/server.rs b/parity/secretstore/server.rs index 450a9d78769..7f624a1e87b 100644 --- a/parity/secretstore/server.rs +++ b/parity/secretstore/server.rs @@ -123,7 +123,7 @@ mod server { #[cfg(feature = "secretstore")] mod server { use std::sync::Arc; - use ethcore_secretstore; + use parity_secretstore; use parity_crypto::publickey::KeyPair; use ansi_term::Colour::{Red, White}; use super::{Configuration, Dependencies, NodeSecretKey, ContractAddress, Executor}; @@ -131,23 +131,23 @@ mod server { #[cfg(feature = "accounts")] use super::super::KeyStoreNodeKeyPair; - fn into_service_contract_address(address: ContractAddress) -> ethcore_secretstore::ContractAddress { + fn into_service_contract_address(address: ContractAddress) -> parity_secretstore::ContractAddress { match address { - ContractAddress::Registry => ethcore_secretstore::ContractAddress::Registry, - ContractAddress::Address(address) => ethcore_secretstore::ContractAddress::Address(address), + ContractAddress::Registry => parity_secretstore::ContractAddress::Registry, + ContractAddress::Address(address) => parity_secretstore::ContractAddress::Address(address), } } /// Key server pub struct KeyServer { - _key_server: Box, + _key_server: Box, } impl KeyServer { /// Create new key server pub fn new(mut conf: Configuration, deps: Dependencies, executor: Executor) -> Result { - let self_secret: Arc = match conf.self_secret.take() { - Some(NodeSecretKey::Plain(secret)) => Arc::new(ethcore_secretstore::PlainNodeKeyPair::new( + let self_secret: Arc = match conf.self_secret.take() { + Some(NodeSecretKey::Plain(secret)) => Arc::new(parity_secretstore::PlainNodeKeyPair::new( KeyPair::from_secret(secret).map_err(|e| format!("invalid secret: {}", e))?)), #[cfg(feature = "accounts")] Some(NodeSecretKey::KeyStore(account)) => { @@ -177,8 +177,8 @@ mod server { } let key_server_name = format!("{}:{}", conf.interface, conf.port); - let mut cconf = ethcore_secretstore::ServiceConfiguration { - listener_address: if conf.http_enabled { Some(ethcore_secretstore::NodeAddress { + let mut cconf = parity_secretstore::ServiceConfiguration { + listener_address: if conf.http_enabled { Some(parity_secretstore::NodeAddress { address: conf.http_interface.clone(), port: conf.http_port, }) } else { None }, @@ -188,12 +188,12 @@ mod server { service_contract_doc_store_address: conf.service_contract_doc_store_address.map(into_service_contract_address), service_contract_doc_sretr_address: conf.service_contract_doc_sretr_address.map(into_service_contract_address), acl_check_contract_address: conf.acl_check_contract_address.map(into_service_contract_address), - cluster_config: ethcore_secretstore::ClusterConfiguration { - listener_address: ethcore_secretstore::NodeAddress { + cluster_config: parity_secretstore::ClusterConfiguration { + listener_address: parity_secretstore::NodeAddress { address: conf.interface.clone(), port: conf.port, }, - nodes: conf.nodes.into_iter().map(|(p, (ip, port))| (p, ethcore_secretstore::NodeAddress { + nodes: conf.nodes.into_iter().map(|(p, (ip, port))| (p, parity_secretstore::NodeAddress { address: ip, port: port, })).collect(), @@ -207,9 +207,9 @@ mod server { cconf.cluster_config.nodes.insert(self_secret.public().clone(), cconf.cluster_config.listener_address.clone()); - let db = ethcore_secretstore::open_secretstore_db(&conf.data_path)?; + let db = parity_secretstore::open_secretstore_db(&conf.data_path)?; let trusted_client = TrustedClient::new(self_secret.clone(), deps.client, deps.sync, deps.miner); - let key_server = ethcore_secretstore::start(trusted_client, self_secret, cconf, db, executor) + let key_server = parity_secretstore::start(trusted_client, self_secret, cconf, db, executor) .map_err(|e| format!("Error starting KeyServer {}: {}", key_server_name, e))?; Ok(KeyServer { diff --git a/rpc/Cargo.toml b/rpc/Cargo.toml index 52397f46406..dcf0bc702eb 100644 --- a/rpc/Cargo.toml +++ b/rpc/Cargo.toml @@ -16,7 +16,7 @@ multihash = "0.8" order-stat = "0.1" rand = "0.7" rand_xorshift = "0.2" -rustc-hex = "1.0" +rustc-hex = "2.1.0" semver = "0.9" serde = "1.0" serde_derive = "1.0" @@ -51,7 +51,7 @@ ethereum-types = "0.8.0" fastmap = { path = "../util/fastmap" } machine = { path = "../ethcore/machine" } parity-bytes = "0.1" -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } eip-712 = { path = "../util/EIP-712" } ethjson = { path = "../json" } @@ -59,7 +59,7 @@ ethkey = { path = "../accounts/ethkey" } ethstore = { path = "../accounts/ethstore" } fetch = { path = "../util/fetch" } keccak-hash = "0.4.0" -parity-runtime = { path = "../util/runtime" } +parity-runtime = { version = "0.1.1", features = ["test-helpers"] } parity-updater = { path = "../updater" } parity-version = { path = "../util/version" } rlp = "0.4.0" @@ -77,7 +77,7 @@ ethcore-io = { path = "../util/io" } ethcore-network = { path = "../util/network" } ethjson = { path = "../json", features = ["test-helpers"] } fake-fetch = { path = "../util/fake-fetch" } -macros = { path = "../util/macros" } +maplit = "1.0.2" spec = { path = "../ethcore/spec" } pretty_assertions = "0.1" transaction-pool = "2.0.1" diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index 45911ad6748..126d229261a 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -120,7 +120,7 @@ extern crate pretty_assertions; #[cfg(test)] #[macro_use] -extern crate macros; +extern crate maplit; #[cfg(test)] extern crate fake_fetch; diff --git a/rpc/src/v1/helpers/engine_signer.rs b/rpc/src/v1/helpers/engine_signer.rs index 7602673fbf7..032fc97de13 100644 --- a/rpc/src/v1/helpers/engine_signer.rs +++ b/rpc/src/v1/helpers/engine_signer.rs @@ -36,16 +36,14 @@ impl EngineSigner { impl engine::signer::EngineSigner for EngineSigner { fn sign(&self, message: Message) -> Result { - match self.accounts.sign(self.address, Some(self.password.clone()), message) { - Ok(ok) => Ok(ok), - Err(_) => Err(Error::InvalidSecretKey), - } + self.accounts.sign(self.address, Some(self.password.clone()), message).map_err(|e| { + Error::Custom(e.to_string()) + }) } fn decrypt(&self, auth_data: &[u8], cipher: &[u8]) -> Result, Error> { self.accounts.decrypt(self.address, None, auth_data, cipher).map_err(|e| { - warn!("Unable to decrypt message: {:?}", e); - Error::InvalidMessage + Error::Custom(e.to_string()) }) } diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 6651a67312e..5fcf45ec891 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -513,7 +513,7 @@ pub fn vm(error: &VMError, output: &[u8]) -> Error { use rustc_hex::ToHex; let data = match error { - &VMError::Reverted => format!("{} 0x{}", VMError::Reverted, output.to_hex()), + &VMError::Reverted => format!("{} 0x{}", VMError::Reverted, output.to_hex::()), error => format!("{}", error), }; diff --git a/rpc/src/v1/helpers/secretstore.rs b/rpc/src/v1/helpers/secretstore.rs index d13e015469e..e4de0a9056d 100644 --- a/rpc/src/v1/helpers/secretstore.rs +++ b/rpc/src/v1/helpers/secretstore.rs @@ -31,7 +31,7 @@ const INIT_VEC_LEN: usize = 16; /// Generate document key to store in secret store. pub fn generate_document_key(account_public: Public, server_key_public: Public) -> Result { // generate random plain document key - let document_key = Random.generate().map_err(errors::encryption)?; + let document_key = Random.generate(); // encrypt document key using server key let (common_point, encrypted_point) = encrypt_secret(document_key.public(), &server_key_public)?; @@ -141,8 +141,7 @@ fn encrypt_secret(secret: &Public, joint_public: &Public) -> Result<(Public, Pub // TODO: it is copypaste of `encrypt_secret` from secret_store/src/key_server_cluster/math.rs // use shared version from SS math library, when it'll be available - let key_pair = Random.generate() - .map_err(errors::encryption)?; + let key_pair = Random.generate(); // k * T let mut common_point = ec_math_utils::generation_point(); diff --git a/rpc/src/v1/helpers/signature.rs b/rpc/src/v1/helpers/signature.rs index a271d8e4a8e..31a66e168c5 100644 --- a/rpc/src/v1/helpers/signature.rs +++ b/rpc/src/v1/helpers/signature.rs @@ -71,7 +71,7 @@ mod tests { /// mocked signer fn sign(should_prefix: bool, data: Vec, signing_chain_id: Option) -> (H160, [u8; 32], [u8; 32], U64) { let hash = if should_prefix { eth_data_hash(data) } else { keccak(data) }; - let account = Random.generate().unwrap(); + let account = Random.generate(); let address = account.address(); let sig = crypto::publickey::sign(account.secret(), &hash).unwrap(); let (r, s, v) = (sig.r(), sig.s(), sig.v()); diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index 9a01a7a3b59..b478ae746f5 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -197,7 +197,7 @@ where } fn phrase_to_address(&self, phrase: String) -> Result { - Ok(Brain::new(phrase).generate().expect("Brain::generate always returns Ok; qed").address()) + Ok(Brain::new(phrase).generate().address()) } fn list_accounts(&self, _: u64, _: Option, _: Option) -> Result>> { diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index acf28b092dd..fe45defc4a6 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -205,7 +205,7 @@ impl Parity for ParityClient where } fn phrase_to_address(&self, phrase: String) -> Result { - Ok(Brain::new(phrase).generate().expect("Brain::generate always returns Ok; qed").address()) + Ok(Brain::new(phrase).generate().address()) } fn list_accounts(&self, count: u64, after: Option, block_number: Option) -> Result>> { diff --git a/rpc/src/v1/impls/parity_accounts.rs b/rpc/src/v1/impls/parity_accounts.rs index e1e0f33ac7b..7ed042d0e0e 100644 --- a/rpc/src/v1/impls/parity_accounts.rs +++ b/rpc/src/v1/impls/parity_accounts.rs @@ -118,7 +118,7 @@ impl ParityAccounts for ParityAccountsClient { fn new_account_from_phrase(&self, phrase: String, pass: Password) -> Result { self.deprecation_notice("parity_newAccountFromPhrase"); - let brain = Brain::new(phrase).generate().unwrap(); + let brain = Brain::new(phrase).generate(); self.accounts.insert_account(brain.secret().clone(), &pass) .map(Into::into) .map_err(|e| errors::account("Could not create account.", e)) diff --git a/rpc/src/v1/tests/helpers/sync_provider.rs b/rpc/src/v1/tests/helpers/sync_provider.rs index 81e50c3b5ba..a7984d2ae07 100644 --- a/rpc/src/v1/tests/helpers/sync_provider.rs +++ b/rpc/src/v1/tests/helpers/sync_provider.rs @@ -46,7 +46,7 @@ impl TestSyncProvider { status: RwLock::new(SyncStatus { state: SyncState::Idle, network_id: config.network_id, - protocol_version: 63, + protocol_version: 64, start_block_number: 0, last_imported_block_number: None, highest_block_number: None, @@ -82,11 +82,11 @@ impl SyncProvider for TestSyncProvider { PeerInfo { id: Some("node1".to_owned()), client_version: ClientVersion::from("Parity-Ethereum/1/v2.4.0/linux/rustc"), - capabilities: vec!["eth/62".to_owned(), "eth/63".to_owned()], + capabilities: vec!["eth/63".to_owned(), "eth/64".to_owned()], remote_address: "127.0.0.1:7777".to_owned(), local_address: "127.0.0.1:8888".to_owned(), eth_info: Some(EthProtocolInfo { - version: 62, + version: 63, difficulty: Some(40.into()), head: H256::from_low_u64_be(50), }), @@ -95,11 +95,11 @@ impl SyncProvider for TestSyncProvider { PeerInfo { id: None, client_version: ClientVersion::from("Parity-Ethereum/2/v2.4.0/linux/rustc"), - capabilities: vec!["eth/63".to_owned(), "eth/64".to_owned()], + capabilities: vec!["eth/64".to_owned(), "eth/65".to_owned()], remote_address: "Handshake".to_owned(), local_address: "127.0.0.1:3333".to_owned(), eth_info: Some(EthProtocolInfo { - version: 64, + version: 65, difficulty: None, head: H256::from_low_u64_be(60), }), @@ -113,16 +113,16 @@ impl SyncProvider for TestSyncProvider { } fn transactions_stats(&self) -> BTreeMap { - map![ + btreemap![ H256::from_low_u64_be(1) => TransactionStats { first_seen: 10, - propagated_to: map![ + propagated_to: btreemap![ H512::from_low_u64_be(128) => 16 ], }, H256::from_low_u64_be(5) => TransactionStats { first_seen: 16, - propagated_to: map![ + propagated_to: btreemap![ H512::from_low_u64_be(16) => 1 ], } diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 82821901941..5e8fe52a8c9 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -125,7 +125,7 @@ impl EthTester { #[test] fn rpc_eth_protocol_version() { let request = r#"{"jsonrpc": "2.0", "method": "eth_protocolVersion", "params": [], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":"63","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":"64","id":1}"#; assert_eq!(EthTester::default().io.handle_request_sync(request), Some(response.to_owned())); } @@ -564,12 +564,11 @@ fn rpc_eth_transaction_count_by_number_pending() { #[test] fn rpc_eth_pending_transaction_by_hash() { use ethereum_types::H256; - use rlp; use types::transaction::SignedTransaction; let tester = EthTester::default(); { - let bytes = FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); + let bytes: Vec = FromHex::from_hex("f85f800182520894095e7baea6a6c7c4c2dfeb977efac326af552d870a801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a0efffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804").unwrap(); let tx = rlp::decode(&bytes).expect("decoding failure"); let tx = SignedTransaction::new(tx).unwrap(); tester.miner.pending_transactions.lock().insert(H256::zero(), tx); @@ -913,7 +912,7 @@ fn rpc_eth_send_raw_transaction() { let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); let t = t.with_signature(signature, None); - let rlp = rlp::encode(&t).to_hex(); + let rlp: String = rlp::encode(&t).to_hex(); let req = r#"{ "jsonrpc": "2.0", diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index a9c023f68c3..0e906147da5 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -255,7 +255,7 @@ fn rpc_parity_net_peers() { let io = deps.default_client(); let request = r#"{"jsonrpc": "2.0", "method": "parity_netPeers", "params":[], "id": 1}"#; - let response = r#"{"jsonrpc":"2.0","result":{"active":0,"connected":120,"max":50,"peers":[{"caps":["eth/62","eth/63"],"id":"node1","name":{"ParityClient":{"can_handle_large_requests":true,"compiler":"rustc","identity":"1","name":"Parity-Ethereum","os":"linux","semver":"2.4.0"}},"network":{"localAddress":"127.0.0.1:8888","remoteAddress":"127.0.0.1:7777"},"protocols":{"eth":{"difficulty":"0x28","head":"0000000000000000000000000000000000000000000000000000000000000032","version":62},"pip":null}},{"caps":["eth/63","eth/64"],"id":null,"name":{"ParityClient":{"can_handle_large_requests":true,"compiler":"rustc","identity":"2","name":"Parity-Ethereum","os":"linux","semver":"2.4.0"}},"network":{"localAddress":"127.0.0.1:3333","remoteAddress":"Handshake"},"protocols":{"eth":{"difficulty":null,"head":"000000000000000000000000000000000000000000000000000000000000003c","version":64},"pip":null}}]},"id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"active":0,"connected":120,"max":50,"peers":[{"caps":["eth/63","eth/64"],"id":"node1","name":{"ParityClient":{"can_handle_large_requests":true,"compiler":"rustc","identity":"1","name":"Parity-Ethereum","os":"linux","semver":"2.4.0"}},"network":{"localAddress":"127.0.0.1:8888","remoteAddress":"127.0.0.1:7777"},"protocols":{"eth":{"difficulty":"0x28","head":"0000000000000000000000000000000000000000000000000000000000000032","version":63},"pip":null}},{"caps":["eth/64","eth/65"],"id":null,"name":{"ParityClient":{"can_handle_large_requests":true,"compiler":"rustc","identity":"2","name":"Parity-Ethereum","os":"linux","semver":"2.4.0"}},"network":{"localAddress":"127.0.0.1:3333","remoteAddress":"Handshake"},"protocols":{"eth":{"difficulty":null,"head":"000000000000000000000000000000000000000000000000000000000000003c","version":65},"pip":null}}]},"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } @@ -363,7 +363,7 @@ fn rpc_parity_pending_transactions_with_limit_with_filter() { fn rpc_parity_encrypt() { let deps = Dependencies::new(); let io = deps.default_client(); - let key = format!("{:x}", Random.generate().unwrap().public()); + let key = format!("{:x}", Random.generate().public()); let request = r#"{"jsonrpc": "2.0", "method": "parity_encryptMessage", "params":["0x"#.to_owned() + &key + r#"", "0x01"], "id": 1}"#; assert!(io.handle_request_sync(&request).unwrap().contains("result"), "Should return success."); diff --git a/rpc/src/v1/tests/mocked/parity_set.rs b/rpc/src/v1/tests/mocked/parity_set.rs index 249d2806fe7..6f11176cb87 100644 --- a/rpc/src/v1/tests/mocked/parity_set.rs +++ b/rpc/src/v1/tests/mocked/parity_set.rs @@ -166,7 +166,7 @@ fn rpc_parity_set_extra_data() { let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); - assert_eq!(miner.authoring_params().extra_data, "cd1722f3947def4cf144679da39c4c32bdc35681".from_hex().unwrap()); + assert_eq!(miner.authoring_params().extra_data, "cd1722f3947def4cf144679da39c4c32bdc35681".from_hex::>().unwrap()); } #[test] diff --git a/rpc/src/v1/tests/mocked/personal.rs b/rpc/src/v1/tests/mocked/personal.rs index 54a09c7a1b4..bf7138281d2 100644 --- a/rpc/src/v1/tests/mocked/personal.rs +++ b/rpc/src/v1/tests/mocked/personal.rs @@ -306,6 +306,7 @@ fn ec_recover_invalid_signature() { fn should_not_unlock_account_temporarily_if_allow_perm_is_disabled() { let tester = setup(); let address = tester.accounts.new_account(&"password123".into()).unwrap(); + let message = [1u8; 32].into(); let request = r#"{ "jsonrpc": "2.0", @@ -320,13 +321,14 @@ fn should_not_unlock_account_temporarily_if_allow_perm_is_disabled() { let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Time-unlocking is not supported when permanent unlock is disabled.","data":"Use personal_sendTransaction or enable permanent unlocking, instead."},"id":1}"#; assert_eq!(tester.io.handle_request_sync(&request), Some(response.into())); - assert!(tester.accounts.sign(address, None, Default::default()).is_err(), "Should not unlock account."); + assert!(tester.accounts.sign(address, None, message).is_err(), "Should not unlock account."); } #[test] fn should_unlock_account_permanently() { let tester = setup(); let address = tester.accounts.new_account(&"password123".into()).unwrap(); + let message = [1u8; 32].into(); let request = r#"{ "jsonrpc": "2.0", @@ -340,7 +342,7 @@ fn should_unlock_account_permanently() { }"#; let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#; assert_eq!(tester.io.handle_request_sync(&request), Some(response.into())); - assert!(tester.accounts.sign(address, None, Default::default()).is_ok(), "Should unlock account."); + assert!(tester.accounts.sign(address, None, message).is_ok(), "Should unlock account."); } #[test] @@ -366,8 +368,8 @@ fn sign_eip191_with_validator() { data: keccak("hello world").as_bytes().to_vec().into() }).unwrap(); let result = eip191::hash_message(EIP191Version::PresignedTransaction, with_validator).unwrap(); - let result = tester.accounts.sign(address, Some("password123".into()), result).unwrap().into_electrum(); - let expected = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{}", result.to_hex()) + r#"","id":1}"#; + let result: String = tester.accounts.sign(address, Some("password123".into()), result).unwrap().into_electrum().to_hex(); + let expected = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{}", result) + r#"","id":1}"#; let response = tester.io.handle_request_sync(&request).unwrap(); assert_eq!(response, expected) } diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index 99645f8e49f..5e9b9bd9fb8 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -87,13 +87,10 @@ fn eth_signing(signing_queue_enabled: bool) -> SigningTester { #[test] fn rpc_eth_sign() { - use rustc_hex::FromHex; - let tester = eth_signing(true); let account = tester.accounts.insert_account(Secret::from([69u8; 32]), &"abcd".into()).unwrap(); tester.accounts.unlock_account_permanently(account, "abcd".into()).unwrap(); - let _message = "0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f".from_hex().unwrap(); let req = r#"{ "jsonrpc": "2.0", @@ -496,7 +493,7 @@ fn should_decrypt_message_if_account_is_unlocked() { fn should_add_decryption_to_the_queue() { // given let tester = eth_signing(true); - let acc = Random.generate().unwrap(); + let acc = Random.generate(); assert_eq!(tester.signer.requests().len(), 0); // when @@ -533,7 +530,7 @@ fn should_add_decryption_to_the_queue() { fn should_compose_transaction() { // given let tester = eth_signing(true); - let acc = Random.generate().unwrap(); + let acc = Random.generate(); assert_eq!(tester.signer.requests().len(), 0); let from = format!("{:x}", acc.address()); diff --git a/rpc/src/v1/tests/mocked/signing_unsafe.rs b/rpc/src/v1/tests/mocked/signing_unsafe.rs index 11d5f605513..ffc7e9bfe80 100644 --- a/rpc/src/v1/tests/mocked/signing_unsafe.rs +++ b/rpc/src/v1/tests/mocked/signing_unsafe.rs @@ -165,10 +165,10 @@ fn rpc_eth_sign_transaction() { let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); let t = t.with_signature(signature, None); let signature = t.signature(); - let rlp = rlp::encode(&t); + let rlp: String = rlp::encode(&t).to_hex(); let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + - r#""raw":"0x"# + &rlp.to_hex() + r#"","# + + r#""raw":"0x"# + &rlp + r#"","# + r#""tx":{"# + r#""blockHash":null,"blockNumber":null,"# + &format!("\"chainId\":{},", t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))) + @@ -180,7 +180,7 @@ fn rpc_eth_sign_transaction() { r#""nonce":"0x1","# + &format!("\"publicKey\":\"0x{:x}\",", t.recover_public().unwrap()) + &format!("\"r\":\"0x{:x}\",", U256::from(signature.r())) + - &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + + &format!("\"raw\":\"0x{}\",", rlp) + &format!("\"s\":\"0x{:x}\",", U256::from(signature.s())) + &format!("\"standardV\":\"0x{:x}\",", U256::from(t.standard_v())) + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + diff --git a/rpc/src/v1/types/block.rs b/rpc/src/v1/types/block.rs index deb59b3d1d1..317a1a58240 100644 --- a/rpc/src/v1/types/block.rs +++ b/rpc/src/v1/types/block.rs @@ -206,10 +206,16 @@ impl Serialize for Rich { mod tests { use std::collections::BTreeMap; use ethereum_types::{H64, H160, H256, U256, Bloom as H2048}; - use serde_json; use v1::types::{Transaction, Bytes}; use super::{Block, RichBlock, BlockTransactions, Header, RichHeader}; + fn default_extra_info() -> BTreeMap { + btreemap![ + "mixHash".into() => format!("{:?}", H256::default()), + "nonce".into() => format!("{:?}", H64::default()) + ] + } + #[test] fn test_serialize_block_transactions() { let t = BlockTransactions::Full(vec![Transaction::default()]); @@ -248,10 +254,7 @@ mod tests { let serialized_block = serde_json::to_string(&block).unwrap(); let rich_block = RichBlock { inner: block, - extra_info: map![ - "mixHash".into() => format!("{:?}", H256::zero()), - "nonce".into() => format!("{:?}", H64::default()) - ], + extra_info: default_extra_info(), }; let serialized_rich_block = serde_json::to_string(&rich_block).unwrap(); @@ -286,10 +289,7 @@ mod tests { let serialized_block = serde_json::to_string(&block).unwrap(); let rich_block = RichBlock { inner: block, - extra_info: map![ - "mixHash".into() => format!("{:?}", H256::zero()), - "nonce".into() => format!("{:?}", H64::default()) - ], + extra_info: default_extra_info(), }; let serialized_rich_block = serde_json::to_string(&rich_block).unwrap(); @@ -321,10 +321,7 @@ mod tests { let serialized_header = serde_json::to_string(&header).unwrap(); let rich_header = RichHeader { inner: header, - extra_info: map![ - "mixHash".into() => format!("{:?}", H256::zero()), - "nonce".into() => format!("{:?}", H64::default()) - ], + extra_info: default_extra_info(), }; let serialized_rich_header = serde_json::to_string(&rich_header).unwrap(); diff --git a/rpc/src/v1/types/bytes.rs b/rpc/src/v1/types/bytes.rs index f0891e22a3f..a43c91e475f 100644 --- a/rpc/src/v1/types/bytes.rs +++ b/rpc/src/v1/types/bytes.rs @@ -50,9 +50,10 @@ impl Into> for Bytes { impl Serialize for Bytes { fn serialize(&self, serializer: S) -> Result - where S: Serializer { + where S: Serializer + { let mut serialized = "0x".to_owned(); - serialized.push_str(self.0.to_hex().as_ref()); + serialized.push_str(self.0.to_hex::().as_ref()); serializer.serialize_str(serialized.as_ref()) } } @@ -89,7 +90,6 @@ impl<'a> Visitor<'a> for BytesVisitor { #[cfg(test)] mod tests { use super::*; - use serde_json; use rustc_hex::FromHex; #[test] diff --git a/rpc/src/v1/types/call_request.rs b/rpc/src/v1/types/call_request.rs index 59658dc07e7..0d0829e1d60 100644 --- a/rpc/src/v1/types/call_request.rs +++ b/rpc/src/v1/types/call_request.rs @@ -103,7 +103,7 @@ mod tests { gas_price: Some(U256::from_str("9184e72a000").unwrap()), gas: Some(U256::from_str("76c0").unwrap()), value: Some(U256::from_str("9184e72a").unwrap()), - data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), + data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex::>().unwrap().into()), nonce: None }); } diff --git a/rpc/src/v1/types/sync.rs b/rpc/src/v1/types/sync.rs index f54303beb8a..4e9eb41e31c 100644 --- a/rpc/src/v1/types/sync.rs +++ b/rpc/src/v1/types/sync.rs @@ -195,8 +195,6 @@ pub struct ChainStatus { #[cfg(test)] mod tests { - use serde_json; - use std::collections::BTreeMap; use super::{SyncInfo, SyncStatus, Peers, TransactionStats, ChainStatus, H512}; #[test] @@ -240,9 +238,7 @@ mod tests { fn test_serialize_transaction_stats() { let stats = TransactionStats { first_seen: 100, - propagated_to: map![ - H512::from_low_u64_be(10) => 50 - ], + propagated_to: btreemap![H512::from_low_u64_be(10) => 50], }; let serialized = serde_json::to_string(&stats).unwrap(); diff --git a/rpc/src/v1/types/trace.rs b/rpc/src/v1/types/trace.rs index b59a07b6ef9..f993acf5b71 100644 --- a/rpc/src/v1/types/trace.rs +++ b/rpc/src/v1/types/trace.rs @@ -674,8 +674,6 @@ impl From<(H256, Executed)> for TraceResultsWithTransactionHash { #[cfg(test)] mod tests { - use serde_json; - use std::collections::BTreeMap; use v1::types::Bytes; use trace::TraceError; use ethereum_types::Address; @@ -875,12 +873,12 @@ mod tests { #[test] fn test_statediff_serialize() { - let t = StateDiff(map![ + let t = StateDiff(btreemap![ Address::from_low_u64_be(42) => AccountDiff { balance: Diff::Same, nonce: Diff::Born(1.into()), code: Diff::Same, - storage: map![ + storage: btreemap![ H256::from_low_u64_be(42) => Diff::Same ] }, @@ -888,7 +886,7 @@ mod tests { balance: Diff::Same, nonce: Diff::Changed(ChangedType { from: 1.into(), to: 0.into() }), code: Diff::Died(vec![96].into()), - storage: map![], + storage: btreemap![], } ]); let serialized = serde_json::to_string(&t).unwrap(); diff --git a/rpc/src/v1/types/transaction_request.rs b/rpc/src/v1/types/transaction_request.rs index b91d3eb2b71..81a7bc3c60b 100644 --- a/rpc/src/v1/types/transaction_request.rs +++ b/rpc/src/v1/types/transaction_request.rs @@ -186,7 +186,7 @@ mod tests { gas_price: Some(U256::from_str("9184e72a000").unwrap()), gas: Some(U256::from_str("76c0").unwrap()), value: Some(U256::from_str("9184e72a").unwrap()), - data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex().unwrap().into()), + data: Some("d46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675".from_hex::>().unwrap().into()), nonce: None, condition: None, }); diff --git a/secret-store/Cargo.toml b/secret-store/Cargo.toml deleted file mode 100644 index f12a8e0f9df..00000000000 --- a/secret-store/Cargo.toml +++ /dev/null @@ -1,41 +0,0 @@ -[package] -description = "Parity Ethereum (EthCore) Secret Store" -name = "ethcore-secretstore" -version = "1.0.0" -license = "GPL-3.0" -authors = ["Parity Technologies "] - -[dependencies] -byteorder = "1.0" -ethabi = "9.0.1" -ethabi-contract = "9.0.0" -ethabi-derive = "9.0.1" -ethereum-types = "0.8.0" -ethkey = { path = "../accounts/ethkey", optional = true } -futures = "0.1" -hyper = { version = "0.12", default-features = false } -keccak-hash = "0.4.0" -kvdb = "0.4.0" -kvdb-sled = "0.1.0" -lazy_static = "1.0" -log = "0.4" -parity-bytes = "0.1" -parity-crypto = { version = "0.4.2", features = ["publickey"] } -parity-runtime = { path = "../util/runtime" } -parking_lot = "0.10.0" -percent-encoding = "2.1.0" -rustc-hex = "1.0" -serde = "1.0" -serde_derive = "1.0" -serde_json = "1.0" -tiny-keccak = "1.4" -tokio = "0.1.22" -tokio-io = "0.1" -tokio-service = "0.1" -url = "2.1.0" -jsonrpc-server-utils = "14.0.3" - -[dev-dependencies] -env_logger = "0.5" -tempdir = "0.3" -kvdb-rocksdb = "0.5.0" diff --git a/secret-store/res/acl_storage.json b/secret-store/res/acl_storage.json deleted file mode 100644 index cfdefd9c735..00000000000 --- a/secret-store/res/acl_storage.json +++ /dev/null @@ -1,3 +0,0 @@ -[ - {"constant":true,"inputs":[{"name":"user","type":"address"},{"name":"document","type":"bytes32"}],"name":"checkPermissions","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"} -] diff --git a/secret-store/res/key_server_set.json b/secret-store/res/key_server_set.json deleted file mode 100644 index 28530e3532b..00000000000 --- a/secret-store/res/key_server_set.json +++ /dev/null @@ -1,24 +0,0 @@ -[ - {"constant":true,"inputs":[],"name":"getMigrationMaster","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getMigrationKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"id","type":"bytes32"}],"name":"startMigration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getCurrentKeyServerIndex","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getMigrationKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[],"name":"getMigrationId","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[],"name":"getNewKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"id","type":"bytes32"}],"name":"confirmMigration","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[],"name":"getMigrationKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"isMigrationConfirmed","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[],"name":"getCurrentKeyServersCount","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[],"name":"getCurrentKeyServers","outputs":[{"name":"","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[],"name":"getCurrentLastChange","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getCurrentKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getNewKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getCurrentKeyServerAddress","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"getNewKeyServerPublic","outputs":[{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"index","type":"uint8"}],"name":"getCurrentKeyServer","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"keyServer","type":"address"}],"name":"KeyServerAdded","type":"event"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"keyServer","type":"address"}],"name":"KeyServerRemoved","type":"event"}, - {"anonymous":false,"inputs":[],"name":"MigrationStarted","type":"event"}, - {"anonymous":false,"inputs":[],"name":"MigrationCompleted","type":"event"} -] \ No newline at end of file diff --git a/secret-store/res/service.json b/secret-store/res/service.json deleted file mode 100644 index d79c38e7aac..00000000000 --- a/secret-store/res/service.json +++ /dev/null @@ -1,33 +0,0 @@ -[ - {"constant":true,"inputs":[{"name":"keyServer","type":"address"}],"name":"requireKeyServer","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"}, - - {"constant":true,"inputs":[],"name":"serverKeyGenerationRequestsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"serverKeyGenerationError","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getServerKeyGenerationRequest","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"address"},{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"serverKeyPublic","type":"bytes"}],"name":"serverKeyGenerated","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"keyServer","type":"address"}],"name":"isServerKeyGenerationResponseRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"},{"indexed":false,"name":"author","type":"address"},{"indexed":false,"name":"threshold","type":"uint8"}],"name":"ServerKeyGenerationRequested","type":"event"}, - - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"serverKeyRetrievalError","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[],"name":"serverKeyRetrievalRequestsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"keyServer","type":"address"}],"name":"isServerKeyRetrievalResponseRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getServerKeyRetrievalRequest","outputs":[{"name":"","type":"bytes32"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"serverKeyPublic","type":"bytes"},{"name":"threshold","type":"uint8"}],"name":"serverKeyRetrieved","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"}],"name":"ServerKeyRetrievalRequested","type":"event"}, - - {"constant":true,"inputs":[],"name":"documentKeyStoreRequestsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"documentKeyStoreError","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"}],"name":"documentKeyStored","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"keyServer","type":"address"}],"name":"isDocumentKeyStoreResponseRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getDocumentKeyStoreRequest","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"address"},{"name":"","type":"bytes"},{"name":"","type":"bytes"}],"payable":false,"stateMutability":"view","type":"function"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"},{"indexed":false,"name":"author","type":"address"},{"indexed":false,"name":"commonPoint","type":"bytes"},{"indexed":false,"name":"encryptedPoint","type":"bytes"}],"name":"DocumentKeyStoreRequested","type":"event"}, - - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"requester","type":"address"},{"name":"commonPoint","type":"bytes"},{"name":"threshold","type":"uint8"}],"name":"documentKeyCommonRetrieved","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"keyServer","type":"address"},{"name":"requester","type":"address"}],"name":"isDocumentKeyShadowRetrievalResponseRequired","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"requester","type":"address"},{"name":"participants","type":"uint256"},{"name":"decryptedSecret","type":"bytes"},{"name":"shadow","type":"bytes"}],"name":"documentKeyPersonalRetrieved","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":false,"inputs":[{"name":"serverKeyId","type":"bytes32"},{"name":"requester","type":"address"}],"name":"documentKeyShadowRetrievalError","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}, - {"constant":true,"inputs":[],"name":"documentKeyShadowRetrievalRequestsCount","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}, - {"constant":true,"inputs":[{"name":"index","type":"uint256"}],"name":"getDocumentKeyShadowRetrievalRequest","outputs":[{"name":"","type":"bytes32"},{"name":"","type":"bytes"},{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"},{"indexed":false,"name":"requester","type":"address"}],"name":"DocumentKeyCommonRetrievalRequested","type":"event"}, - {"anonymous":false,"inputs":[{"indexed":false,"name":"serverKeyId","type":"bytes32"},{"indexed":false,"name":"requesterPublic","type":"bytes"}],"name":"DocumentKeyPersonalRetrievalRequested","type":"event"} -] \ No newline at end of file diff --git a/secret-store/src/acl_storage.rs b/secret-store/src/acl_storage.rs deleted file mode 100644 index 2c9dbf2d203..00000000000 --- a/secret-store/src/acl_storage.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{HashMap, HashSet}; -use parking_lot::{Mutex, RwLock}; -use ethereum_types::Address; -use ethabi::FunctionOutputDecoder; -use blockchain::{SecretStoreChain, NewBlocksNotify, ContractAddress, BlockId}; -use types::{Error, ServerKeyId}; - -use_contract!(acl_storage, "res/acl_storage.json"); - -const ACL_CHECKER_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_acl_checker"; - -/// ACL storage of Secret Store -pub trait AclStorage: Send + Sync { - /// Check if requester can access document with hash `document` - fn check(&self, requester: Address, document: &ServerKeyId) -> Result; -} - -/// On-chain ACL storage implementation. -pub struct OnChainAclStorage { - /// Cached on-chain contract. - contract: Mutex, -} - -/// Cached on-chain ACL storage contract. -struct CachedContract { - /// Blockchain client. - client: Arc, - /// Contract address source. - address_source: ContractAddress, - /// Current contract address. - contract_address: Option
, -} - -/// Dummy ACL storage implementation (check always passed). -#[derive(Default, Debug)] -pub struct DummyAclStorage { - prohibited: RwLock>>, -} - -impl OnChainAclStorage { - pub fn new(trusted_client: Arc, address_source: ContractAddress) -> Result, Error> { - let acl_storage = Arc::new(OnChainAclStorage { - contract: Mutex::new(CachedContract::new(trusted_client.clone(), address_source)), - }); - trusted_client.add_listener(acl_storage.clone()); - Ok(acl_storage) - } -} - -impl AclStorage for OnChainAclStorage { - fn check(&self, requester: Address, document: &ServerKeyId) -> Result { - self.contract.lock().check(requester, document) - } -} - -impl NewBlocksNotify for OnChainAclStorage { - fn new_blocks(&self, _new_enacted_len: usize) { - self.contract.lock().update_contract_address() - } -} - -impl CachedContract { - pub fn new(client: Arc, address_source: ContractAddress) -> Self { - let mut contract = CachedContract { - client, - address_source, - contract_address: None, - }; - contract.update_contract_address(); - contract - } - - pub fn update_contract_address(&mut self) { - let contract_address = self.client.read_contract_address( - ACL_CHECKER_CONTRACT_REGISTRY_NAME, - &self.address_source - ); - if contract_address != self.contract_address { - trace!(target: "secretstore", "Configuring for ACL checker contract from address {:?}", - contract_address); - - self.contract_address = contract_address; - } - } - - pub fn check(&mut self, requester: Address, document: &ServerKeyId) -> Result { - if self.client.is_trusted() { - // call contract to check accesss - match self.contract_address { - Some(contract_address) => { - let (encoded, decoder) = acl_storage::functions::check_permissions::call(requester, document.clone()); - let d = self.client.call_contract(BlockId::Latest, contract_address, encoded) - .map_err(|e| Error::Internal(format!("ACL checker call error: {}", e.to_string())))?; - decoder.decode(&d) - .map_err(|e| Error::Internal(format!("ACL checker call error: {}", e.to_string()))) - }, - None => Err(Error::Internal("ACL checker contract is not configured".to_owned())), - } - } else { - Err(Error::Internal("Calling ACL contract without trusted blockchain client".into())) - } - } -} - -impl DummyAclStorage { - /// Prohibit given requester access to given documents - #[cfg(test)] - pub fn prohibit(&self, requester: Address, document: ServerKeyId) { - self.prohibited.write() - .entry(requester) - .or_insert_with(Default::default) - .insert(document); - } -} - -impl AclStorage for DummyAclStorage { - fn check(&self, requester: Address, document: &ServerKeyId) -> Result { - Ok(self.prohibited.read() - .get(&requester) - .map(|docs| !docs.contains(document)) - .unwrap_or(true)) - } -} diff --git a/secret-store/src/blockchain.rs b/secret-store/src/blockchain.rs deleted file mode 100644 index bed3eb1a772..00000000000 --- a/secret-store/src/blockchain.rs +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use bytes::Bytes; -use ethereum_types::{H256, Address, Public}; -use ethabi::RawLog; -use crypto::publickey::{Signature, Error as EthKeyError}; - -/// Type for block number. -/// Duplicated from ethcore types -pub type BlockNumber = u64; - -/// Uniquely identifies block. -/// Duplicated from ethcore types -#[derive(Debug, PartialEq, Copy, Clone, Hash, Eq)] -pub enum BlockId { - /// Block's sha3. - /// Querying by hash is always faster. - Hash(H256), - /// Block number within canon blockchain. - Number(BlockNumber), - /// Earliest block (genesis). - Earliest, - /// Latest mined block. - Latest, -} - -/// Contract address. -#[derive(Debug, Clone)] -pub enum ContractAddress { - /// Address is read from registry. - Registry, - /// Address is specified. - Address(ethereum_types::Address), -} - -/// Key pair with signing ability. -pub trait SigningKeyPair: Send + Sync { - /// Public portion of key. - fn public(&self) -> &Public; - /// Address of key owner. - fn address(&self) -> Address; - /// Sign data with the key. - fn sign(&self, data: &H256) -> Result; -} - -/// Wrapps client ChainNotify in order to send signal about new blocks -pub trait NewBlocksNotify: Send + Sync { - /// Fires when chain has new blocks. - /// Sends this signal only, if contracts' update required - fn new_blocks(&self, _new_enacted_len: usize) { - // does nothing by default - } -} - -/// Blockchain logs Filter. -#[derive(Debug, PartialEq)] -pub struct Filter { - /// Blockchain will be searched from this block. - pub from_block: BlockId, - - /// Search addresses. - /// - /// If None, match all. - /// If specified, log must be produced by one of these addresses. - pub address: Option>, - - /// Search topics. - /// - /// If None, match all. - /// If specified, log must contain one of these topics. - pub topics: Vec>>, -} - -/// Blockchain representation for Secret Store -pub trait SecretStoreChain: Send + Sync + 'static { - /// Adds listener for chain's NewBlocks event - fn add_listener(&self, target: Arc); - - /// Check if the underlying chain is in the trusted state - fn is_trusted(&self) -> bool; - - /// Transact contract. - fn transact_contract(&self, contract: Address, tx_data: Bytes) -> Result<(), EthKeyError>; - - /// Read contract address. If address source is registry, address only returned if current client state is - /// trusted. Address from registry is read from registry from block latest block with - /// REQUEST_CONFIRMATIONS_REQUIRED confirmations. - fn read_contract_address(&self, registry_name: &str, address: &ContractAddress) -> Option
; - - /// Call contract in the blockchain - fn call_contract(&self, block_id: BlockId, contract_address: Address, data: Bytes) -> Result; - - /// Returns blockhash for block id - fn block_hash(&self, id: BlockId) -> Option; - - /// Returns block number for block id - fn block_number(&self, id: BlockId) -> Option; - - /// Retrieve last blockchain logs for the filter - fn retrieve_last_logs(&self, filter: Filter) -> Option>; - - /// Get hash of the last block with predefined number of confirmations (depends on the chain). - fn get_confirmed_block_hash(&self) -> Option; -} diff --git a/secret-store/src/key_server.rs b/secret-store/src/key_server.rs deleted file mode 100644 index abc2a75f513..00000000000 --- a/secret-store/src/key_server.rs +++ /dev/null @@ -1,714 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeSet; -use std::sync::Arc; -use futures::{future::{err, result}, Future}; -use parking_lot::Mutex; -use crypto::DEFAULT_MAC; -use crypto::publickey::public_to_address; -use parity_runtime::Executor; -use super::acl_storage::AclStorage; -use super::key_storage::KeyStorage; -use super::key_server_set::KeyServerSet; -use blockchain::SigningKeyPair; -use key_server_cluster::{math, new_network_cluster, ClusterSession, WaitableSession}; -use traits::{AdminSessionsServer, ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; -use types::{Error, Public, RequestSignature, Requester, ServerKeyId, EncryptedDocumentKey, EncryptedDocumentKeyShadow, - ClusterConfiguration, MessageHash, EncryptedMessageSignature, NodeId}; -use key_server_cluster::{ClusterClient, ClusterConfiguration as NetClusterConfiguration, NetConnectionsManagerConfig}; - -/// Secret store key server implementation -pub struct KeyServerImpl { - data: Arc>, -} - -/// Secret store key server data. -pub struct KeyServerCore { - cluster: Arc, -} - -impl KeyServerImpl { - /// Create new key server instance - pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, - acl_storage: Arc, key_storage: Arc, executor: Executor) -> Result - { - Ok(KeyServerImpl { - data: Arc::new(Mutex::new(KeyServerCore::new(config, key_server_set, self_key_pair, acl_storage, key_storage, executor)?)), - }) - } - - /// Get cluster client reference. - pub fn cluster(&self) -> Arc { - self.data.lock().cluster.clone() - } -} - -impl KeyServer for KeyServerImpl {} - -impl AdminSessionsServer for KeyServerImpl { - fn change_servers_set( - &self, - old_set_signature: RequestSignature, - new_set_signature: RequestSignature, - new_servers_set: BTreeSet, - ) -> Box + Send> { - return_session(self.data.lock().cluster - .new_servers_set_change_session(None, None, new_servers_set, old_set_signature, new_set_signature)) - } -} - -impl ServerKeyGenerator for KeyServerImpl { - fn generate_key( - &self, - key_id: ServerKeyId, - author: Requester, - threshold: usize, - ) -> Box + Send> { - // recover requestor' address key from signature - let address = author.address(&key_id).map_err(Error::InsufficientRequesterData); - - // generate server key - return_session(address.and_then(|address| self.data.lock().cluster - .new_generation_session(key_id, None, address, threshold))) - } - - fn restore_key_public( - &self, - key_id: ServerKeyId, - author: Requester, - ) -> Box + Send> { - // recover requestor' public key from signature - let session_and_address = author - .address(&key_id) - .map_err(Error::InsufficientRequesterData) - .and_then(|address| self.data.lock().cluster.new_key_version_negotiation_session(key_id) - .map(|session| (session, address))); - let (session, address) = match session_and_address { - Ok((session, address)) => (session, address), - Err(error) => return Box::new(err(error)), - }; - - // negotiate key version && retrieve common key data - let core_session = session.session.clone(); - Box::new(session.into_wait_future() - .and_then(move |_| core_session.common_key_data() - .map(|key_share| (key_share, address))) - .and_then(|(key_share, address)| if key_share.author == address { - Ok(key_share.public) - } else { - Err(Error::AccessDenied) - })) - } -} - -impl DocumentKeyServer for KeyServerImpl { - fn store_document_key( - &self, - key_id: ServerKeyId, - author: Requester, - common_point: Public, - encrypted_document_key: Public, - ) -> Box + Send> { - // store encrypted key - return_session(self.data.lock().cluster.new_encryption_session(key_id, - author.clone(), common_point, encrypted_document_key)) - } - - fn generate_document_key( - &self, - key_id: ServerKeyId, - author: Requester, - threshold: usize, - ) -> Box + Send> { - // recover requestor' public key from signature - let public = result(author.public(&key_id).map_err(Error::InsufficientRequesterData)); - - // generate server key - let data = self.data.clone(); - let server_key = public.and_then(move |public| { - let data = data.lock(); - let session = data.cluster.new_generation_session(key_id, None, public_to_address(&public), threshold); - result(session.map(|session| (public, session))) - }) - .and_then(|(public, session)| session.into_wait_future().map(move |server_key| (public, server_key))); - - // generate random document key - let document_key = server_key.and_then(|(public, server_key)| - result(math::generate_random_point() - .and_then(|document_key| math::encrypt_secret(&document_key, &server_key) - .map(|encrypted_document_key| (public, document_key, encrypted_document_key)))) - ); - - // store document key in the storage - let data = self.data.clone(); - let stored_document_key = document_key.and_then(move |(public, document_key, encrypted_document_key)| { - let data = data.lock(); - let session = data.cluster.new_encryption_session(key_id, - author.clone(), encrypted_document_key.common_point, encrypted_document_key.encrypted_point); - result(session.map(|session| (public, document_key, session))) - }) - .and_then(|(public, document_key, session)| session.into_wait_future().map(move |_| (public, document_key))); - - // encrypt document key with requestor public key - let encrypted_document_key = stored_document_key - .and_then(|(public, document_key)| crypto::publickey::ecies::encrypt(&public, &DEFAULT_MAC, document_key.as_bytes()) - .map_err(|err| Error::Internal(format!("Error encrypting document key: {}", err)))); - - Box::new(encrypted_document_key) - } - - fn restore_document_key( - &self, - key_id: ServerKeyId, - requester: Requester, - ) -> Box + Send> { - // recover requestor' public key from signature - let public = result(requester.public(&key_id).map_err(Error::InsufficientRequesterData)); - - // decrypt document key - let data = self.data.clone(); - let stored_document_key = public.and_then(move |public| { - let data = data.lock(); - let session = data.cluster.new_decryption_session(key_id, None, requester.clone(), None, false, false); - result(session.map(|session| (public, session))) - }) - .and_then(|(public, session)| session.into_wait_future().map(move |document_key| (public, document_key))); - - // encrypt document key with requestor public key - let encrypted_document_key = stored_document_key - .and_then(|(public, document_key)| - crypto::publickey::ecies::encrypt(&public, &DEFAULT_MAC, document_key.decrypted_secret.as_bytes()) - .map_err(|err| Error::Internal(format!("Error encrypting document key: {}", err)))); - - Box::new(encrypted_document_key) - } - - fn restore_document_key_shadow( - &self, - key_id: ServerKeyId, - requester: Requester, - ) -> Box + Send> { - return_session(self.data.lock().cluster.new_decryption_session(key_id, - None, requester.clone(), None, true, false)) - } -} - -impl MessageSigner for KeyServerImpl { - fn sign_message_schnorr( - &self, - key_id: ServerKeyId, - requester: Requester, - message: MessageHash, - ) -> Box + Send> { - // recover requestor' public key from signature - let public = result(requester.public(&key_id).map_err(Error::InsufficientRequesterData)); - - // sign message - let data = self.data.clone(); - let signature = public.and_then(move |public| { - let data = data.lock(); - let session = data.cluster.new_schnorr_signing_session(key_id, requester.clone().into(), None, message); - result(session.map(|session| (public, session))) - }) - .and_then(|(public, session)| session.into_wait_future().map(move |signature| (public, signature))); - - // compose two message signature components into single one - let combined_signature = signature.map(|(public, signature)| { - let mut combined_signature = [0; 64]; - combined_signature[..32].clone_from_slice(signature.0.as_bytes()); - combined_signature[32..].clone_from_slice(signature.1.as_bytes()); - (public, combined_signature) - }); - - // encrypt signature with requestor public key - let encrypted_signature = combined_signature - .and_then(|(public, combined_signature)| crypto::publickey::ecies::encrypt(&public, &DEFAULT_MAC, &combined_signature) - .map_err(|err| Error::Internal(format!("Error encrypting message signature: {}", err)))); - - Box::new(encrypted_signature) - } - - fn sign_message_ecdsa( - &self, - key_id: ServerKeyId, - requester: Requester, - message: MessageHash, - ) -> Box + Send> { - // recover requestor' public key from signature - let public = result(requester.public(&key_id).map_err(Error::InsufficientRequesterData)); - - // sign message - let data = self.data.clone(); - let signature = public.and_then(move |public| { - let data = data.lock(); - let session = data.cluster.new_ecdsa_signing_session(key_id, requester.clone().into(), None, message); - result(session.map(|session| (public, session))) - }) - .and_then(|(public, session)| session.into_wait_future().map(move |signature| (public, signature))); - - // encrypt combined signature with requestor public key - let encrypted_signature = signature - .and_then(|(public, signature)| crypto::publickey::ecies::encrypt(&public, &DEFAULT_MAC, &*signature) - .map_err(|err| Error::Internal(format!("Error encrypting message signature: {}", err)))); - - Box::new(encrypted_signature) - } -} - -impl KeyServerCore { - pub fn new(config: &ClusterConfiguration, key_server_set: Arc, self_key_pair: Arc, - acl_storage: Arc, key_storage: Arc, executor: Executor) -> Result - { - let cconfig = NetClusterConfiguration { - self_key_pair: self_key_pair.clone(), - key_server_set: key_server_set, - acl_storage: acl_storage, - key_storage: key_storage, - admin_public: config.admin_public, - preserve_sessions: false, - }; - let net_config = NetConnectionsManagerConfig { - listen_address: (config.listener_address.address.clone(), config.listener_address.port), - allow_connecting_to_higher_nodes: config.allow_connecting_to_higher_nodes, - auto_migrate_enabled: config.auto_migrate_enabled, - }; - - let core = new_network_cluster(executor, cconfig, net_config)?; - let cluster = core.client(); - core.run()?; - - Ok(KeyServerCore { - cluster, - }) - } -} - -fn return_session( - session: Result, Error>, -) -> Box + Send> { - match session { - Ok(session) => Box::new(session.into_wait_future()), - Err(error) => Box::new(err(error)) - } -} - -#[cfg(test)] -pub mod tests { - use std::collections::BTreeSet; - use std::time; - use std::sync::Arc; - use std::net::SocketAddr; - use std::collections::BTreeMap; - use futures::Future; - use crypto::DEFAULT_MAC; - use crypto::publickey::{Secret, Random, Generator, verify_public}; - use acl_storage::DummyAclStorage; - use key_storage::KeyStorage; - use key_storage::tests::DummyKeyStorage; - use node_key_pair::PlainNodeKeyPair; - use key_server_set::tests::MapKeyServerSet; - use key_server_cluster::math; - use ethereum_types::{H256, H520}; - use parity_runtime::Runtime; - use types::{Error, Public, ClusterConfiguration, NodeAddress, RequestSignature, ServerKeyId, - EncryptedDocumentKey, EncryptedDocumentKeyShadow, MessageHash, EncryptedMessageSignature, - Requester, NodeId}; - use traits::{AdminSessionsServer, ServerKeyGenerator, DocumentKeyServer, MessageSigner, KeyServer}; - use super::KeyServerImpl; - - #[derive(Default)] - pub struct DummyKeyServer; - - impl KeyServer for DummyKeyServer {} - - impl AdminSessionsServer for DummyKeyServer { - fn change_servers_set( - &self, - _old_set_signature: RequestSignature, - _new_set_signature: RequestSignature, - _new_servers_set: BTreeSet, - ) -> Box + Send> { - unimplemented!("test-only") - } - } - - impl ServerKeyGenerator for DummyKeyServer { - fn generate_key( - &self, - _key_id: ServerKeyId, - _author: Requester, - _threshold: usize, - ) -> Box + Send> { - unimplemented!("test-only") - } - - fn restore_key_public( - &self, - _key_id: ServerKeyId, - _author: Requester, - ) -> Box + Send> { - unimplemented!("test-only") - } - } - - impl DocumentKeyServer for DummyKeyServer { - fn store_document_key( - &self, - _key_id: ServerKeyId, - _author: Requester, - _common_point: Public, - _encrypted_document_key: Public, - ) -> Box + Send> { - unimplemented!("test-only") - } - - fn generate_document_key( - &self, - _key_id: ServerKeyId, - _author: Requester, - _threshold: usize, - ) -> Box + Send> { - unimplemented!("test-only") - } - - fn restore_document_key( - &self, - _key_id: ServerKeyId, - _requester: Requester, - ) -> Box + Send> { - unimplemented!("test-only") - } - - fn restore_document_key_shadow( - &self, - _key_id: ServerKeyId, - _requester: Requester, - ) -> Box + Send> { - unimplemented!("test-only") - } - } - - impl MessageSigner for DummyKeyServer { - fn sign_message_schnorr( - &self, - _key_id: ServerKeyId, - _requester: Requester, - _message: MessageHash, - ) -> Box + Send> { - unimplemented!("test-only") - } - - fn sign_message_ecdsa( - &self, - _key_id: ServerKeyId, - _requester: Requester, - _message: MessageHash, - ) -> Box + Send> { - unimplemented!("test-only") - } - } - - fn make_key_servers(start_port: u16, num_nodes: usize) -> (Vec, Vec>, Runtime) { - let key_pairs: Vec<_> = (0..num_nodes).map(|_| Random.generate().unwrap()).collect(); - let configs: Vec<_> = (0..num_nodes).map(|i| ClusterConfiguration { - listener_address: NodeAddress { - address: "127.0.0.1".into(), - port: start_port + (i as u16), - }, - nodes: key_pairs.iter().enumerate().map(|(j, kp)| (kp.public().clone(), - NodeAddress { - address: "127.0.0.1".into(), - port: start_port + (j as u16), - })).collect(), - key_server_set_contract_address: None, - allow_connecting_to_higher_nodes: false, - admin_public: None, - auto_migrate_enabled: false, - }).collect(); - let key_servers_set: BTreeMap = configs[0].nodes.iter() - .map(|(k, a)| (k.clone(), format!("{}:{}", a.address, a.port).parse().unwrap())) - .collect(); - let key_storages = (0..num_nodes).map(|_| Arc::new(DummyKeyStorage::default())).collect::>(); - let runtime = Runtime::with_thread_count(4); - let key_servers: Vec<_> = configs.into_iter().enumerate().map(|(i, cfg)| - KeyServerImpl::new(&cfg, Arc::new(MapKeyServerSet::new(false, key_servers_set.clone())), - Arc::new(PlainNodeKeyPair::new(key_pairs[i].clone())), - Arc::new(DummyAclStorage::default()), - key_storages[i].clone(), runtime.executor()).unwrap() - ).collect(); - - // wait until connections are established. It is fast => do not bother with events here - let start = time::Instant::now(); - let mut tried_reconnections = false; - loop { - if key_servers.iter().all(|ks| ks.cluster().is_fully_connected()) { - break; - } - - let old_tried_reconnections = tried_reconnections; - let mut fully_connected = true; - for key_server in &key_servers { - if !key_server.cluster().is_fully_connected() { - fully_connected = false; - if !old_tried_reconnections { - tried_reconnections = true; - key_server.cluster().connect(); - } - } - } - if fully_connected { - break; - } - if time::Instant::now() - start > time::Duration::from_millis(3000) { - panic!("connections are not established in 3000ms"); - } - } - - (key_servers, key_storages, runtime) - } - - #[test] - fn document_key_generation_and_retrievement_works_over_network_with_single_node() { - let _ = ::env_logger::try_init(); - let (key_servers, _, runtime) = make_key_servers(6070, 1); - - // generate document key - let threshold = 0; - let document = Random.generate().unwrap().secret().clone(); - let secret = Random.generate().unwrap().secret().clone(); - let signature: Requester = crypto::publickey::sign(&secret, &document).unwrap().into(); - let generated_key = key_servers[0].generate_document_key( - *document, - signature.clone(), - threshold, - ).wait().unwrap(); - let generated_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); - - // now let's try to retrieve key back - for key_server in key_servers.iter() { - let retrieved_key = key_server.restore_document_key( - *document, - signature.clone(), - ).wait().unwrap(); - let retrieved_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); - assert_eq!(retrieved_key, generated_key); - } - drop(runtime); - } - - #[test] - fn document_key_generation_and_retrievement_works_over_network_with_3_nodes() { - let _ = ::env_logger::try_init(); - let (key_servers, key_storages, runtime) = make_key_servers(6080, 3); - - let test_cases = [0, 1, 2]; - for threshold in &test_cases { - // generate document key - let document = Random.generate().unwrap().secret().clone(); - let secret = Random.generate().unwrap().secret().clone(); - let signature: Requester = crypto::publickey::sign(&secret, &document).unwrap().into(); - let generated_key = key_servers[0].generate_document_key( - *document, - signature.clone(), - *threshold, - ).wait().unwrap(); - let generated_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); - - // now let's try to retrieve key back - for (i, key_server) in key_servers.iter().enumerate() { - let retrieved_key = key_server.restore_document_key( - *document, - signature.clone(), - ).wait().unwrap(); - let retrieved_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); - assert_eq!(retrieved_key, generated_key); - - let key_share = key_storages[i].get(&document).unwrap().unwrap(); - assert!(key_share.common_point.is_some()); - assert!(key_share.encrypted_point.is_some()); - } - } - drop(runtime); - } - - #[test] - fn server_key_generation_and_storing_document_key_works_over_network_with_3_nodes() { - let _ = ::env_logger::try_init(); - let (key_servers, _, runtime) = make_key_servers(6090, 3); - - let test_cases = [0, 1, 2]; - for threshold in &test_cases { - // generate server key - let server_key_id = Random.generate().unwrap().secret().clone(); - let requestor_secret = Random.generate().unwrap().secret().clone(); - let signature: Requester = crypto::publickey::sign(&requestor_secret, &server_key_id).unwrap().into(); - let server_public = key_servers[0].generate_key( - *server_key_id, - signature.clone(), - *threshold, - ).wait().unwrap(); - - // generate document key (this is done by KS client so that document key is unknown to any KS) - let generated_key = Random.generate().unwrap().public().clone(); - let encrypted_document_key = math::encrypt_secret(&generated_key, &server_public).unwrap(); - - // store document key - key_servers[0].store_document_key(*server_key_id, signature.clone(), - encrypted_document_key.common_point, encrypted_document_key.encrypted_point).wait().unwrap(); - - // now let's try to retrieve key back - for key_server in key_servers.iter() { - let retrieved_key = key_server.restore_document_key(*server_key_id, signature.clone()).wait().unwrap(); - let retrieved_key = crypto::publickey::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &retrieved_key).unwrap(); - let retrieved_key = Public::from_slice(&retrieved_key); - assert_eq!(retrieved_key, generated_key); - } - } - drop(runtime); - } - - #[test] - fn server_key_generation_and_message_signing_works_over_network_with_3_nodes() { - let _ = ::env_logger::try_init(); - let (key_servers, _, runtime) = make_key_servers(6100, 3); - - let test_cases = [0, 1, 2]; - for threshold in &test_cases { - // generate server key - let server_key_id = Random.generate().unwrap().secret().clone(); - let requestor_secret = Random.generate().unwrap().secret().clone(); - let signature: Requester = crypto::publickey::sign(&requestor_secret, &server_key_id).unwrap().into(); - let server_public = key_servers[0].generate_key( - *server_key_id, - signature.clone(), - *threshold, - ).wait().unwrap(); - - // sign message - let message_hash = H256::from_low_u64_be(42); - let combined_signature = key_servers[0].sign_message_schnorr( - *server_key_id, - signature, - message_hash, - ).wait().unwrap(); - let combined_signature = crypto::publickey::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &combined_signature).unwrap(); - let signature_c = Secret::copy_from_slice(&combined_signature[..32]).unwrap(); - let signature_s = Secret::copy_from_slice(&combined_signature[32..]).unwrap(); - - // check signature - assert_eq!(math::verify_schnorr_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); - } - drop(runtime); - } - - #[test] - fn decryption_session_is_delegated_when_node_does_not_have_key_share() { - let _ = ::env_logger::try_init(); - let (key_servers, key_storages, runtime) = make_key_servers(6110, 3); - - // generate document key - let threshold = 0; - let document = Random.generate().unwrap().secret().clone(); - let secret = Random.generate().unwrap().secret().clone(); - let signature: Requester = crypto::publickey::sign(&secret, &document).unwrap().into(); - let generated_key = key_servers[0].generate_document_key( - *document, - signature.clone(), - threshold, - ).wait().unwrap(); - let generated_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &generated_key).unwrap(); - - // remove key from node0 - key_storages[0].remove(&document).unwrap(); - - // now let's try to retrieve key back by requesting it from node0, so that session must be delegated - let retrieved_key = key_servers[0].restore_document_key(*document, signature).wait().unwrap(); - let retrieved_key = crypto::publickey::ecies::decrypt(&secret, &DEFAULT_MAC, &retrieved_key).unwrap(); - assert_eq!(retrieved_key, generated_key); - drop(runtime); - } - - #[test] - fn schnorr_signing_session_is_delegated_when_node_does_not_have_key_share() { - let _ = ::env_logger::try_init(); - let (key_servers, key_storages, runtime) = make_key_servers(6114, 3); - let threshold = 1; - - // generate server key - let server_key_id = Random.generate().unwrap().secret().clone(); - let requestor_secret = Random.generate().unwrap().secret().clone(); - let signature: Requester = crypto::publickey::sign(&requestor_secret, &server_key_id).unwrap().into(); - let server_public = key_servers[0].generate_key(*server_key_id, signature.clone(), threshold).wait().unwrap(); - - // remove key from node0 - key_storages[0].remove(&server_key_id).unwrap(); - - // sign message - let message_hash = H256::from_low_u64_be(42); - let combined_signature = key_servers[0].sign_message_schnorr( - *server_key_id, - signature, - message_hash, - ).wait().unwrap(); - let combined_signature = crypto::publickey::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &combined_signature).unwrap(); - let signature_c = Secret::copy_from_slice(&combined_signature[..32]).unwrap(); - let signature_s = Secret::copy_from_slice(&combined_signature[32..]).unwrap(); - - // check signature - assert_eq!(math::verify_schnorr_signature(&server_public, &(signature_c, signature_s), &message_hash), Ok(true)); - drop(runtime); - } - - #[test] - fn ecdsa_signing_session_is_delegated_when_node_does_not_have_key_share() { - let _ = ::env_logger::try_init(); - let (key_servers, key_storages, runtime) = make_key_servers(6117, 4); - let threshold = 1; - - // generate server key - let server_key_id = Random.generate().unwrap().secret().clone(); - let requestor_secret = Random.generate().unwrap().secret().clone(); - let signature = crypto::publickey::sign(&requestor_secret, &server_key_id).unwrap(); - let server_public = key_servers[0].generate_key( - *server_key_id, - signature.clone().into(), - threshold, - ).wait().unwrap(); - - // remove key from node0 - key_storages[0].remove(&server_key_id).unwrap(); - - // sign message - let message_hash = H256::random(); - let signature = key_servers[0].sign_message_ecdsa( - *server_key_id, - signature.clone().into(), - message_hash, - ).wait().unwrap(); - let signature = crypto::publickey::ecies::decrypt(&requestor_secret, &DEFAULT_MAC, &signature).unwrap(); - let signature = H520::from_slice(&signature[0..65]); - - // check signature - assert!(verify_public(&server_public, &signature.into(), &message_hash).unwrap()); - drop(runtime); - } - - #[test] - fn servers_set_change_session_works_over_network() { - // TODO [Test] - } -} diff --git a/secret-store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs b/secret-store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs deleted file mode 100644 index b90d850ff05..00000000000 --- a/secret-store/src/key_server_cluster/admin_sessions/key_version_negotiation_session.rs +++ /dev/null @@ -1,969 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeSet, BTreeMap}; -use ethereum_types::{Address, H256}; -use crypto::publickey::Secret; -use futures::Oneshot; -use parking_lot::Mutex; -use key_server_cluster::{Error, SessionId, NodeId, DocumentKeyShare}; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession, CompletionSignal}; -use key_server_cluster::decryption_session::SessionImpl as DecryptionSession; -use key_server_cluster::signing_session_ecdsa::SessionImpl as EcdsaSigningSession; -use key_server_cluster::signing_session_schnorr::SessionImpl as SchnorrSigningSession; -use key_server_cluster::message::{Message, KeyVersionNegotiationMessage, RequestKeyVersions, - KeyVersions, KeyVersionsError, FailedKeyVersionContinueAction, CommonKeyData}; -use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - -// TODO [Opt]: change sessions so that versions are sent by chunks. -/// Number of versions sent in single message. -const VERSIONS_PER_MESSAGE: usize = 32; - -/// Key version negotiation transport. -pub trait SessionTransport { - /// Broadcast message to all nodes. - fn broadcast(&self, message: KeyVersionNegotiationMessage) -> Result<(), Error>; - /// Send message to given node. - fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error>; -} - -/// Key version negotiation result computer. -pub trait SessionResultComputer: Send + Sync { - /// Compute result of session, if possible. - fn compute_result(&self, threshold: Option, confirmations: &BTreeSet, versions: &BTreeMap>) -> Option>; -} - -/// Key discovery session API. -pub struct SessionImpl { - /// Session core. - core: SessionCore, - /// Session data. - data: Mutex, -} - -/// Action after key version is negotiated. -#[derive(Clone)] -pub enum ContinueAction { - /// Decryption session + origin + is_shadow_decryption + is_broadcast_decryption. - Decrypt(Arc, Option
, bool, bool), - /// Schnorr signing session + message hash. - SchnorrSign(Arc, H256), - /// ECDSA signing session + message hash. - EcdsaSign(Arc, H256), -} - -/// Failed action after key version is negotiated. -#[derive(Clone, Debug, PartialEq)] -pub enum FailedContinueAction { - /// Decryption origin + requester. - Decrypt(Option
, Address), -} - -/// Immutable session data. -struct SessionCore { - /// Session meta. - pub meta: ShareChangeSessionMeta, - /// Sub-session id. - pub sub_session: Secret, - /// Key share. - pub key_share: Option, - /// Session result computer. - pub result_computer: Arc, - /// Session transport. - pub transport: T, - /// Session nonce. - pub nonce: u64, - /// Session completion signal. - pub completed: CompletionSignal>, -} - -/// Mutable session data. -struct SessionData { - /// Session state. - pub state: SessionState, - /// Initialization confirmations. - pub confirmations: Option>, - /// Common key data that nodes have agreed upon. - pub key_share: Option, - /// { Version => Nodes } - pub versions: Option>>, - /// Session result. - pub result: Option, Error>>, - /// Continue action. - pub continue_with: Option, - /// Failed continue action (reported in error message by master node). - pub failed_continue_with: Option, -} - -/// SessionImpl creation parameters -pub struct SessionParams { - /// Session meta. - pub meta: ShareChangeSessionMeta, - /// Sub-session id. - pub sub_session: Secret, - /// Key share. - pub key_share: Option, - /// Session result computer. - pub result_computer: Arc, - /// Session transport to communicate to other cluster nodes. - pub transport: T, - /// Session nonce. - pub nonce: u64, -} - -/// Signing session state. -#[derive(Debug, PartialEq)] -enum SessionState { - /// Waiting for initialization. - WaitingForInitialization, - /// Waiting for responses. - WaitingForResponses, - /// Session is completed. - Finished, -} - -/// Isolated session transport. -pub struct IsolatedSessionTransport { - /// Cluster. - pub cluster: Arc, - /// Key id. - pub key_id: SessionId, - /// Sub session id. - pub sub_session: Secret, - /// Session-level nonce. - pub nonce: u64, -} - -/// Fastest session result computer. Computes first possible version that can be recovered on this node. -/// If there's no such version, selects version with the most support. -pub struct FastestResultComputer { - /// This node id. - self_node_id: NodeId, - /// Threshold (if known). - threshold: Option, - /// Count of all configured key server nodes. - configured_nodes_count: usize, - /// Count of all connected key server nodes. - connected_nodes_count: usize, -} - -/// Selects version with most support, waiting for responses from all nodes. -pub struct LargestSupportResultComputer; - -impl SessionImpl where T: SessionTransport { - /// Create new session. - pub fn new(params: SessionParams) -> (Self, Oneshot, Error>>) { - let (completed, oneshot) = CompletionSignal::new(); - (SessionImpl { - core: SessionCore { - meta: params.meta, - sub_session: params.sub_session, - key_share: params.key_share.clone(), - result_computer: params.result_computer, - transport: params.transport, - nonce: params.nonce, - completed, - }, - data: Mutex::new(SessionData { - state: SessionState::WaitingForInitialization, - confirmations: None, - key_share: params.key_share.map(|key_share| DocumentKeyShare { - threshold: key_share.threshold, - author: key_share.author, - public: key_share.public, - ..Default::default() - }), - versions: None, - result: None, - continue_with: None, - failed_continue_with: None, - }) - }, oneshot) - } - - /// Return session meta. - pub fn meta(&self) -> &ShareChangeSessionMeta { - &self.core.meta - } - - /// Return result computer reference. - pub fn version_holders(&self, version: &H256) -> Result, Error> { - Ok(self.data.lock().versions.as_ref().ok_or(Error::InvalidStateForRequest)? - .get(version).ok_or(Error::ServerKeyIsNotFound)? - .clone()) - } - - /// Set continue action. - pub fn set_continue_action(&self, action: ContinueAction) { - self.data.lock().continue_with = Some(action); - } - - /// Take continue action. - pub fn take_continue_action(&self) -> Option { - self.data.lock().continue_with.take() - } - - /// Take failed continue action. - pub fn take_failed_continue_action(&self) -> Option { - self.data.lock().failed_continue_with.take() - } - - /// Return session completion result (if available). - pub fn result(&self) -> Option, Error>> { - self.data.lock().result.clone() - } - - /// Retrieve common key data (author, threshold, public), if available. - pub fn common_key_data(&self) -> Result { - self.data.lock().key_share.clone() - .ok_or(Error::InvalidStateForRequest) - } - - /// Initialize session. - pub fn initialize(&self, connected_nodes: BTreeSet) -> Result<(), Error> { - // check state - let mut data = self.data.lock(); - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // update state - let mut confirmations = connected_nodes; - let mut versions: BTreeMap> = BTreeMap::new(); - let received_own_confirmation = confirmations.remove(&self.core.meta.self_node_id); - if received_own_confirmation { - if let Some(key_share) = self.core.key_share.as_ref() { - for version in &key_share.versions { - versions.entry(version.hash.clone()) - .or_insert_with(Default::default) - .insert(self.core.meta.self_node_id.clone()); - } - } - } - - // update state - let no_confirmations_required = confirmations.is_empty(); - data.state = SessionState::WaitingForResponses; - data.confirmations = Some(confirmations); - data.versions = Some(versions); - - // try to complete session - Self::try_complete(&self.core, &mut *data); - if no_confirmations_required && data.state != SessionState::Finished { - return Err(Error::ServerKeyIsNotFound); - } else if data.state == SessionState::Finished { - return Ok(()); - } - - // send requests - let confirmations = data.confirmations.as_ref().expect("dilled couple of lines above; qed"); - for connected_node in confirmations { - self.core.transport.send(connected_node, KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions { - session: self.core.meta.id.clone().into(), - sub_session: self.core.sub_session.clone().into(), - session_nonce: self.core.nonce, - }))?; - } - - Ok(()) - } - - /// Process single message. - pub fn process_message(&self, sender: &NodeId, message: &KeyVersionNegotiationMessage) -> Result<(), Error> { - if self.core.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &KeyVersionNegotiationMessage::RequestKeyVersions(ref message) => - self.on_key_versions_request(sender, message), - &KeyVersionNegotiationMessage::KeyVersions(ref message) => - self.on_key_versions(sender, message), - &KeyVersionNegotiationMessage::KeyVersionsError(ref message) => { - // remember failed continue action - if let Some(FailedKeyVersionContinueAction::Decrypt(Some(ref origin), ref requester)) = message.continue_with { - self.data.lock().failed_continue_with = - Some(FailedContinueAction::Decrypt(Some(origin.clone().into()), requester.clone().into())); - } - - self.on_session_error(sender, message.error.clone()); - Ok(()) - }, - } - } - - /// Process key versions request. - pub fn on_key_versions_request(&self, sender: &NodeId, _message: &RequestKeyVersions) -> Result<(), Error> { - debug_assert!(sender != &self.core.meta.self_node_id); - - // check message - if *sender != self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // send response - self.core.transport.send(sender, KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: self.core.meta.id.clone().into(), - sub_session: self.core.sub_session.clone().into(), - session_nonce: self.core.nonce, - key_common: self.core.key_share.as_ref().map(|key_share| CommonKeyData { - threshold: key_share.threshold, - author: key_share.author.into(), - public: key_share.public.into(), - }), - versions: self.core.key_share.as_ref().map(|key_share| - key_share.versions.iter().rev() - .filter(|v| v.id_numbers.contains_key(sender)) - .chain(key_share.versions.iter().rev().filter(|v| !v.id_numbers.contains_key(sender))) - .map(|v| v.hash.clone().into()) - .take(VERSIONS_PER_MESSAGE) - .collect()) - .unwrap_or_else(|| Default::default()) - }))?; - - // update state - data.state = SessionState::Finished; - data.result = Some(Ok(None)); - self.core.completed.send(Ok(None)); - - Ok(()) - } - - /// Process key versions response. - pub fn on_key_versions(&self, sender: &NodeId, message: &KeyVersions) -> Result<(), Error> { - debug_assert!(sender != &self.core.meta.self_node_id); - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::WaitingForResponses && data.state != SessionState::Finished { - return Err(Error::InvalidStateForRequest); - } - let reason = "this field is filled on master node when initializing; this is initialized master node; qed"; - if !data.confirmations.as_mut().expect(reason).remove(sender) { - return Err(Error::InvalidMessage); - } - - // remember versions that sender have - { - match message.key_common.as_ref() { - Some(key_common) if data.key_share.is_none() => { - data.key_share = Some(DocumentKeyShare { - threshold: key_common.threshold, - author: key_common.author.clone().into(), - public: key_common.public.clone().into(), - ..Default::default() - }); - }, - Some(key_common) => { - let prev_key_share = data.key_share.as_ref() - .expect("data.key_share.is_none() is matched by previous branch; qed"); - if prev_key_share.threshold != key_common.threshold || - prev_key_share.author.as_bytes() != key_common.author.as_bytes() || - prev_key_share.public.as_bytes() != key_common.public.as_bytes() - { - return Err(Error::InvalidMessage); - } - }, - None if message.versions.is_empty() => (), - None => return Err(Error::InvalidMessage), - } - - let versions = data.versions.as_mut().expect(reason); - for version in &message.versions { - versions.entry(version.clone().into()) - .or_insert_with(Default::default) - .insert(sender.clone()); - } - } - - // try to compute result - if data.state != SessionState::Finished { - Self::try_complete(&self.core, &mut *data); - } - - Ok(()) - } - - /// Try to complete result && finish session. - fn try_complete(core: &SessionCore, data: &mut SessionData) { - let reason = "this field is filled on master node when initializing; try_complete is only called on initialized master node; qed"; - let confirmations = data.confirmations.as_ref().expect(reason); - let versions = data.versions.as_ref().expect(reason); - let threshold = data.key_share.as_ref().map(|key_share| key_share.threshold); - if let Some(result) = core.result_computer.compute_result(threshold, confirmations, versions) { - // when the master node processing decryption service request, it starts with a key version negotiation session - // if the negotiation fails, only master node knows about it - // => if the error is fatal, only the master will know about it and report it to the contract && the request will never be rejected - // => let's broadcast fatal error so that every other node know about it, and, if it trusts to master node - // will report error to the contract - if let (Some(continue_with), Err(error)) = (data.continue_with.as_ref(), result.as_ref()) { - let origin = match *continue_with { - ContinueAction::Decrypt(_, origin, _, _) => origin.clone(), - _ => None, - }; - - let requester = match *continue_with { - ContinueAction::Decrypt(ref session, _, _, _) => session.requester().and_then(|r| r.address(&core.meta.id).ok()), - _ => None, - }; - - if origin.is_some() && requester.is_some() && !error.is_non_fatal() { - let requester = requester.expect("checked in above condition; qed"); - data.failed_continue_with = - Some(FailedContinueAction::Decrypt(origin.clone(), requester.clone())); - - let send_result = core.transport.broadcast(KeyVersionNegotiationMessage::KeyVersionsError(KeyVersionsError { - session: core.meta.id.clone().into(), - sub_session: core.sub_session.clone().into(), - session_nonce: core.nonce, - error: error.clone(), - continue_with: Some(FailedKeyVersionContinueAction::Decrypt( - origin.map(Into::into), - requester.into(), - )), - })); - - if let Err(send_error) = send_result { - warn!(target: "secretstore_net", "{}: failed to broadcast key version negotiation error {}: {}", - core.meta.self_node_id, error, send_error); - } - } - } - - let result = result.map(Some); - data.state = SessionState::Finished; - data.result = Some(result.clone()); - core.completed.send(result); - } - } -} - -impl ClusterSession for SessionImpl where T: SessionTransport { - type Id = SessionIdWithSubSession; - type CreationData = (); - type SuccessfulResult = Option<(H256, NodeId)>; - - fn type_name() -> &'static str { - "version negotiation" - } - - fn id(&self) -> SessionIdWithSubSession { - SessionIdWithSubSession::new(self.core.meta.id.clone(), self.core.sub_session.clone()) - } - - fn is_finished(&self) -> bool { - self.data.lock().state == SessionState::Finished - } - - fn on_session_timeout(&self) { - let mut data = self.data.lock(); - - if data.confirmations.is_some() { - data.confirmations.as_mut().expect("checked a line above; qed").clear(); - Self::try_complete(&self.core, &mut *data); - if data.state != SessionState::Finished { - warn!(target: "secretstore_net", "{}: key version negotiation session failed with timeout", self.core.meta.self_node_id); - - data.result = Some(Err(Error::ConsensusTemporaryUnreachable)); - self.core.completed.send(Err(Error::ConsensusTemporaryUnreachable)); - } - } - } - - fn on_node_timeout(&self, node: &NodeId) { - self.on_session_error(node, Error::NodeDisconnected) - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - let mut data = self.data.lock(); - - if data.confirmations.is_some() { - let is_waiting_for_confirmation = data.confirmations.as_mut().expect("checked a line above; qed").remove(node); - if !is_waiting_for_confirmation { - return; - } - - Self::try_complete(&self.core, &mut *data); - if data.state == SessionState::Finished { - return; - } - } - - warn!(target: "secretstore_net", "{}: key version negotiation session failed because of {} from {}", - self.core.meta.self_node_id, error, node); - - data.state = SessionState::Finished; - data.result = Some(Err(error.clone())); - self.core.completed.send(Err(error)); - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::KeyVersionNegotiation(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl SessionTransport for IsolatedSessionTransport { - fn broadcast(&self, message: KeyVersionNegotiationMessage) -> Result<(), Error> { - self.cluster.broadcast(Message::KeyVersionNegotiation(message)) - } - - fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error> { - self.cluster.send(node, Message::KeyVersionNegotiation(message)) - } -} - -impl FastestResultComputer { - pub fn new(self_node_id: NodeId, key_share: Option<&DocumentKeyShare>, configured_nodes_count: usize, connected_nodes_count: usize) -> Self { - let threshold = key_share.map(|ks| ks.threshold); - FastestResultComputer { - self_node_id, - threshold, - configured_nodes_count, - connected_nodes_count, - } - }} - -impl SessionResultComputer for FastestResultComputer { - fn compute_result(&self, threshold: Option, confirmations: &BTreeSet, versions: &BTreeMap>) -> Option> { - match self.threshold.or(threshold) { - // if there's no versions at all && we're not waiting for confirmations anymore - _ if confirmations.is_empty() && versions.is_empty() => Some(Err(Error::ServerKeyIsNotFound)), - // if we have key share on this node - Some(threshold) => { - // select version this node have, with enough participants - let has_key_share = self.threshold.is_some(); - let version = versions.iter().find(|&(_, ref n)| !has_key_share || n.contains(&self.self_node_id) && n.len() >= threshold + 1); - // if there's no such version, wait for more confirmations - match version { - Some((version, nodes)) => Some(Ok((version.clone(), if has_key_share { self.self_node_id.clone() } else { nodes.iter().cloned().nth(0) - .expect("version is only inserted when there's at least one owner; qed") }))), - None if !confirmations.is_empty() => None, - // otherwise - try to find any version - None => Some(versions.iter() - .find(|&(_, ref n)| n.len() >= threshold + 1) - .map(|(version, nodes)| Ok((version.clone(), nodes.iter().cloned().nth(0) - .expect("version is only inserted when there's at least one owner; qed")))) - // if there's no version consensus among all connected nodes - // AND we're connected to ALL configured nodes - // OR there are less than required nodes for key restore - // => this means that we can't restore key with CURRENT configuration => respond with fatal error - // otherwise we could try later, after all nodes are connected - .unwrap_or_else(|| Err(if self.configured_nodes_count == self.connected_nodes_count - || self.configured_nodes_count < threshold + 1 { - Error::ConsensusUnreachable - } else { - Error::ConsensusTemporaryUnreachable - }))), - } - }, - // if we do not have share, then wait for all confirmations - None if !confirmations.is_empty() => None, - // ...and select version with largest support - None => Some(versions.iter() - .max_by_key(|&(_, ref n)| n.len()) - .map(|(version, nodes)| Ok((version.clone(), nodes.iter().cloned().nth(0) - .expect("version is only inserted when there's at least one owner; qed")))) - .unwrap_or_else(|| Err(if self.configured_nodes_count == self.connected_nodes_count { - Error::ConsensusUnreachable - } else { - Error::ConsensusTemporaryUnreachable - }))), - } - } -} - -impl SessionResultComputer for LargestSupportResultComputer { - fn compute_result(&self, _threshold: Option, confirmations: &BTreeSet, versions: &BTreeMap>) -> Option> { - if !confirmations.is_empty() { - return None; - } - if versions.is_empty() { - return Some(Err(Error::ServerKeyIsNotFound)); - } - - versions.iter() - .max_by_key(|&(_, ref n)| n.len()) - .map(|(version, nodes)| Ok((version.clone(), nodes.iter().cloned().nth(0) - .expect("version is only inserted when there's at least one owner; qed")))) - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::collections::{VecDeque, BTreeMap, BTreeSet}; - use ethereum_types::{H512, H160, Address}; - use crypto::publickey::public_to_address; - use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, DummyKeyStorage, - DocumentKeyShare, DocumentKeyShareVersion}; - use key_server_cluster::math; - use key_server_cluster::cluster::Cluster; - use key_server_cluster::cluster::tests::DummyCluster; - use key_server_cluster::cluster_sessions::ClusterSession; - use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - use key_server_cluster::decryption_session::create_default_decryption_session; - use key_server_cluster::message::{ - Message, KeyVersionNegotiationMessage, RequestKeyVersions, - CommonKeyData, KeyVersions, - }; - use super::{ - SessionImpl, SessionTransport, SessionParams, FastestResultComputer, LargestSupportResultComputer, - SessionResultComputer, SessionState, ContinueAction, FailedContinueAction, - }; - - struct DummyTransport { - cluster: Arc, - } - - impl SessionTransport for DummyTransport { - fn broadcast(&self, message: KeyVersionNegotiationMessage) -> Result<(), Error> { - self.cluster.broadcast(Message::KeyVersionNegotiation(message)) - } - - fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error> { - self.cluster.send(node, Message::KeyVersionNegotiation(message)) - } - } - - struct Node { - pub cluster: Arc, - pub key_storage: Arc, - pub session: SessionImpl, - } - - struct MessageLoop { - pub session_id: SessionId, - pub nodes: BTreeMap, - pub queue: VecDeque<(NodeId, NodeId, Message)>, - } - - impl MessageLoop { - pub fn prepare_nodes(nodes_num: usize) -> BTreeMap> { - (0..nodes_num).map(|_| (math::generate_random_point().unwrap(), - Arc::new(DummyKeyStorage::default()))).collect() - } - - pub fn empty(nodes_num: usize) -> Self { - Self::new(Self::prepare_nodes(nodes_num)) - } - - pub fn new(nodes: BTreeMap>) -> Self { - let master_node_id = nodes.keys().cloned().nth(0).unwrap(); - let sub_sesion = math::generate_random_scalar().unwrap(); - let all_nodes_ids: BTreeSet<_> = nodes.keys().cloned().collect(); - MessageLoop { - session_id: Default::default(), - nodes: nodes.iter().map(|(node_id, key_storage)| { - let cluster = Arc::new(DummyCluster::new(node_id.clone())); - cluster.add_nodes(all_nodes_ids.iter().cloned()); - (node_id.clone(), Node { - cluster: cluster.clone(), - key_storage: key_storage.clone(), - session: SessionImpl::new(SessionParams { - meta: ShareChangeSessionMeta { - id: Default::default(), - self_node_id: node_id.clone(), - master_node_id: master_node_id.clone(), - configured_nodes_count: nodes.len(), - connected_nodes_count: nodes.len(), - }, - sub_session: sub_sesion.clone(), - key_share: key_storage.get(&Default::default()).unwrap(), - result_computer: Arc::new(FastestResultComputer::new( - node_id.clone(), - key_storage.get(&Default::default()).unwrap().as_ref(), - nodes.len(), nodes.len() - )), - transport: DummyTransport { - cluster: cluster, - }, - nonce: 0, - }).0, - }) - }).collect(), - queue: VecDeque::new(), - } - } - - pub fn node_id(&self, idx: usize) -> &NodeId { - self.nodes.keys().nth(idx).unwrap() - } - - pub fn session(&self, idx: usize) -> &SessionImpl { - &self.nodes.values().nth(idx).unwrap().session - } - - pub fn take_message(&mut self) -> Option<(NodeId, NodeId, Message)> { - self.nodes.values() - .filter_map(|n| n.cluster.take_message().map(|m| (n.session.meta().self_node_id.clone(), m.0, m.1))) - .nth(0) - .or_else(|| self.queue.pop_front()) - } - - pub fn process_message(&mut self, msg: (NodeId, NodeId, Message)) -> Result<(), Error> { - match msg.2 { - Message::KeyVersionNegotiation(message) => - self.nodes[&msg.1].session.process_message(&msg.0, &message), - _ => panic!("unexpected"), - } - } - - pub fn run(&mut self) { - while let Some((from, to, message)) = self.take_message() { - self.process_message((from, to, message)).unwrap(); - } - } - } - - #[test] - fn negotiation_fails_if_initialized_twice() { - let ml = MessageLoop::empty(1); - assert_eq!(ml.session(0).initialize(BTreeSet::new()), Ok(())); - assert_eq!(ml.session(0).initialize(BTreeSet::new()), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn negotiation_fails_if_message_contains_wrong_nonce() { - let ml = MessageLoop::empty(2); - assert_eq!(ml.session(1).process_message(ml.node_id(0), &KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 100, - })), Err(Error::ReplayProtection)); - } - - #[test] - fn negotiation_fails_if_versions_request_received_from_non_master() { - let ml = MessageLoop::empty(3); - assert_eq!(ml.session(2).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - })), Err(Error::InvalidMessage)); - } - - #[test] - fn negotiation_fails_if_versions_request_received_twice() { - let ml = MessageLoop::empty(2); - assert_eq!(ml.session(1).process_message(ml.node_id(0), &KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - })), Ok(())); - assert_eq!(ml.session(1).process_message(ml.node_id(0), &KeyVersionNegotiationMessage::RequestKeyVersions(RequestKeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - })), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn negotiation_fails_if_versions_received_before_initialization() { - let ml = MessageLoop::empty(2); - assert_eq!(ml.session(1).process_message(ml.node_id(0), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - key_common: Some(CommonKeyData { - threshold: 10, - author: Default::default(), - public: Default::default(), - }), - versions: Vec::new(), - })), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn negotiation_does_not_fails_if_versions_received_after_completion() { - let ml = MessageLoop::empty(3); - ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); - assert_eq!(ml.session(0).data.lock().state, SessionState::WaitingForResponses); - - let version_id = (*math::generate_random_scalar().unwrap()).clone(); - assert_eq!(ml.session(0).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - key_common: Some(CommonKeyData { - threshold: 0, - author: Default::default(), - public: Default::default(), - }), - - versions: vec![version_id.clone().into()] - })), Ok(())); - assert_eq!(ml.session(0).data.lock().state, SessionState::Finished); - - assert_eq!(ml.session(0).process_message(ml.node_id(2), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - key_common: Some(CommonKeyData { - threshold: 0, - author: Default::default(), - public: Default::default(), - }), - - versions: vec![version_id.clone().into()] - })), Ok(())); - assert_eq!(ml.session(0).data.lock().state, SessionState::Finished); - } - - #[test] - fn negotiation_fails_if_wrong_common_data_sent() { - fn run_test(key_common: CommonKeyData) { - let ml = MessageLoop::empty(3); - ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); - - let version_id = (*math::generate_random_scalar().unwrap()).clone(); - assert_eq!(ml.session(0).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - key_common: Some(CommonKeyData { - threshold: 1, - author: Default::default(), - public: Default::default(), - }), - versions: vec![version_id.clone().into()] - })), Ok(())); - assert_eq!(ml.session(0).process_message(ml.node_id(2), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - key_common: Some(key_common), - versions: vec![version_id.clone().into()] - })), Err(Error::InvalidMessage)); - } - - run_test(CommonKeyData { - threshold: 2, - author: Default::default(), - public: Default::default(), - }); - - run_test(CommonKeyData { - threshold: 1, - author: H160::from_low_u64_be(1).into(), - public: Default::default(), - }); - - run_test(CommonKeyData { - threshold: 1, - author: H160::from_low_u64_be(2).into(), - public: Default::default(), - }); - } - - #[test] - fn negotiation_fails_if_threshold_empty_when_versions_are_not_empty() { - let ml = MessageLoop::empty(2); - ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); - - let version_id = (*math::generate_random_scalar().unwrap()).clone(); - assert_eq!(ml.session(0).process_message(ml.node_id(1), &KeyVersionNegotiationMessage::KeyVersions(KeyVersions { - session: Default::default(), - sub_session: math::generate_random_scalar().unwrap().into(), - session_nonce: 0, - key_common: None, - versions: vec![version_id.clone().into()] - })), Err(Error::InvalidMessage)); - } - - #[test] - fn fast_negotiation_does_not_completes_instantly_when_enough_share_owners_are_connected() { - let nodes = MessageLoop::prepare_nodes(2); - let version_id = (*math::generate_random_scalar().unwrap()).clone(); - nodes.values().nth(0).unwrap().insert(Default::default(), DocumentKeyShare { - author: H160::from_low_u64_be(2), - threshold: 1, - public: H512::from_low_u64_be(3), - common_point: None, - encrypted_point: None, - versions: vec![DocumentKeyShareVersion { - hash: version_id, - id_numbers: vec![(nodes.keys().cloned().nth(0).unwrap(), math::generate_random_scalar().unwrap())].into_iter().collect(), - secret_share: math::generate_random_scalar().unwrap(), - }], - }).unwrap(); - let ml = MessageLoop::new(nodes); - ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); - // we can't be sure that node has given key version because previous ShareAdd session could fail - assert!(ml.session(0).data.lock().state != SessionState::Finished); - - // check that upon completion, commmon key data is known - assert_eq!(ml.session(0).common_key_data(), Ok(DocumentKeyShare { - author: H160::from_low_u64_be(2), - threshold: 1, - public: H512::from_low_u64_be(3), - ..Default::default() - })); - } - - #[test] - fn fastest_computer_returns_missing_share_if_no_versions_returned() { - let computer = FastestResultComputer { - self_node_id: Default::default(), - threshold: None, - configured_nodes_count: 1, - connected_nodes_count: 1, - }; - assert_eq!(computer.compute_result(Some(10), &Default::default(), &Default::default()), Some(Err(Error::ServerKeyIsNotFound))); - } - - #[test] - fn largest_computer_returns_missing_share_if_no_versions_returned() { - let computer = LargestSupportResultComputer; - assert_eq!(computer.compute_result(Some(10), &Default::default(), &Default::default()), Some(Err(Error::ServerKeyIsNotFound))); - } - - #[test] - fn fatal_error_is_not_broadcasted_if_started_without_origin() { - let mut ml = MessageLoop::empty(3); - ml.session(0).set_continue_action(ContinueAction::Decrypt(create_default_decryption_session(), None, false, false)); - ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); - ml.run(); - - assert!(ml.nodes.values().all(|n| n.session.is_finished() && - n.session.take_failed_continue_action().is_none())); - } - - #[test] - fn fatal_error_is_broadcasted_if_started_with_origin() { - let mut ml = MessageLoop::empty(3); - ml.session(0).set_continue_action(ContinueAction::Decrypt(create_default_decryption_session(), Some(Address::from_low_u64_be(1)), true, true)); - ml.session(0).initialize(ml.nodes.keys().cloned().collect()).unwrap(); - ml.run(); - - // on all nodes session is completed - assert!(ml.nodes.values().all(|n| n.session.is_finished())); - - // slave nodes have non-empty failed continue action - assert!(ml.nodes.values().skip(1).all(|n| n.session.take_failed_continue_action() - == Some(FailedContinueAction::Decrypt(Some(Address::from_low_u64_be(1)), public_to_address(&H512::from_low_u64_be(2)))))); - } -} diff --git a/secret-store/src/key_server_cluster/admin_sessions/mod.rs b/secret-store/src/key_server_cluster/admin_sessions/mod.rs deleted file mode 100644 index c253f4436f5..00000000000 --- a/secret-store/src/key_server_cluster/admin_sessions/mod.rs +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -pub mod key_version_negotiation_session; -pub mod servers_set_change_session; -pub mod share_add_session; -pub mod share_change_session; - -mod sessions_queue; - -use key_server_cluster::{SessionId, NodeId, SessionMeta, Error}; - -/// Share change session metadata. -#[derive(Debug, Clone)] -pub struct ShareChangeSessionMeta { - /// Key id. - pub id: SessionId, - /// Id of node, which has started this session. - pub master_node_id: NodeId, - /// Id of node, on which this session is running. - pub self_node_id: NodeId, - /// Count of all configured key server nodes. - pub configured_nodes_count: usize, - /// Count of all connected key server nodes. - pub connected_nodes_count: usize, -} - -impl ShareChangeSessionMeta { - /// Convert to consensus session meta. `all_nodes_set` is the union of `old_nodes_set` && `new_nodes_set`. - pub fn into_consensus_meta(self, all_nodes_set_len: usize) -> Result { - Ok(SessionMeta { - id: self.id, - master_node_id: self.master_node_id, - self_node_id: self.self_node_id, - threshold: all_nodes_set_len.checked_sub(1).ok_or(Error::ConsensusUnreachable)?, - configured_nodes_count: self.configured_nodes_count, - connected_nodes_count: self.connected_nodes_count, - }) - } -} diff --git a/secret-store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs b/secret-store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs deleted file mode 100644 index 1e1f71918b9..00000000000 --- a/secret-store/src/key_server_cluster/admin_sessions/servers_set_change_session.rs +++ /dev/null @@ -1,1456 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeSet, BTreeMap}; -use std::collections::btree_map::Entry; -use futures::Oneshot; -use parking_lot::Mutex; -use ethereum_types::H256; -use crypto::publickey::{Public, Signature}; -use key_server_cluster::{Error, NodeId, SessionId, KeyStorage}; -use key_server_cluster::math; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{ClusterSession, CompletionSignal}; -use key_server_cluster::message::{Message, ServersSetChangeMessage, - ConsensusMessageWithServersSet, InitializeConsensusSessionWithServersSet, - ServersSetChangeConsensusMessage, ConfirmConsensusInitialization, UnknownSessionsRequest, UnknownSessions, - ServersSetChangeShareAddMessage, ServersSetChangeError, ServersSetChangeCompleted, - ServersSetChangeDelegate, ServersSetChangeDelegateResponse, InitializeShareChangeSession, - ConfirmShareChangeSessionInitialization, KeyVersionNegotiationMessage, ShareChangeKeyVersionNegotiation}; -use key_server_cluster::share_change_session::{ShareChangeSession, ShareChangeSessionParams, ShareChangeSessionPlan, - prepare_share_change_session_plan}; -use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSessionImpl, - SessionParams as KeyVersionNegotiationSessionParams, LargestSupportResultComputer, - SessionTransport as KeyVersionNegotiationTransport}; -use key_server_cluster::jobs::job_session::JobTransport; -use key_server_cluster::jobs::servers_set_change_access_job::{ServersSetChangeAccessJob, ServersSetChangeAccessRequest}; -use key_server_cluster::jobs::unknown_sessions_job::{UnknownSessionsJob}; -use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; -use key_server_cluster::admin_sessions::sessions_queue::SessionsQueue; -use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - -/// Maximal number of active share change sessions. -const MAX_ACTIVE_KEY_SESSIONS: usize = 64; - -/// Servers set change session. -/// Brief overview: -/// 1) consensus establishing -/// 2) master node requests all other nodes for sessions he is not participating (aka unknown sessions) -/// 3) every slave node responds with sessions id => we are able to collect Map of unknown sessions on master -/// 4) for every known session (i.e. session that master participates in): -/// 4.1) share change plan is created = nodes to add shares for, nodes to move shares from/to, nodes to remove shares from -/// 4.2) share change session is started. Share change session = sequential execution of ShareAdd, then ShareMove && then ShareRemove sessions (order matters here) for single key -/// 5) for every unknown session: -/// 5.1) sub_master is selected from sessions participants -/// 5.2) share change session is delegated from master to this sub_master -/// 5.3) share change session is executed by this sub_master -/// 5.4) share change confirm is sent from sub_master to master -/// 6) upon completing all known share change sessions && receiving confirmations for all unknown share change sessions, session completion signal is sent to all slave nodes && session is completed -pub struct SessionImpl { - /// Session core. - core: SessionCore, - /// Session data. - data: Mutex, -} - -/// Session state. -#[derive(Debug, PartialEq)] -enum SessionState { - /// Establishing consensus. - EstablishingConsensus, - /// Running share change sessions. - RunningShareChangeSessions, - /// Session is completed. - Finished, -} - -/// Immutable session data. -struct SessionCore { - /// Servers set change session meta (id is computed from new_nodes_set). - pub meta: ShareChangeSessionMeta, - /// Cluster which allows this node to send messages to other nodes in the cluster. - pub cluster: Arc, - /// Keys storage. - pub key_storage: Arc, - /// Session-level nonce. - pub nonce: u64, - /// All known nodes. - pub all_nodes_set: BTreeSet, - /// Administrator public key. - pub admin_public: Public, - /// Migration id (if this session is a part of auto-migration process). - pub migration_id: Option, - /// Session completion signal. - pub completed: CompletionSignal<()>, -} - -/// Servers set change consensus session type. -type ServersSetChangeConsensusSession = ConsensusSession; - -/// Mutable session data. -struct SessionData { - /// Session state. - pub state: SessionState, - /// Consensus-based servers set change session. - pub consensus_session: Option, - /// New nodes set. - pub new_nodes_set: Option>, - /// Share change sessions queue (valid on master nodes only). - pub sessions_queue: Option, - /// Share change sessions key version negotiation. - pub negotiation_sessions: BTreeMap>, - /// Share change sessions initialization state (valid on master nodes only). - pub sessions_initialization_state: BTreeMap, - /// Sessions delegated to other nodes (valid on master node only). - pub delegated_key_sessions: BTreeMap, - /// Active share change sessions. - pub active_key_sessions: BTreeMap, - /// Servers set change result. - pub result: Option>, -} - -/// Session initialization data. -struct SessionInitializationData { - /// Master node id. - pub master: NodeId, - /// Nodes that have confirmed session initialization request. - pub confirmations: BTreeSet, -} - -/// SessionImpl creation parameters -pub struct SessionParams { - /// Session meta (artificial). - pub meta: ShareChangeSessionMeta, - /// Cluster. - pub cluster: Arc, - /// Keys storage. - pub key_storage: Arc, - /// Session nonce. - pub nonce: u64, - /// All known nodes. - pub all_nodes_set: BTreeSet, - /// Administrator public key. - pub admin_public: Public, - /// Migration id (if this session is a part of auto-migration process). - pub migration_id: Option, -} - -/// Servers set change consensus transport. -struct ServersSetChangeConsensusTransport { - /// Session id. - id: SessionId, - /// Session-level nonce. - nonce: u64, - /// Migration id (if part of auto-migration process). - migration_id: Option, - /// Cluster. - cluster: Arc, -} - -/// Unknown sessions job transport. -struct UnknownSessionsJobTransport { - /// Session id. - id: SessionId, - /// Session-level nonce. - nonce: u64, - /// Cluster. - cluster: Arc, -} - -/// Key version negotiation transport. -struct ServersSetChangeKeyVersionNegotiationTransport { - /// Session id. - id: SessionId, - /// Session-level nonce. - nonce: u64, - /// Cluster. - cluster: Arc, -} - -impl SessionImpl { - /// Create new servers set change session. - pub fn new(params: SessionParams) -> Result<(Self, Oneshot>), Error> { - let (completed, oneshot) = CompletionSignal::new(); - Ok((SessionImpl { - core: SessionCore { - meta: params.meta, - cluster: params.cluster, - key_storage: params.key_storage, - nonce: params.nonce, - all_nodes_set: params.all_nodes_set, - admin_public: params.admin_public, - migration_id: params.migration_id, - completed, - }, - data: Mutex::new(SessionData { - state: SessionState::EstablishingConsensus, - consensus_session: None, - new_nodes_set: None, - sessions_queue: None, - negotiation_sessions: BTreeMap::new(), - sessions_initialization_state: BTreeMap::new(), - delegated_key_sessions: BTreeMap::new(), - active_key_sessions: BTreeMap::new(), - result: None, - }), - }, oneshot)) - } - - /// Get session id. - pub fn id(&self) -> &SessionId { - &self.core.meta.id - } - - /// Get migration id. - pub fn migration_id(&self) -> Option<&H256> { - self.core.migration_id.as_ref() - } - - /// Return session completion result (if available). - pub fn result(&self) -> Option> { - self.data.lock().result.clone() - } - - /// Initialize servers set change session on master node. - pub fn initialize(&self, new_nodes_set: BTreeSet, all_set_signature: Signature, new_set_signature: Signature) -> Result<(), Error> { - check_nodes_set(&self.core.all_nodes_set, &new_nodes_set)?; - - let mut data = self.data.lock(); - if data.state != SessionState::EstablishingConsensus || data.consensus_session.is_some() { - return Err(Error::InvalidStateForRequest); - } - - let mut consensus_session = ConsensusSession::new(ConsensusSessionParams { - meta: self.core.meta.clone().into_consensus_meta(self.core.all_nodes_set.len())?, - consensus_executor: ServersSetChangeAccessJob::new_on_master(self.core.admin_public.clone(), - self.core.all_nodes_set.clone(), - new_nodes_set.clone(), - all_set_signature, - new_set_signature), - consensus_transport: ServersSetChangeConsensusTransport { - id: self.core.meta.id.clone(), - nonce: self.core.nonce, - migration_id: self.core.migration_id.clone(), - cluster: self.core.cluster.clone(), - }, - })?; - - consensus_session.initialize(self.core.all_nodes_set.clone())?; - - let is_finished = consensus_session.state() == ConsensusSessionState::ConsensusEstablished; - data.consensus_session = Some(consensus_session); - data.new_nodes_set = Some(new_nodes_set); - - // this is the case when all other nodes are isolated - if is_finished { - Self::complete_session(&self.core, &mut *data)?; - } - - Ok(()) - } - - /// Process servers set change message. - pub fn process_message(&self, sender: &NodeId, message: &ServersSetChangeMessage) -> Result<(), Error> { - if self.core.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &ServersSetChangeMessage::ServersSetChangeConsensusMessage(ref message) => - self.on_consensus_message(sender, message), - &ServersSetChangeMessage::UnknownSessionsRequest(ref message) => - self.on_unknown_sessions_requested(sender, message), - &ServersSetChangeMessage::UnknownSessions(ref message) => - self.on_unknown_sessions(sender, message), - &ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ref message) => - self.on_key_version_negotiation(sender, message), - &ServersSetChangeMessage::InitializeShareChangeSession(ref message) => - self.on_initialize_share_change_session(sender, message), - &ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(ref message) => - self.on_share_change_session_confirmation(sender, message), - &ServersSetChangeMessage::ServersSetChangeDelegate(ref message) => - self.on_sessions_delegation(sender, message), - &ServersSetChangeMessage::ServersSetChangeDelegateResponse(ref message) => - self.on_delegated_session_completed(sender, message), - &ServersSetChangeMessage::ServersSetChangeShareAddMessage(ref message) => - self.on_share_add_message(sender, message), - &ServersSetChangeMessage::ServersSetChangeError(ref message) => { - self.on_session_error(sender, message.error.clone()); - Ok(()) - }, - &ServersSetChangeMessage::ServersSetChangeCompleted(ref message) => - self.on_session_completed(sender, message), - } - } - - /// When consensus-related message is received. - pub fn on_consensus_message(&self, sender: &NodeId, message: &ServersSetChangeConsensusMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::EstablishingConsensus { - return Err(Error::InvalidStateForRequest); - } - - // start slave consensus session if needed - if self.core.meta.self_node_id != self.core.meta.master_node_id { - if data.consensus_session.is_none() { - match &message.message { - &ConsensusMessageWithServersSet::InitializeConsensusSession(_) => { - data.consensus_session = Some(ConsensusSession::new(ConsensusSessionParams { - meta: self.core.meta.clone().into_consensus_meta(self.core.all_nodes_set.len())?, - consensus_executor: ServersSetChangeAccessJob::new_on_slave(self.core.admin_public.clone()), - consensus_transport: ServersSetChangeConsensusTransport { - id: self.core.meta.id.clone(), - nonce: self.core.nonce, - migration_id: self.core.migration_id.clone(), - cluster: self.core.cluster.clone(), - }, - })?); - }, - _ => return Err(Error::InvalidStateForRequest), - } - } - } - - // process consensus message - let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidMessage)?; - let is_establishing_consensus = consensus_session.state() == ConsensusSessionState::EstablishingConsensus; - match &message.message { - &ConsensusMessageWithServersSet::InitializeConsensusSession(ref message) => - consensus_session.on_consensus_partial_request(sender, ServersSetChangeAccessRequest::from(message))?, - &ConsensusMessageWithServersSet::ConfirmConsensusInitialization(ref message) => - consensus_session.on_consensus_partial_response(sender, message.is_confirmed)?, - } - - // when consensus is established => request unknown sessions - let is_consensus_established = consensus_session.state() == ConsensusSessionState::ConsensusEstablished; - if self.core.meta.self_node_id != self.core.meta.master_node_id || !is_establishing_consensus || !is_consensus_established { - return Ok(()); - } - - let unknown_sessions_job = UnknownSessionsJob::new_on_master(self.core.key_storage.clone(), self.core.meta.self_node_id.clone()); - consensus_session.disseminate_jobs(unknown_sessions_job, self.unknown_sessions_transport(), false).map(|_| ()) - } - - /// When unknown sessions are requested. - pub fn on_unknown_sessions_requested(&self, sender: &NodeId, message: &UnknownSessionsRequest) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - let new_nodes_set = { - let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidMessage)?; - let unknown_sessions_job = UnknownSessionsJob::new_on_slave(self.core.key_storage.clone()); - let unknown_sessions_transport = self.unknown_sessions_transport(); - - // and respond with unknown sessions - consensus_session.on_job_request(&sender, sender.clone(), unknown_sessions_job, unknown_sessions_transport)?; - - consensus_session.consensus_job().executor() - .new_servers_set() - .expect("consensus session is now completed; new_servers_set is intermediate result of consensus session; qed") - .clone() - }; - - // update state - data.state = SessionState::RunningShareChangeSessions; - data.new_nodes_set = Some(new_nodes_set); - - Ok(()) - } - - /// When unknown sessions are received. - pub fn on_unknown_sessions(&self, sender: &NodeId, message: &UnknownSessions) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::EstablishingConsensus { - return Err(Error::InvalidStateForRequest); - } - - // process message - let unknown_sessions = { - let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidMessage)?; - consensus_session.on_job_response(sender, message.unknown_sessions.iter().cloned().map(Into::into).collect())?; - if consensus_session.state() != ConsensusSessionState::Finished { - return Ok(()); - } - - // all nodes have reported their unknown sessions - // => we are ready to start adding/moving/removing shares - consensus_session.result()? - }; - - // initialize sessions queue - data.state = SessionState::RunningShareChangeSessions; - data.sessions_queue = Some(SessionsQueue::new(&self.core.key_storage, unknown_sessions.keys().cloned().collect())); - - // and disseminate session initialization requests - Self::disseminate_session_initialization_requests(&self.core, &mut *data) - } - - /// When key version negotiation message is received. - pub fn on_key_version_negotiation(&self, sender: &NodeId, message: &ShareChangeKeyVersionNegotiation) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::RunningShareChangeSessions { - return Err(Error::InvalidStateForRequest); - } - - // process message - match &message.message { - &KeyVersionNegotiationMessage::RequestKeyVersions(ref message) if sender == &self.core.meta.master_node_id => { - let key_id = message.session.clone().into(); - let key_share = self.core.key_storage.get(&key_id)?; - let (negotiation_session, _) = KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { - meta: ShareChangeSessionMeta { - id: key_id.clone(), - self_node_id: self.core.meta.self_node_id.clone(), - master_node_id: sender.clone(), - configured_nodes_count: self.core.meta.configured_nodes_count, - connected_nodes_count: self.core.meta.connected_nodes_count, - }, - sub_session: message.sub_session.clone().into(), - key_share: key_share, - result_computer: Arc::new(LargestSupportResultComputer {}), - transport: ServersSetChangeKeyVersionNegotiationTransport { - id: self.core.meta.id.clone(), - nonce: self.core.nonce, - cluster: self.core.cluster.clone(), - }, - nonce: message.session_nonce, - }); - negotiation_session.on_key_versions_request(sender, message)?; - debug_assert!(negotiation_session.is_finished()); - Ok(()) - }, - &KeyVersionNegotiationMessage::KeyVersions(ref message) if self.core.meta.self_node_id == self.core.meta.master_node_id => { - let key_id = message.session.clone().into(); - { - let negotiation_session = data.negotiation_sessions.get(&key_id).ok_or(Error::InvalidMessage)?; - negotiation_session.on_key_versions(sender, message)?; - if !negotiation_session.is_finished() { - return Ok(()); - } - } - - // else prepare plan && start share change session - if !Self::initialize_share_change_session(&self.core, &mut *data, key_id)? { - Self::disseminate_session_initialization_requests(&self.core, &mut *data)?; - } - - Ok(()) - }, - _ => Err(Error::InvalidMessage), - } - } - - /// When share change session initialization is requested. - pub fn on_initialize_share_change_session(&self, sender: &NodeId, message: &InitializeShareChangeSession) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // we only accept delegation requests from master node - if sender != &self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::RunningShareChangeSessions { - return Err(Error::InvalidStateForRequest); - } - - // insert new session - let key_id = message.key_id.clone().into(); - match data.active_key_sessions.contains_key(&key_id) { - true => return Err(Error::InvalidMessage), - false => { - let master_plan = ShareChangeSessionPlan { - key_version: message.version.clone().into(), - version_holders: message.version_holders.iter().cloned().map(Into::into).collect(), - consensus_group: message.consensus_group.iter().cloned().map(Into::into).collect(), - new_nodes_map: message.new_nodes_map.iter().map(|(k, v)| (k.clone().into(), v.clone().map(Into::into))).collect(), - }; - - // if master plan is empty, it is cheating - if master_plan.is_empty() { - return Err(Error::InvalidMessage); - } - - // on nodes, holding selected key share version, we could check if master node plan is correct - let master_node_id = message.master_node_id.clone().into(); - if let Some(key_share) = self.core.key_storage.get(&key_id)? { - let version = message.version.clone().into(); - let key_share_owners = message.version_holders.iter().cloned().map(Into::into).collect(); - let new_nodes_set = data.new_nodes_set.as_ref() - .expect("new_nodes_set is filled during consensus establishing; change sessions are running after this; qed"); - let local_plan = prepare_share_change_session_plan( - &self.core.all_nodes_set, - key_share.threshold, - &key_id, - version, - &master_node_id, - &key_share_owners, - new_nodes_set)?; - - if local_plan.new_nodes_map.keys().collect::>() != master_plan.new_nodes_map.keys().collect::>() { - return Err(Error::InvalidMessage); - } - } - - let session = Self::create_share_change_session(&self.core, key_id, master_node_id, master_plan)?; - if !session.is_finished() { - data.active_key_sessions.insert(key_id.clone(), session); - } - }, - }; - - // send confirmation - self.core.cluster.send(sender, Message::ServersSetChange(ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(ConfirmShareChangeSessionInitialization { - session: message.session.clone(), - session_nonce: message.session_nonce.clone(), - key_id: message.key_id.clone(), - }))) - } - - /// When share change session initialization is confirmed. - pub fn on_share_change_session_confirmation(&self, sender: &NodeId, message: &ConfirmShareChangeSessionInitialization) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // we only accept delegation requests from master node - if self.core.meta.self_node_id != self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::RunningShareChangeSessions { - return Err(Error::InvalidStateForRequest); - } - - // add confirmation - let key_id = message.key_id.clone().into(); - let session_master = { - let session_init_data = data.sessions_initialization_state.get_mut(&key_id).ok_or(Error::InvalidMessage)?; - if !session_init_data.confirmations.remove(sender) { - return Err(Error::InvalidMessage); - } - - if !session_init_data.confirmations.is_empty() { - return Ok(()); - } - - session_init_data.master.clone() - }; - - // and start/delegate session if required - data.sessions_initialization_state.remove(&key_id); - if self.core.meta.self_node_id != session_master { - data.delegated_key_sessions.insert(key_id, session_master.clone()); - return self.core.cluster.send(&session_master, Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeDelegate(ServersSetChangeDelegate { - session: self.core.meta.id.clone().into(), - session_nonce: self.core.nonce, - key_id: key_id.into(), - }))); - } - - // initialize share change session - { - let key_session = data.active_key_sessions.get_mut(&key_id).ok_or(Error::InvalidMessage)?; - key_session.initialize()?; - if !key_session.is_finished() { - return Ok(()); - } - } - - // complete key session - Self::complete_key_session(&self.core, &mut *data, true, key_id) - } - - /// When sessions execution is delegated to this node. - pub fn on_sessions_delegation(&self, sender: &NodeId, message: &ServersSetChangeDelegate) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // we only accept delegation requests from master node - if sender != &self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::RunningShareChangeSessions { - return Err(Error::InvalidStateForRequest); - } - - // start session - let key_session = data.active_key_sessions.get_mut(&message.key_id.clone().into()).ok_or(Error::InvalidMessage)?; - key_session.initialize() - } - - /// When delegated session execution is completed. - pub fn on_delegated_session_completed(&self, sender: &NodeId, message: &ServersSetChangeDelegateResponse) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // we only accept delegation requests on master node - if self.core.meta.self_node_id != self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - // check state - let mut data = self.data.lock(); - if data.state != SessionState::RunningShareChangeSessions { - return Err(Error::InvalidStateForRequest); - } - - // forget delegated session - let key_id = message.key_id.clone().into(); - match data.delegated_key_sessions.entry(key_id) { - Entry::Occupied(entry) => if entry.get() == sender { - entry.remove() - } else { - return Err(Error::InvalidMessage); - }, - _ => return Err(Error::InvalidMessage), - }; - - // check if we need to complete the whole change session - Self::disseminate_session_initialization_requests(&self.core, &mut *data) - } - - /// When share add message is received. - pub fn on_share_add_message(&self, sender: &NodeId, message: &ServersSetChangeShareAddMessage) -> Result<(), Error> { - self.on_share_change_message(message.message.session_id().clone().into(), |session| - session.on_share_add_message(sender, &message.message)) - } - - /// When session completion message is received. - pub fn on_session_completed(&self, sender: &NodeId, message: &ServersSetChangeCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - if sender != &self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - let mut data = self.data.lock(); - data.result = Some(Ok(())); - if data.active_key_sessions.len() != 0 { - return Err(Error::TooEarlyForRequest); - } - - // if we are on the set of nodes that are being removed from the cluster, let's clear database - if !data.new_nodes_set.as_ref() - .expect("new_nodes_set is filled during initialization; session is completed after initialization; qed") - .contains(&self.core.meta.self_node_id) { - self.core.key_storage.clear()?; - } - - data.state = SessionState::Finished; - self.core.completed.send(Ok(())); - - Ok(()) - } - - /// Create unknown sessions transport. - fn unknown_sessions_transport(&self) -> UnknownSessionsJobTransport { - UnknownSessionsJobTransport { - id: self.core.meta.id.clone(), - nonce: self.core.nonce, - cluster: self.core.cluster.clone(), - } - } - - /// When share change message is received. - fn on_share_change_message Result<(), Error>>(&self, session_id: SessionId, message_processor: F) -> Result<(), Error> { - // check state - let mut data = self.data.lock(); - if data.state != SessionState::RunningShareChangeSessions { - return Err(Error::InvalidStateForRequest); - } - - // process message - let (is_finished, is_master) = { - let key_session = data.active_key_sessions.get_mut(&session_id).ok_or(Error::InvalidMessage)?; - message_processor(key_session)?; - (key_session.is_finished(), key_session.is_master()) - }; - - if is_finished { - Self::complete_key_session(&self.core, &mut *data, is_master, session_id)?; - } - - Ok(()) - } - - /// Create share change session. - fn create_share_change_session(core: &SessionCore, key_id: SessionId, master_node_id: NodeId, session_plan: ShareChangeSessionPlan) -> Result { - ShareChangeSession::new(ShareChangeSessionParams { - session_id: core.meta.id.clone(), - nonce: core.nonce, - meta: ShareChangeSessionMeta { - id: key_id, - self_node_id: core.meta.self_node_id.clone(), - master_node_id: master_node_id, - configured_nodes_count: core.meta.configured_nodes_count, - connected_nodes_count: core.meta.connected_nodes_count, - }, - cluster: core.cluster.clone(), - key_storage: core.key_storage.clone(), - plan: session_plan, - }) - } - - /// Disseminate session initialization requests. - fn disseminate_session_initialization_requests(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { - debug_assert_eq!(core.meta.self_node_id, core.meta.master_node_id); - if data.sessions_queue.is_some() { - let number_of_sessions_active = data.active_key_sessions.len() - + data.delegated_key_sessions.len() - + data.negotiation_sessions.len(); - let mut number_of_sessions_to_start = MAX_ACTIVE_KEY_SESSIONS.saturating_sub(number_of_sessions_active); - while number_of_sessions_to_start > 0 { - let key_id = match data.sessions_queue.as_mut().expect("checked before beginning of the loop; qed").next() { - None => break, // complete session - Some(Err(e)) => return Err(e), - Some(Ok(key_id)) => key_id, - }; - - let key_share = core.key_storage.get(&key_id)?; - let (negotiation_session, _) = KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { - meta: ShareChangeSessionMeta { - id: key_id, - self_node_id: core.meta.self_node_id.clone(), - master_node_id: core.meta.self_node_id.clone(), - configured_nodes_count: core.meta.configured_nodes_count, - connected_nodes_count: core.meta.connected_nodes_count, - }, - sub_session: math::generate_random_scalar()?, - key_share: key_share, - result_computer: Arc::new(LargestSupportResultComputer {}), // TODO [Opt]: could use modified Fast version - transport: ServersSetChangeKeyVersionNegotiationTransport { - id: core.meta.id.clone(), - nonce: core.nonce, - cluster: core.cluster.clone(), - }, - nonce: 0, - }); - negotiation_session.initialize(core.cluster.nodes())?; - if !negotiation_session.is_finished() { - data.negotiation_sessions.insert(key_id, negotiation_session); - continue; - } - - if !Self::initialize_share_change_session(core, data, key_id)? { - continue; - } - - number_of_sessions_to_start = number_of_sessions_to_start - 1; - } - - // if iteration is not yet finished => return - if number_of_sessions_to_start == 0 { - return Ok(()); - } - } - - // iteration is finished => complete session - if data.state != SessionState::Finished { - data.sessions_queue = None; - if data.active_key_sessions.len() == 0 && - data.delegated_key_sessions.len() == 0 && - data.negotiation_sessions.len() == 0 { - Self::complete_session(core, data)?; - } - } - - Ok(()) - } - - /// Initialize share change session. - fn initialize_share_change_session(core: &SessionCore, data: &mut SessionData, key_id: SessionId) -> Result { - // get selected version && old nodes set from key negotiation session - let negotiation_session = data.negotiation_sessions.remove(&key_id) - .expect("share change session is only initialized when negotiation is completed; qed"); - let (selected_version, selected_master) = negotiation_session - .result() - .expect("share change session is only initialized when negotiation is completed; qed")? - .expect("initialize_share_change_session is only called on share change master; negotiation session completes with some on master; qed"); - let selected_version_holders = negotiation_session.version_holders(&selected_version)?; - let selected_version_threshold = negotiation_session.common_key_data()?.threshold; - - // prepare session change plan && check if something needs to be changed - let old_nodes_set = selected_version_holders; - let new_nodes_set = data.new_nodes_set.as_ref() - .expect("this method is called after consensus estabished; new_nodes_set is a result of consensus session; qed"); - let session_plan = prepare_share_change_session_plan(&core.all_nodes_set, - selected_version_threshold, - &key_id, - selected_version.clone(), - &selected_master, - &old_nodes_set, - new_nodes_set)?; - if session_plan.is_empty() { - return Ok(false); - } - - // send key session initialization requests - let mut confirmations: BTreeSet<_> = session_plan.new_nodes_map.keys().cloned().collect(); - let need_create_session = confirmations.remove(&core.meta.self_node_id); - let initialization_message = Message::ServersSetChange(ServersSetChangeMessage::InitializeShareChangeSession(InitializeShareChangeSession { - session: core.meta.id.clone().into(), - session_nonce: core.nonce, - key_id: key_id.clone().into(), - version: selected_version.into(), - version_holders: old_nodes_set.iter().cloned().map(Into::into).collect(), - master_node_id: selected_master.clone().into(), - consensus_group: session_plan.consensus_group.iter().cloned().map(Into::into).collect(), - new_nodes_map: session_plan.new_nodes_map.iter() - .map(|(n, nid)| (n.clone().into(), nid.clone().map(Into::into))) - .collect(), - })); - for node in &confirmations { - core.cluster.send(&node, initialization_message.clone())?; - } - - // create session on this node if required - if need_create_session { - data.active_key_sessions.insert(key_id.clone(), Self::create_share_change_session(core, key_id, - selected_master.clone(), - session_plan)?); - } - - // initialize session if required - let wait_for_confirmations = !confirmations.is_empty(); - if !wait_for_confirmations { - data.active_key_sessions.get_mut(&key_id) - .expect("!wait_for_confirmations is true only if this is the only session participant; if this is session participant, session is created above; qed") - .initialize()?; - } else { - data.sessions_initialization_state.insert(key_id, SessionInitializationData { - master: selected_master, - confirmations: confirmations, - }); - } - - Ok(true) - } - - /// Return delegated session to master. - fn return_delegated_session(core: &SessionCore, key_id: &SessionId) -> Result<(), Error> { - assert!(core.meta.self_node_id != core.meta.master_node_id); - core.cluster.send(&core.meta.master_node_id, Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeDelegateResponse(ServersSetChangeDelegateResponse { - session: core.meta.id.clone().into(), - session_nonce: core.nonce, - key_id: key_id.clone().into(), - }))) - } - - /// Complete key session. - fn complete_key_session(core: &SessionCore, data: &mut SessionData, is_master: bool, session_id: SessionId) -> Result<(), Error> { - data.active_key_sessions.remove(&session_id); - let is_general_master = core.meta.self_node_id == core.meta.master_node_id; - if is_master && !is_general_master { - Self::return_delegated_session(core, &session_id)?; - } - if is_general_master { - Self::disseminate_session_initialization_requests(core, data)?; - } - - if data.result.is_some() && data.active_key_sessions.len() == 0 { - data.state = SessionState::Finished; - core.completed.send(Ok(())); - } - - Ok(()) - } - - /// Complete servers set change session. - fn complete_session(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { - debug_assert_eq!(core.meta.self_node_id, core.meta.master_node_id); - - // send completion notification - core.cluster.broadcast(Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeCompleted(ServersSetChangeCompleted { - session: core.meta.id.clone().into(), - session_nonce: core.nonce, - })))?; - - // if we are on the set of nodes that are being removed from the cluster, let's clear database - if !data.new_nodes_set.as_ref() - .expect("new_nodes_set is filled during initialization; session is completed after initialization; qed") - .contains(&core.meta.self_node_id) { - core.key_storage.clear()?; - } - - data.state = SessionState::Finished; - data.result = Some(Ok(())); - core.completed.send(Ok(())); - - Ok(()) - } -} - -impl ClusterSession for SessionImpl { - type Id = SessionId; - type CreationData = (); // never used directly - type SuccessfulResult = (); - - fn type_name() -> &'static str { - "servers set change" - } - - fn id(&self) -> SessionId { - self.core.meta.id.clone() - } - - fn is_finished(&self) -> bool { - self.data.lock().state == SessionState::Finished - } - - fn on_session_timeout(&self) { - self.on_session_error(&self.core.meta.self_node_id, Error::NodeDisconnected); - } - - fn on_node_timeout(&self, node: &NodeId) { - self.on_session_error(node, Error::NodeDisconnected); - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - // error in generation session is considered fatal - // => broadcast error if error occured on this node - if *node == self.core.meta.self_node_id { - // do not bother processing send error, as we already processing error - let _ = self.core.cluster.broadcast(Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeError(ServersSetChangeError { - session: self.core.meta.id.clone().into(), - session_nonce: self.core.nonce, - error: error.clone().into(), - }))); - } - - let mut data = self.data.lock(); - - warn!(target: "secretstore_net", "{}: servers set change session failed: {} on {}", - self.core.meta.self_node_id, error, node); - - data.state = SessionState::Finished; - data.result = Some(Err(error.clone())); - self.core.completed.send(Err(error)); - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::ServersSetChange(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl JobTransport for ServersSetChangeConsensusTransport { - type PartialJobRequest=ServersSetChangeAccessRequest; - type PartialJobResponse=bool; - - fn send_partial_request(&self, node: &NodeId, request: ServersSetChangeAccessRequest) -> Result<(), Error> { - self.cluster.send(node, Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(ServersSetChangeConsensusMessage { - session: self.id.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessageWithServersSet::InitializeConsensusSession(InitializeConsensusSessionWithServersSet { - migration_id: self.migration_id.clone().map(Into::into), - old_nodes_set: request.old_servers_set.into_iter().map(Into::into).collect(), - new_nodes_set: request.new_servers_set.into_iter().map(Into::into).collect(), - old_set_signature: request.old_set_signature.into(), - new_set_signature: request.new_set_signature.into(), - }), - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { - self.cluster.send(node, Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(ServersSetChangeConsensusMessage { - session: self.id.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessageWithServersSet::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: response, - }), - }))) - } -} - -impl JobTransport for UnknownSessionsJobTransport { - type PartialJobRequest=NodeId; - type PartialJobResponse=BTreeSet; - - fn send_partial_request(&self, node: &NodeId, _request: NodeId) -> Result<(), Error> { - self.cluster.send(node, Message::ServersSetChange(ServersSetChangeMessage::UnknownSessionsRequest(UnknownSessionsRequest { - session: self.id.clone().into(), - session_nonce: self.nonce, - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: BTreeSet) -> Result<(), Error> { - self.cluster.send(node, Message::ServersSetChange(ServersSetChangeMessage::UnknownSessions(UnknownSessions { - session: self.id.clone().into(), - session_nonce: self.nonce, - unknown_sessions: response.into_iter().map(Into::into).collect(), - }))) - } -} - -impl KeyVersionNegotiationTransport for ServersSetChangeKeyVersionNegotiationTransport { - fn broadcast(&self, message: KeyVersionNegotiationMessage) -> Result<(), Error> { - self.cluster.broadcast(Message::ServersSetChange(ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ShareChangeKeyVersionNegotiation { - session: self.id.clone().into(), - session_nonce: self.nonce, - message: message, - }))) - } - - fn send(&self, node: &NodeId, message: KeyVersionNegotiationMessage) -> Result<(), Error> { - self.cluster.send(node, Message::ServersSetChange(ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ShareChangeKeyVersionNegotiation { - session: self.id.clone().into(), - session_nonce: self.nonce, - message: message, - }))) - } -} - -fn check_nodes_set(all_nodes_set: &BTreeSet, new_nodes_set: &BTreeSet) -> Result<(), Error> { - // all_nodes_set is the set of nodes we're currently connected to (and configured for) - match new_nodes_set.iter().any(|n| !all_nodes_set.contains(n)) { - true => Err(Error::NodeDisconnected), - false => Ok(()) - } -} - -#[cfg(test)] -pub mod tests { - use std::sync::Arc; - use std::collections::{VecDeque, BTreeMap, BTreeSet}; - use ethereum_types::H256; - use crypto::publickey::{Random, Generator, Public, Signature, KeyPair, sign}; - use blockchain::SigningKeyPair; - use key_server_cluster::{NodeId, SessionId, Error, KeyStorage, PlainNodeKeyPair}; - use key_server_cluster::cluster_sessions::ClusterSession; - use key_server_cluster::cluster::tests::MessageLoop as ClusterMessageLoop; - use key_server_cluster::generation_session::tests::{MessageLoop as GenerationMessageLoop}; - use key_server_cluster::math; - use key_server_cluster::message::Message; - use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - use key_server_cluster::jobs::servers_set_change_access_job::ordered_nodes_hash; - use super::{SessionImpl, SessionParams}; - - pub trait AdminSessionAdapter { - const SIGN_NEW_NODES: bool; - - fn create( - meta: ShareChangeSessionMeta, - admin_public: Public, - all_nodes_set: BTreeSet, - ml: &ClusterMessageLoop, - idx: usize - ) -> S; - } - - pub struct MessageLoop { - pub ml: ClusterMessageLoop, - pub admin_key_pair: KeyPair, - pub original_key_pair: KeyPair, - pub original_key_version: H256, - pub all_nodes_set: BTreeSet, - pub new_nodes_set: BTreeSet, - pub all_set_signature: Signature, - pub new_set_signature: Signature, - pub sessions: BTreeMap, - pub queue: VecDeque<(NodeId, NodeId, Message)>, - } - - impl ::std::fmt::Debug for MessageLoop { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "{:?}", self.ml) - } - } - - struct Adapter; - - impl AdminSessionAdapter for Adapter { - const SIGN_NEW_NODES: bool = true; - - fn create( - mut meta: ShareChangeSessionMeta, - admin_public: Public, - all_nodes_set: BTreeSet, - ml: &ClusterMessageLoop, - idx: usize - ) -> SessionImpl { - meta.self_node_id = *ml.node_key_pair(idx).public(); - SessionImpl::new(SessionParams { - meta: meta, - all_nodes_set: all_nodes_set, - cluster: ml.cluster(idx).view().unwrap(), - key_storage: ml.key_storage(idx).clone(), - nonce: 1, - admin_public: admin_public, - migration_id: None, - }).unwrap().0 - } - } - - impl MessageLoop { - pub fn with_gml>( - gml: GenerationMessageLoop, - master: NodeId, - add: Option>, - removed_nodes_ids: Option>, - isolated_nodes_ids: Option>, - ) -> Self { - // read generated key data - let original_key_pair = gml.compute_key_pair(); - let original_key_version = gml.key_version(); - Self::with_ml::( - gml.0, - original_key_pair, - original_key_version, - master, - add, - removed_nodes_ids, - isolated_nodes_ids) - } - - pub fn and_then>( - self, - master: NodeId, - add: Option>, - removed_nodes_ids: Option>, - isolated_nodes_ids: Option>, - ) -> Self { - Self::with_ml::( - self.ml, - self.original_key_pair, - self.original_key_version, - master, - add, - removed_nodes_ids, - isolated_nodes_ids, - ) - } - - pub fn with_ml>( - mut ml: ClusterMessageLoop, - original_key_pair: KeyPair, - original_key_version: H256, - master: NodeId, - add: Option>, - removed_nodes_ids: Option>, - isolated_nodes_ids: Option>, - ) -> Self { - let add = add.unwrap_or_default(); - let removed_nodes_ids = removed_nodes_ids.unwrap_or_default(); - let isolated_nodes_ids = isolated_nodes_ids.unwrap_or_default(); - - // generate admin key pair - let admin_key_pair = Random.generate().unwrap(); - let admin_public = admin_key_pair.public().clone(); - - // all active nodes set - let mut all_nodes_set: BTreeSet<_> = ml.nodes().into_iter() - .filter(|n| !isolated_nodes_ids.contains(n)) - .collect(); - // new nodes set includes all old nodes, except nodes being removed + all nodes being added - let new_nodes_set: BTreeSet = all_nodes_set.iter().cloned() - .chain(add.iter().map(|kp| *kp.public())) - .filter(|n| !removed_nodes_ids.contains(n)) - .collect(); - let mut old_set_to_sign = all_nodes_set.clone(); - all_nodes_set.extend(add.iter().map(|kp| *kp.public())); - if C::SIGN_NEW_NODES { - old_set_to_sign.extend(add.iter().map(|kp| *kp.public())); - } - for isolated_node_id in &isolated_nodes_ids { - all_nodes_set.remove(isolated_node_id); - } - - let meta = ShareChangeSessionMeta { - self_node_id: master, - master_node_id: master, - id: SessionId::default(), - configured_nodes_count: all_nodes_set.len(), - connected_nodes_count: all_nodes_set.len(), - }; - - // include new nodes in the cluster - for node_key_pair in &add { - ml.include(Arc::new(PlainNodeKeyPair::new(node_key_pair.clone()))); - } - // isolate nodes from the cluster - for isolated_node_id in &isolated_nodes_ids { - let idx = ml.nodes().iter().position(|n| n == isolated_node_id).unwrap(); - ml.exclude(idx); - } - - // prepare set of nodes - let sessions: BTreeMap<_, _> = (0..ml.nodes().len()) - .map(|idx| (ml.node(idx), C::create(meta.clone(), admin_public, all_nodes_set.clone(), &ml, idx))) - .collect(); - - let all_set_signature = sign(admin_key_pair.secret(), &ordered_nodes_hash(&old_set_to_sign)).unwrap(); - let new_set_signature = sign(admin_key_pair.secret(), &ordered_nodes_hash(&new_nodes_set)).unwrap(); - - MessageLoop { - ml, - admin_key_pair: admin_key_pair, - original_key_pair, - original_key_version, - all_nodes_set: all_nodes_set.clone(), - new_nodes_set: new_nodes_set, - all_set_signature: all_set_signature, - new_set_signature: new_set_signature, - sessions, - queue: Default::default(), - } - } - - pub fn run(&mut self) { - // run session until completion - while let Some((from, to, message)) = self.take_message() { - self.process_message((from, to, message)).unwrap(); - } - - // check that all sessions have finished - assert!(self.sessions.values().all(|s| s.is_finished())); - } - - pub fn take_message(&mut self) -> Option<(NodeId, NodeId, Message)> { - self.ml.take_message().or_else(|| self.queue.pop_front()) - } - - pub fn process_message(&mut self, msg: (NodeId, NodeId, Message)) -> Result<(), Error> { - match self.sessions[&msg.1].on_message(&msg.0, &msg.2) { - Ok(_) => Ok(()), - Err(Error::TooEarlyForRequest) => { - self.queue.push_back(msg); - Ok(()) - }, - Err(err) => Err(err), - } - } - - /// This only works for schemes where threshold = 1 - pub fn check_secret_is_preserved<'a, I: IntoIterator>(&self, nodes: I) { - let nodes: Vec<_> = nodes.into_iter().collect(); - let key_storages: Vec<_> = nodes.iter().map(|n| self.ml.key_storage_of(n)).collect(); - let n = nodes.len(); - let document_secret_plain = math::generate_random_point().unwrap(); - for n1 in 0..n { - for n2 in n1+1..n { - let share1 = key_storages[n1].get(&SessionId::default()).unwrap(); - let share2 = key_storages[n2].get(&SessionId::default()).unwrap(); - - let id_number1 = share1.as_ref().unwrap().last_version().unwrap().id_numbers[nodes[n1]].clone(); - let id_number2 = share1.as_ref().unwrap().last_version().unwrap().id_numbers[nodes[n2]].clone(); - // now encrypt and decrypt data - let (document_secret_decrypted, document_secret_decrypted_test) = - math::tests::do_encryption_and_decryption(1, - self.original_key_pair.public(), - &[id_number1, id_number2], - &[share1.unwrap().last_version().unwrap().secret_share.clone(), - share2.unwrap().last_version().unwrap().secret_share.clone()], - Some(self.original_key_pair.secret()), - document_secret_plain.clone()); - - assert_eq!(document_secret_plain, document_secret_decrypted_test); - assert_eq!(document_secret_plain, document_secret_decrypted); - } - } - } - } - - impl MessageLoop { - pub fn run_at(mut self, master: NodeId) -> Self { - self.sessions[&master].initialize( - self.new_nodes_set.clone(), - self.all_set_signature.clone(), - self.new_set_signature.clone()).unwrap(); - self.run(); - self - } - } - - pub fn generate_key(num_nodes: usize, threshold: usize) -> GenerationMessageLoop { - let gml = GenerationMessageLoop::new(num_nodes).init(threshold).unwrap(); - gml.0.loop_until(|| gml.0.is_empty()); - gml - } - - #[test] - fn node_added_using_servers_set_change() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // add 1 node so that it becames 2-of-4 session - let add = vec![Random.generate().unwrap()]; - let master = gml.0.node(0); - let ml = MessageLoop::with_gml::(gml, master, Some(add), None, None).run_at(master); - - // try to recover secret for every possible combination of nodes && check that secret is the same - ml.check_secret_is_preserved(ml.sessions.keys()); - } - - #[test] - fn node_added_using_server_set_change_from_this_node() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // insert 1 node so that it becames 2-of-4 session - // master node is the node we are adding => - // 1) add session is delegated to one of old nodes - // 2) key share is pushed to new node - // 3) delegated session is returned back to added node - let add = vec![Random.generate().unwrap()]; - let master = add[0].public().clone(); - let ml = MessageLoop::with_gml::(gml, master, Some(add), None, None).run_at(master); - - // try to recover secret for every possible combination of nodes && check that secret is the same - ml.check_secret_is_preserved(ml.sessions.keys()); - } - - #[test] - fn node_moved_using_servers_set_change() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // remove 1 node && insert 1 node so that one share is moved - let master = gml.0.node(0); - let remove: BTreeSet<_> = ::std::iter::once(gml.0.node(1)).collect(); - let add = vec![Random.generate().unwrap()]; - let ml = MessageLoop::with_gml::(gml, master, Some(add), Some(remove.clone()), None).run_at(master); - - // check that secret is still the same as before moving the share - ml.check_secret_is_preserved(ml.sessions.keys() - .filter(|k| !remove.contains(k))); - - // check that all removed nodes do not own key share - assert!(ml.sessions.keys().filter(|k| remove.contains(k)) - .all(|k| ml.ml.key_storage_of(k).get(&SessionId::default()).unwrap().is_none())); - } - - #[test] - fn node_removed_using_servers_set_change() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // remove 1 node so that session becames 2-of-2 - let remove: BTreeSet<_> = ::std::iter::once(gml.0.node(0)).collect(); - let master = gml.0.node(0); - let ml = MessageLoop::with_gml::(gml, master, None, Some(remove.clone()), None).run_at(master); - - // try to recover secret for every possible combination of nodes && check that secret is the same - ml.check_secret_is_preserved(ml.sessions.keys() - .filter(|k| !remove.contains(k))); - - // check that all removed nodes do not own key share - assert!(ml.sessions.keys().filter(|k| remove.contains(k)) - .all(|k| ml.ml.key_storage_of(k).get(&SessionId::default()).unwrap().is_none())); - } - - #[test] - fn isolated_node_removed_using_servers_set_change() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // remove 1 node so that session becames 2-of-2 - let isolate: BTreeSet<_> = ::std::iter::once(gml.0.node(1)).collect(); - let master = gml.0.node(0); - let ml = MessageLoop::with_gml::(gml, master, None, None, Some(isolate.clone())) - .run_at(master); - - // try to recover secret for every possible combination of nodes && check that secret is the same - ml.check_secret_is_preserved(ml.sessions.keys() - .filter(|k| !isolate.contains(k))); - - // check that all isolated nodes still OWN key share - assert!(ml.sessions.keys().filter(|k| isolate.contains(k)) - .all(|k| ml.ml.key_storage_of(k).get(&SessionId::default()).unwrap().is_some())); - } - - #[test] - fn having_less_than_required_nodes_after_change_does_not_fail_change_session() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // remove 2 nodes so that key becomes irrecoverable (make sure the session is completed - // even though key is irrecoverable) - let remove: BTreeSet<_> = gml.0.nodes().into_iter().skip(1).take(2).collect(); - let master = gml.0.node(0); - let ml = MessageLoop::with_gml::(gml, master, None, Some(remove.clone()), None).run_at(master); - - // check that all removed nodes do not own key share - assert!(ml.sessions.keys().filter(|k| remove.contains(k)) - .all(|k| ml.ml.key_storage_of(k).get(&SessionId::default()).unwrap().is_none())); - - // and now let's add new node (make sure the session is completed, even though key is still irrecoverable) - // isolated here are not actually isolated, but removed on the previous step - let add = vec![Random.generate().unwrap()]; - let master = add[0].public().clone(); - let ml = ml.and_then::(master, Some(add.clone()), None, Some(remove)).run_at(master); - - // check that all added nodes do not own key share (there's not enough nodes to run share add session) - assert!(ml.sessions.keys().filter(|k| add.iter().any(|n| n.public() == *k)) - .all(|k| ml.ml.key_storage_of(k).get(&SessionId::default()).unwrap().is_none())); - } - - #[test] - fn removing_node_from_cluster_of_2_works() { - // initial 2-of-2 session - let gml = generate_key(2, 1); - - // make 2nd node isolated so that key becomes irrecoverable (make sure the session is completed, - // even though key is irrecoverable) - let isolate: BTreeSet<_> = gml.0.nodes().into_iter().skip(1).take(1).collect(); - let master = gml.0.node(0); - MessageLoop::with_gml::(gml, master, None, None, Some(isolate)).run_at(master); - } - - #[test] - fn adding_node_that_has_lost_its_database_works() { - // initial 2-of-2 session - let gml = generate_key(2, 1); - - // insert 1 node so that it becames 2-of-3 session - let add = vec![Random.generate().unwrap()]; - let master = gml.0.node(0); - let ml = MessageLoop::with_gml::(gml, master, Some(add.clone()), None, None) - .run_at(master); - - // now let's say new node has lost its db and we're trying to join it again - ml.ml.key_storage_of(add[0].public()).clear().unwrap(); - - // this time old nodes have version, where new node is mentioned, but it doesn't report it when negotiating - let ml = ml.and_then::(master, Some(add), None, None).run_at(master); - - // try to recover secret for every possible combination of nodes && check that secret is the same - ml.check_secret_is_preserved(ml.sessions.keys()); - } -} diff --git a/secret-store/src/key_server_cluster/admin_sessions/sessions_queue.rs b/secret-store/src/key_server_cluster/admin_sessions/sessions_queue.rs deleted file mode 100644 index 448f5bdd82c..00000000000 --- a/secret-store/src/key_server_cluster/admin_sessions/sessions_queue.rs +++ /dev/null @@ -1,56 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{VecDeque, BTreeSet}; -use key_server_cluster::{Error, SessionId, KeyStorage}; - -/// Queue of share change sessions. -pub struct SessionsQueue { - /// Sessions, known on this node. - known_sessions: VecDeque, - /// Unknown sessions. - unknown_sessions: VecDeque, -} - -impl SessionsQueue { - /// Create new sessions queue. - pub fn new(key_storage: &Arc, unknown_sessions: BTreeSet) -> Self { - // TODO [Opt]: - // 1) known sessions - change to iter - // 2) unknown sesions - request chunk-by-chunk - SessionsQueue { - known_sessions: key_storage.iter().map(|(k, _)| k).collect(), - unknown_sessions: unknown_sessions.into_iter().collect(), - } - } -} - -impl Iterator for SessionsQueue { - type Item = Result; - - fn next(&mut self) -> Option { - if let Some(known_session) = self.known_sessions.pop_front() { - return Some(Ok(known_session)); - } - - if let Some(unknown_session) = self.unknown_sessions.pop_front() { - return Some(Ok(unknown_session)); - } - - None - } -} diff --git a/secret-store/src/key_server_cluster/admin_sessions/share_add_session.rs b/secret-store/src/key_server_cluster/admin_sessions/share_add_session.rs deleted file mode 100644 index 1386b8d39ff..00000000000 --- a/secret-store/src/key_server_cluster/admin_sessions/share_add_session.rs +++ /dev/null @@ -1,1113 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeSet, BTreeMap}; -use ethereum_types::{H256, Address}; -use crypto::publickey::{Public, Secret, Signature}; -use futures::Oneshot; -use parking_lot::Mutex; -use key_server_cluster::{Error, SessionId, NodeId, DocumentKeyShare, DocumentKeyShareVersion, KeyStorage}; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{ClusterSession, CompletionSignal}; -use key_server_cluster::math; -use key_server_cluster::message::{Message, ShareAddMessage, ShareAddConsensusMessage, ConsensusMessageOfShareAdd, - InitializeConsensusSessionOfShareAdd, KeyShareCommon, NewKeysDissemination, ShareAddError, - ConfirmConsensusInitialization, CommonKeyData}; -use key_server_cluster::jobs::job_session::JobTransport; -use key_server_cluster::jobs::dummy_job::{DummyJob, DummyJobTransport}; -use key_server_cluster::jobs::servers_set_change_access_job::{ServersSetChangeAccessJob, ServersSetChangeAccessRequest}; -use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; -use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - -/// Share addition session transport. -pub trait SessionTransport: Clone + JobTransport { - /// Get all connected nodes. Since ShareAdd session requires all cluster nodes to be connected, this set equals to all known cluster nodes set. - fn nodes(&self) -> BTreeSet; - /// Send message to given node. - fn send(&self, node: &NodeId, message: ShareAddMessage) -> Result<(), Error>; - /// Set data for master node (sent to slave nodes in consensus session initialization message). - fn set_master_data(&mut self, consensus_group: BTreeSet, version_holders: BTreeSet, id_numbers: BTreeMap>); -} - -/// Share addition session. -/// Based on "Efficient Multi-Party Digital Signature using Adaptive Secret Sharing for Low-Power Devices in Wireless Networks" paper: -/// http://www.wu.ece.ufl.edu/mypapers/msig.pdf -/// Brief overview: -/// 1) initialization: master node (which has received request for shares addition the message) asks all other nodes to support addition -/// 2) key refreshing distribution (KRD): node generates new random polynom && sends required data to all other nodes -/// 3) key refreshing verification (KRV): node verifies received data -/// 4) node updates its own key share using generated (&& received) data -pub struct SessionImpl { - /// Session core. - core: SessionCore, - /// Session data. - data: Mutex>, -} - -/// Immutable session data. -struct SessionCore { - /// Session metadata. - pub meta: ShareChangeSessionMeta, - /// Session-level nonce. - pub nonce: u64, - /// Original key share (for old nodes only). - pub key_share: Option, - /// Session transport to communicate to other cluster nodes. - pub transport: T, - /// Key storage. - pub key_storage: Arc, - /// Administrator public key. - pub admin_public: Option, - /// Session completion signal. - pub completed: CompletionSignal<()>, -} - -/// Share add consensus session type. -type ShareAddChangeConsensusSession = ConsensusSession; - -/// Mutable session data. -struct SessionData { - /// Session state. - pub state: SessionState, - /// Key version to use for decryption. - pub version: Option, - /// Consensus session. - pub consensus_session: Option>, - /// Holders of key version. - pub version_holders: Option>, - /// NewKeyShare (for nodes being added). - pub new_key_share: Option, - /// Nodes id numbers. - pub id_numbers: Option>>, - /// Secret subshares received from nodes. - pub secret_subshares: Option>>, - /// Share add change result. - pub result: Option>, -} - -/// New key share. -struct NewKeyShare { - /// NewKeyShare: threshold. - pub threshold: usize, - /// NewKeyShare: author. - pub author: Address, - /// NewKeyShare: joint public. - pub joint_public: Public, - /// NewKeyShare: Common (shared) encryption point. - pub common_point: Option, - /// NewKeyShare: Encrypted point. - pub encrypted_point: Option, -} - -/// Session state. -#[derive(Debug, PartialEq)] -enum SessionState { - /// State when consensus is establishing. - ConsensusEstablishing, - /// Waiting for keys dissemination. - WaitingForKeysDissemination, - /// Session is completed. - Finished, -} - -/// SessionImpl creation parameters -pub struct SessionParams { - /// Session metadata. - pub meta: ShareChangeSessionMeta, - /// Session transport. - pub transport: T, - /// Key storage. - pub key_storage: Arc, - /// Administrator public key. - pub admin_public: Option, - /// Session nonce. - pub nonce: u64, -} - -/// Isolated ShareAdd session transport. -#[derive(Clone)] -pub struct IsolatedSessionTransport { - /// Key id. - session: SessionId, - /// Key version. - version: Option, - /// Session-level nonce. - nonce: u64, - /// Holders of key version. - version_holders: Option>, - /// Consensus group. - consensus_group: Option>, - /// Id numbers of all new nodes. - id_numbers: Option>>, - /// Cluster. - cluster: Arc, -} - -impl SessionImpl where T: SessionTransport { - /// Create new share addition session. - pub fn new(params: SessionParams) -> Result<(Self, Oneshot>), Error> { - let key_share = params.key_storage.get(¶ms.meta.id)?; - let (completed, oneshot) = CompletionSignal::new(); - Ok((SessionImpl { - core: SessionCore { - meta: params.meta, - nonce: params.nonce, - key_share: key_share, - transport: params.transport, - key_storage: params.key_storage, - admin_public: params.admin_public, - completed, - }, - data: Mutex::new(SessionData { - state: SessionState::ConsensusEstablishing, - version: None, - consensus_session: None, - version_holders: None, - new_key_share: None, - id_numbers: None, - secret_subshares: None, - result: None, - }), - }, oneshot)) - } - - /// Set pre-established consensus data. - pub fn set_consensus_output(&self, version: &H256, consensus_group: BTreeSet, version_holders: BTreeSet, mut new_nodes_map: BTreeMap>) -> Result<(), Error> { - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::ConsensusEstablishing || data.consensus_session.is_some() || data.id_numbers.is_some() || data.secret_subshares.is_some() { - return Err(Error::InvalidStateForRequest); - } - - // key share version is required on ShareAdd master node - if let Some(key_share) = self.core.key_share.as_ref() { - if let Ok(key_version) = key_share.version(version) { - let non_isolated_nodes = self.core.transport.nodes(); - for (node, id_number) in &key_version.id_numbers { - { - let external_id_number = new_nodes_map.get(node); - match external_id_number { - Some(&Some(ref external_id_number)) => { - if !version_holders.contains(node) { - // possible when joining version holder, that has lost its database - // and haven't reported version ownership - continue; - } - if external_id_number == id_number { - continue; - } - - return Err(Error::ConsensusUnreachable); - }, - Some(&None) => (), - None => { - if non_isolated_nodes.contains(node) { - return Err(Error::ConsensusUnreachable) - } - continue; - }, - } - } - - new_nodes_map.insert(node.clone(), Some(id_number.clone())); - } - - // check that all id_numbers are filled - if new_nodes_map.values().any(Option::is_none) { - return Err(Error::ConsensusUnreachable); - } - } - } - - // check passed consensus data - Self::check_nodes_map(&self.core, version, &consensus_group, &version_holders, &new_nodes_map)?; - - // update data - data.version = Some(version.clone()); - data.id_numbers = Some(new_nodes_map); - data.secret_subshares = Some(consensus_group.into_iter() - .map(|n| (n, None)) - .collect()); - data.version_holders = Some(version_holders); - - Ok(()) - } - - /// Initialize share add session on master node. - pub fn initialize(&self, version: Option, new_nodes_set: Option>, old_set_signature: Option, new_set_signature: Option) -> Result<(), Error> { - debug_assert_eq!(self.core.meta.self_node_id, self.core.meta.master_node_id); - - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::ConsensusEstablishing || data.consensus_session.is_some() { - return Err(Error::InvalidStateForRequest); - } - - // if consensus is pre-established => start sending ShareAdd-specific messages - let is_consensus_pre_established = data.id_numbers.is_some(); - if is_consensus_pre_established { - return Self::on_consensus_established(&self.core, &mut *data); - } - - // else => prepare to start consensus session - // require all initialization params for consensus session - let version = version.ok_or(Error::InvalidMessage)?; - let old_set_signature = old_set_signature.ok_or(Error::InvalidMessage)?; - let new_set_signature = new_set_signature.ok_or(Error::InvalidMessage)?; - let new_nodes_set = new_nodes_set.ok_or(Error::InvalidMessage)?; - let admin_public = self.core.admin_public.as_ref().cloned().ok_or(Error::ConsensusUnreachable)?; - - // key share version is required on ShareAdd master node - let key_share = self.core.key_share.as_ref().ok_or_else(|| Error::ServerKeyIsNotFound)?; - let key_version = key_share.version(&version)?; - - // old nodes set is all non-isolated owners of version holders - let non_isolated_nodes = self.core.transport.nodes(); - let old_nodes_set: BTreeSet<_> = key_version.id_numbers.keys() - .filter(|n| non_isolated_nodes.contains(n)) - .cloned() - .collect(); - - // new nodes map contains previous id_numbers for old nodes && random number for new nodes - let mut new_nodes_map = BTreeMap::new(); - for new_node in new_nodes_set.into_iter().filter(|n| non_isolated_nodes.contains(n)) { - new_nodes_map.insert(new_node, match key_version.id_numbers.get(&new_node) { - Some(old_id_number) => Some(old_id_number.clone()), - None => Some(math::generate_random_scalar()?), - }); - } - - // let's select consensus group - let consensus_group: BTreeSet<_> = ::std::iter::once(self.core.meta.self_node_id.clone()) - .chain(old_nodes_set.iter() - .filter(|n| **n != self.core.meta.self_node_id && non_isolated_nodes.contains(*n)) - .take(key_share.threshold) - .cloned()) - .collect(); - let version_holders = &old_nodes_set; - - // now check nodes map - Self::check_nodes_map(&self.core, &version, &consensus_group, version_holders, &new_nodes_map)?; - - // prepare consensus session transport - let mut consensus_transport = self.core.transport.clone(); - consensus_transport.set_master_data(consensus_group.clone(), version_holders.clone(), new_nodes_map.clone()); - - // create && initialize consensus session - let mut consensus_session = ConsensusSession::new(ConsensusSessionParams { - meta: self.core.meta.clone().into_consensus_meta(new_nodes_map.len())?, - consensus_executor: ServersSetChangeAccessJob::new_on_master(admin_public, - old_nodes_set.clone(), - new_nodes_map.keys().cloned().collect(), - old_set_signature, - new_set_signature), - consensus_transport: consensus_transport, - })?; - - consensus_session.initialize(new_nodes_map.keys().cloned().collect())?; - - // update data - data.version = Some(version); - data.consensus_session = Some(consensus_session); - data.id_numbers = Some(new_nodes_map); - data.secret_subshares = Some(consensus_group.into_iter().map(|n| (n, None)).collect()); - data.version_holders = Some(version_holders.clone()); - - Ok(()) - } - - /// Process single message. - pub fn process_message(&self, sender: &NodeId, message: &ShareAddMessage) -> Result<(), Error> { - if self.core.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &ShareAddMessage::ShareAddConsensusMessage(ref message) => - self.on_consensus_message(sender, message), - &ShareAddMessage::KeyShareCommon(ref message) => - self.on_common_key_share_data(sender, message), - &ShareAddMessage::NewKeysDissemination(ref message) => - self.on_new_keys_dissemination(sender, message), - &ShareAddMessage::ShareAddError(ref message) => { - self.on_session_error(sender, message.error.clone()); - Ok(()) - }, - } - } - - /// When consensus-related message is received. - pub fn on_consensus_message(&self, sender: &NodeId, message: &ShareAddConsensusMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // start slave consensus session if needed - let mut data = self.data.lock(); - match &message.message { - &ConsensusMessageOfShareAdd::InitializeConsensusSession(ref message) - if data.consensus_session.is_none() && sender == &self.core.meta.master_node_id => { - let admin_public = self.core.admin_public.as_ref().cloned().ok_or(Error::ConsensusUnreachable)?; - data.consensus_session = Some(ConsensusSession::new(ConsensusSessionParams { - meta: self.core.meta.clone().into_consensus_meta(message.new_nodes_map.len())?, - consensus_executor: ServersSetChangeAccessJob::new_on_slave(admin_public), - consensus_transport: self.core.transport.clone(), - })?); - }, - _ => (), - }; - - // process consensus message - let (is_establishing_consensus, is_consensus_established, version, new_nodes_map, consensus_group, version_holders) = { - let consensus_session = data.consensus_session.as_mut().ok_or(Error::InvalidMessage)?; - let is_establishing_consensus = consensus_session.state() == ConsensusSessionState::EstablishingConsensus; - - let (version, new_nodes_map, consensus_group, version_holders) = match &message.message { - &ConsensusMessageOfShareAdd::InitializeConsensusSession(ref message) => { - consensus_session.on_consensus_partial_request(sender, ServersSetChangeAccessRequest::from(message))?; - - let version = message.version.clone().into(); - let consensus_group = message.consensus_group.iter().cloned().map(Into::into).collect(); - let version_holders = message.version_holders.iter().cloned().map(Into::into).collect(); - let new_nodes_map: BTreeMap<_, _> = message.new_nodes_map.iter() - .map(|(n, nn)| (n.clone().into(), Some(nn.clone().into()))) - .collect(); - - // check that all id_numbers are filled - if new_nodes_map.values().any(Option::is_none) { - return Err(Error::ConsensusUnreachable); - } - - // check old set of nodes - Self::check_nodes_map(&self.core, &version, &consensus_group, &version_holders, &new_nodes_map)?; - - (Some(version), Some(new_nodes_map), Some(consensus_group), Some(version_holders)) - }, - &ConsensusMessageOfShareAdd::ConfirmConsensusInitialization(ref message) => { - consensus_session.on_consensus_partial_response(sender, message.is_confirmed)?; - (None, None, None, None) - }, - }; - - ( - is_establishing_consensus, - consensus_session.state() == ConsensusSessionState::ConsensusEstablished, - version, - new_nodes_map, - consensus_group, - version_holders, - ) - }; - - // update data - if let Some(version) = version { - data.version = Some(version); - } - if let Some(new_nodes_map) = new_nodes_map { - data.id_numbers = Some(new_nodes_map); - } - if let Some(consensus_group) = consensus_group { - data.secret_subshares = Some(consensus_group.into_iter().map(|n| (n, None)).collect()); - } - if let Some(version_holders) = version_holders { - data.version_holders = Some(version_holders); - } - - // if consensus is stablished, proceed - if !is_establishing_consensus || !is_consensus_established || self.core.meta.self_node_id != self.core.meta.master_node_id { - return Ok(()); - } - - Self::on_consensus_established(&self.core, &mut *data) - } - - /// When common key share data is received by new node. - pub fn on_common_key_share_data(&self, sender: &NodeId, message: &KeyShareCommon) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - // only master can send this message - if sender != &self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::ConsensusEstablishing || data.id_numbers.is_none() { - return Ok(()); - } - - // we only expect this message once - if data.new_key_share.is_some() { - return Err(Error::InvalidStateForRequest); - } - - // check if we actually waiting for this message - { - let version = data.version.as_ref().ok_or(Error::InvalidStateForRequest)?; - let key_version = self.core.key_share.as_ref().and_then(|ks| ks.version(version).ok()); - if key_version.is_some() { - return Ok(()); - } - } - - // update data - data.state = SessionState::WaitingForKeysDissemination; - data.new_key_share = Some(NewKeyShare { - threshold: message.key_common.threshold, - author: message.key_common.author.clone().into(), - joint_public: message.key_common.public.clone().into(), - common_point: message.common_point.clone().map(Into::into), - encrypted_point: message.encrypted_point.clone().map(Into::into), - }); - - let id_numbers = data.id_numbers.as_mut() - .expect("common key share data is expected after initialization; id_numbers are filled during initialization; qed"); - for (node, id_number) in &message.id_numbers { - let id_number: Secret = id_number.clone().into(); - { - let local_id_number = id_numbers.get(&node.clone().into()); - match local_id_number { - Some(&Some(ref local_id_number)) => { - if *local_id_number == id_number { - continue; - } - - return Err(Error::ConsensusUnreachable); - }, - Some(&None) => (), - None => continue, // can happen for isolated nodes - } - } - - id_numbers.insert(node.clone().into(), Some(id_number)); - } - - Ok(()) - } - - /// When keys dissemination message is received. - pub fn on_new_keys_dissemination(&self, sender: &NodeId, message: &NewKeysDissemination) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - // check state - if data.state == SessionState::ConsensusEstablishing && data.secret_subshares.is_some() { - data.state = SessionState::WaitingForKeysDissemination; - } else if data.state != SessionState::WaitingForKeysDissemination { - return Err(Error::InvalidStateForRequest); - } - - // update data - let explanation = "secret_subshares is filled during initialization; keys are disseminated after initialization; qed"; - { - match data.secret_subshares.as_ref().expect(explanation).get(sender) { - None => return Err(Error::InvalidMessage), - Some(&Some(_)) => return Err(Error::InvalidMessage), - Some(&None) => (), - }; - - let secret_subshare = Self::compute_secret_subshare(&self.core, &mut *data, sender, &message.secret_subshare.clone().into())?; - *data.secret_subshares.as_mut().expect(explanation) - .get_mut(sender) - .expect("checked couple of lines above; qed") = Some(secret_subshare); - } - - // if we have received subshare from master node, it means that we should start dissemination - if sender == &self.core.meta.master_node_id { - Self::on_consensus_established(&self.core, &mut *data)?; - } - - // check if shares from all nodes are received - if data.secret_subshares.as_ref().expect(explanation).values().any(|v| v.is_none()) { - return Ok(()) - } - - // TODO [Trust]: find a way to verificate keys - Self::complete_session(&self.core, &mut *data) - } - - /// Check nodes map. - fn check_nodes_map(core: &SessionCore, version: &H256, consensus_group: &BTreeSet, version_holders: &BTreeSet, new_nodes_map: &BTreeMap>) -> Result<(), Error> { - // check if this node has given version - let has_this_version = match core.key_share.as_ref() { - Some(key_share) => key_share.version(version).is_ok(), - None => false, - }; - - // check && update passed data - match has_this_version { - true => { - // check if version exists - let explanation = "has_this_version is true; it is true if we have given version of the key; qed"; - let key_share = core.key_share.as_ref().expect(explanation); - let key_version = key_share.version(version).expect(explanation); - - // there must be exactly thresold + 1 nodes in consensus group - if consensus_group.len() != key_share.threshold + 1 { - return Err(Error::ConsensusUnreachable); - } - - // every non-isolated node must be a part of new_nodes_set - let non_isolated_nodes = core.transport.nodes(); - if key_version.id_numbers.keys().any(|n| non_isolated_nodes.contains(n) && !new_nodes_map.contains_key(n)) { - return Err(Error::ConsensusUnreachable); - } - - // there must be at least one new node in new_nodes_map - if key_version.id_numbers.keys().filter(|n| non_isolated_nodes.contains(n) && version_holders.contains(n)).count() >= new_nodes_map.len() { - return Err(Error::ConsensusUnreachable); - } - }, - false => { - // if we do not have a share, we should not be a part of consenus group - // but we must be on new nodes set, since this is a ShareAdd session - if consensus_group.contains(&core.meta.self_node_id) || - !new_nodes_map.contains_key(&core.meta.self_node_id) { - return Err(Error::ConsensusUnreachable); - } - }, - } - - // master node must always be a part of consensus group - if !consensus_group.contains(&core.meta.master_node_id) { - return Err(Error::ConsensusUnreachable); - } - - // master node must always be a part of new_nodes_map - if !new_nodes_map.contains_key(&core.meta.master_node_id) { - return Err(Error::ConsensusUnreachable); - } - - Ok(()) - } - - /// Start sending ShareAdd-specific messages, when consensus is established. - fn on_consensus_established(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { - // update state - data.state = SessionState::WaitingForKeysDissemination; - - // if we're not a part of consensus group, wait for secret subshares - let explanation = "secret_subshares is a result of consensus job; consensus is established; qed"; - let is_consensus_group_node = data.secret_subshares.as_ref().expect(explanation).contains_key(&core.meta.self_node_id); - if !is_consensus_group_node { - return Ok(()); - } - - // else if master => send shared data to every new node - if core.meta.self_node_id == core.meta.master_node_id { - Self::disseminate_common_share_data(core, data)?; - } - - // ...and then disseminate keys - Self::disseminate_keys(core, data)?; - - // ..and check if session could be completed - if data.secret_subshares.as_ref().expect(explanation).values().any(|v| v.is_none()) { - return Ok(()) - } - - // TODO [Trust]: find a way to verificate keys - Self::complete_session(core, data) - } - - /// Send common share data to evey new node. - fn disseminate_common_share_data(core: &SessionCore, data: &SessionData) -> Result<(), Error> { - let explanation = "disseminate_common_share_data is only called on master node; master node has specified version of the key; qed"; - let old_key_share = core.key_share.as_ref().expect(explanation); - let old_key_version = old_key_share.version(data.version.as_ref().expect(explanation)).expect(explanation); - let version_holders = data.version_holders.as_ref() - .expect("disseminate_common_share_data is only called on master node; version holders is created during initialization on master node; qed"); - let consensus_group = data.secret_subshares.as_ref() - .expect("disseminate_common_share_data is only called on master node; consensus group is created during initialization on master node; qed"); - let nodes = data.id_numbers.as_ref() - .expect("nodes are filled during consensus establishing; common share data sent after consensus is established; qed") - .keys() - .filter(|n| !consensus_group.contains_key(n)); - for new_node in nodes { - core.transport.send(new_node, ShareAddMessage::KeyShareCommon(KeyShareCommon { - session: core.meta.id.clone().into(), - session_nonce: core.nonce, - key_common: CommonKeyData { - threshold: old_key_share.threshold, - author: old_key_share.author.into(), - public: old_key_share.public.into(), - }, - common_point: old_key_share.common_point.clone().map(Into::into), - encrypted_point: old_key_share.encrypted_point.clone().map(Into::into), - id_numbers: old_key_version.id_numbers.iter() - .filter(|&(k, _)| version_holders.contains(k)) - .map(|(k, v)| (k.clone().into(), v.clone().into())).collect(), - }))?; - } - - Ok(()) - } - - /// Disseminate key refreshing data. - fn disseminate_keys(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { - // generate random polynom with secret share as absolute term - let explanation = "disseminate_keys is only called on consensus group nodes; consensus group nodes have specified version of the key; qed"; - let key_share = core.key_share.as_ref().expect(explanation); - let key_version = key_share.version(data.version.as_ref().expect(explanation)).expect(explanation); - let mut secret_share_polynom = math::generate_random_polynom(key_share.threshold)?; - secret_share_polynom[0] = key_version.secret_share.clone(); - - // calculate secret subshare for every new node (including this node) - let explanation = "disseminate_keys is called after initialization has completed; this field is filled during initialization; qed"; - for (new_node, new_node_number) in data.id_numbers.as_ref().expect(explanation).iter() { - let new_node_number = new_node_number.as_ref().ok_or(Error::InvalidMessage)?; - let secret_subshare = math::compute_polynom(&secret_share_polynom, new_node_number)?; - if new_node != &core.meta.self_node_id { - core.transport.send(new_node, ShareAddMessage::NewKeysDissemination(NewKeysDissemination { - session: core.meta.id.clone().into(), - session_nonce: core.nonce, - secret_subshare: secret_subshare.into(), - }))?; - } else { - let secret_subshare = Self::compute_secret_subshare(core, data, new_node, &secret_subshare)?; - *data.secret_subshares.as_mut().expect(explanation) - .get_mut(&core.meta.self_node_id) - .expect("disseminate_keys is only calle on consensus group nodes; there's entry for every consensus node in secret_subshares; qed") - = Some(secret_subshare); - } - } - - Ok(()) - } - - /// Compute secret subshare from passed secret value. - fn compute_secret_subshare(core: &SessionCore, data: &SessionData, sender: &NodeId, secret_value: &Secret) -> Result { - let explanation = "this field is a result of consensus job; compute_secret_subshare is called after consensus is established"; - let id_numbers = data.id_numbers.as_ref().expect(explanation); - let secret_subshares = data.secret_subshares.as_ref().expect(explanation); - let threshold = core.key_share.as_ref().map(|ks| ks.threshold) - .unwrap_or_else(|| data.new_key_share.as_ref() - .expect("computation occurs after receiving key share threshold if not having one already; qed") - .threshold); - - let explanation = "id_numbers are checked to have Some value for every consensus group node when consensus is establishe; qed"; - let sender_id_number = id_numbers[sender].as_ref().expect(explanation); - let other_id_numbers = secret_subshares.keys().filter(|k| *k != sender).map(|n| id_numbers[n].as_ref().expect(explanation)); - math::compute_secret_subshare(threshold, secret_value, sender_id_number, other_id_numbers) - } - - /// Complete session. - fn complete_session(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { - // if already completed, do nothing - if data.state == SessionState::Finished { - return Ok(()); - } - - // compose updated key share - let explanation = "this field is a result of consensus job; complete_session is called after consensus is established"; - let id_numbers = data.id_numbers.as_ref().expect(explanation); - let secret_subshares = data.secret_subshares.as_ref() - .expect("nodes are filled during consensus establishing; session is completed after consensus is established; qed"); - let secret_share = math::compute_secret_share(secret_subshares.values().map(|ss| ss.as_ref() - .expect("complete_session is only called when subshares from all nodes are received; qed")))?; - - let refreshed_key_version = DocumentKeyShareVersion::new(id_numbers.clone().into_iter().map(|(k, v)| (k.clone(), - v.expect("id_numbers are checked to have Some value for every consensus group node when consensus is establishe; qed"))).collect(), - secret_share); - let mut refreshed_key_share = core.key_share.as_ref().cloned().unwrap_or_else(|| { - let new_key_share = data.new_key_share.as_ref() - .expect("this is new node; on new nodes this field is filled before KRD; session is completed after KRD; qed"); - DocumentKeyShare { - author: new_key_share.author.clone(), - threshold: new_key_share.threshold, - public: new_key_share.joint_public.clone(), - common_point: new_key_share.common_point.clone(), - encrypted_point: new_key_share.encrypted_point.clone(), - versions: Vec::new(), - } - }); - refreshed_key_share.versions.push(refreshed_key_version); - - // save encrypted data to the key storage - data.state = SessionState::Finished; - if core.key_share.is_some() { - core.key_storage.update(core.meta.id.clone(), refreshed_key_share.clone())?; - } else { - core.key_storage.insert(core.meta.id.clone(), refreshed_key_share.clone())?; - } - - // signal session completion - data.state = SessionState::Finished; - data.result = Some(Ok(())); - core.completed.send(Ok(())); - - Ok(()) - } -} - -impl ClusterSession for SessionImpl where T: SessionTransport { - type Id = SessionId; - type CreationData = (); // never used directly - type SuccessfulResult = (); - - fn type_name() -> &'static str { - "share add" - } - - fn id(&self) -> SessionId { - self.core.meta.id.clone() - } - - fn is_finished(&self) -> bool { - self.data.lock().state == SessionState::Finished - } - - fn on_session_timeout(&self) { - self.on_session_error(&self.core.meta.self_node_id, Error::NodeDisconnected) - } - - fn on_node_timeout(&self, node: &NodeId) { - self.on_session_error(node, Error::NodeDisconnected) - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - // error in generation session is considered fatal - // => broadcast error if error occured on this node - if *node == self.core.meta.self_node_id { - for node in self.core.transport.nodes() { - // do not bother processing send error, as we already processing error - let _ = self.core.transport.send(&node, ShareAddMessage::ShareAddError(ShareAddError { - session: self.core.meta.id.clone().into(), - session_nonce: self.core.nonce, - error: error.clone().into(), - })); - } - } - - let mut data = self.data.lock(); - - warn!(target: "secretstore_net", "{}: share add session failed: {} on {}", - self.core.meta.self_node_id, error, node); - - data.state = SessionState::Finished; - data.result = Some(Err(error.clone())); - self.core.completed.send(Err(error)); - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::ShareAdd(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl IsolatedSessionTransport { - pub fn new(session_id: SessionId, version: Option, nonce: u64, cluster: Arc) -> Self { - IsolatedSessionTransport { - session: session_id, - version: version, - nonce: nonce, - cluster: cluster, - id_numbers: None, - version_holders: None, - consensus_group: None, - } - } -} - -impl JobTransport for IsolatedSessionTransport { - type PartialJobRequest = ServersSetChangeAccessRequest; - type PartialJobResponse = bool; - - fn send_partial_request(&self, node: &NodeId, request: ServersSetChangeAccessRequest) -> Result<(), Error> { - let explanation = "partial requests are sent from master node only; on master node this field is filled during creation; qed"; - let id_numbers = self.id_numbers.as_ref().expect(explanation); - - self.cluster.send(node, Message::ShareAdd(ShareAddMessage::ShareAddConsensusMessage(ShareAddConsensusMessage { - session: self.session.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessageOfShareAdd::InitializeConsensusSession(InitializeConsensusSessionOfShareAdd { - version: self.version.clone().expect(explanation).into(), - version_holders: self.version_holders.as_ref().expect(explanation).iter().cloned().map(Into::into).collect(), - consensus_group: self.consensus_group.as_ref().expect(explanation).iter().cloned().map(Into::into).collect(), - old_nodes_set: request.old_servers_set.into_iter().map(Into::into).collect(), - new_nodes_map: request.new_servers_set.into_iter() - .filter_map(|n| id_numbers.get(&n) - .map(|id| (n.into(), id.clone() - .expect("partial requests are sent from master node only after consensus is established; - on master id_numbers are initialized with Some id_number for every consensus group node; qed").into()))) - .collect(), - old_set_signature: request.old_set_signature.into(), - new_set_signature: request.new_set_signature.into(), - }), - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { - self.cluster.send(node, Message::ShareAdd(ShareAddMessage::ShareAddConsensusMessage(ShareAddConsensusMessage { - session: self.session.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessageOfShareAdd::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: response, - }), - }))) - } -} - -impl SessionTransport for IsolatedSessionTransport { - fn nodes(&self) -> BTreeSet { - self.cluster.nodes() - } - - fn set_master_data(&mut self, consensus_group: BTreeSet, version_holders: BTreeSet, id_numbers: BTreeMap>) { - self.version_holders = Some(version_holders); - self.consensus_group = Some(consensus_group); - self.id_numbers = Some(id_numbers); - } - - fn send(&self, node: &NodeId, message: ShareAddMessage) -> Result<(), Error> { - self.cluster.send(node, Message::ShareAdd(message)) - } -} - -#[cfg(test)] -pub mod tests { - use std::collections::BTreeSet; - use crypto::publickey::{Random, Generator, Public}; - use blockchain::SigningKeyPair; - use key_server_cluster::{NodeId, Error, KeyStorage}; - use key_server_cluster::cluster::tests::MessageLoop as ClusterMessageLoop; - use key_server_cluster::servers_set_change_session::tests::{MessageLoop, AdminSessionAdapter, generate_key}; - use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - use super::{SessionImpl, SessionParams, IsolatedSessionTransport}; - - struct Adapter; - - impl AdminSessionAdapter> for Adapter { - const SIGN_NEW_NODES: bool = false; - - fn create( - mut meta: ShareChangeSessionMeta, - admin_public: Public, - _: BTreeSet, - ml: &ClusterMessageLoop, - idx: usize - ) -> SessionImpl { - let key_storage = ml.key_storage(idx).clone(); - let key_version = key_storage.get(&meta.id).unwrap().map(|ks| ks.last_version().unwrap().hash); - - meta.self_node_id = *ml.node_key_pair(idx).public(); - SessionImpl::new(SessionParams { - meta: meta.clone(), - transport: IsolatedSessionTransport::new(meta.id, key_version, 1, ml.cluster(idx).view().unwrap()), - key_storage, - admin_public: Some(admin_public), - nonce: 1, - }).unwrap().0 - } - } - - impl MessageLoop> { - pub fn init_at(self, master: NodeId) -> Result { - self.sessions[&master].initialize( - Some(self.original_key_version), - Some(self.new_nodes_set.clone()), - Some(self.all_set_signature.clone()), - Some(self.new_set_signature.clone()))?; - Ok(self) - } - - pub fn run_at(self, master: NodeId) -> Result { - let mut ml = self.init_at(master)?; - ml.run(); - Ok(ml) - } - } - - #[test] - fn node_add_fails_if_nodes_removed() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // try to remove 1 node - let add = vec![Random.generate().unwrap()]; - let remove: BTreeSet<_> = ::std::iter::once(gml.0.node(1)).collect(); - let master = gml.0.node(0); - assert_eq!(MessageLoop::with_gml::(gml, master, Some(add), Some(remove), None) - .run_at(master).unwrap_err(), Error::ConsensusUnreachable); - } - - #[test] - fn node_add_fails_if_no_nodes_added() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // try to add 0 nodes - let add = vec![]; - let master = gml.0.node(0); - assert_eq!(MessageLoop::with_gml::(gml, master, Some(add), None, None) - .run_at(master).unwrap_err(), Error::ConsensusUnreachable); - } - - #[test] - fn node_add_fails_if_started_on_adding_node() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // try to add 1 node using this node as a master node - let add = vec![Random.generate().unwrap()]; - let master = *add[0].public(); - assert_eq!(MessageLoop::with_gml::(gml, master, Some(add), None, None) - .run_at(master).unwrap_err(), Error::ServerKeyIsNotFound); - } - - #[test] - fn node_add_fails_if_initialized_twice() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // try to add 1 node using this node as a master node - let add = vec![Random.generate().unwrap()]; - let master = gml.0.node(0); - assert_eq!(MessageLoop::with_gml::(gml, master, Some(add), None, None) - .init_at(master).unwrap() - .init_at(master).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn node_add_fails_if_started_without_signatures() { - // initial 2-of-3 session - let gml = generate_key(3, 1); - - // try to add 1 node using this node as a master node - let add = vec![Random.generate().unwrap()]; - let master = gml.0.node(0); - assert_eq!(MessageLoop::with_gml::(gml, master, Some(add), None, None) - .sessions[&master] - .initialize(None, None, None, None).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn nodes_added_using_share_add() { - let test_cases = vec![(3, 1), (3, 3)]; - for (n, add) in test_cases { - // generate key - let gml = generate_key(n, 1); - - // run share add session - let add = (0..add).map(|_| Random.generate().unwrap()).collect(); - let master = gml.0.node(0); - let ml = MessageLoop::with_gml::(gml, master, Some(add), None, None) - .run_at(master).unwrap(); - - // check that secret is still the same as before adding the share - ml.check_secret_is_preserved(ml.sessions.keys()); - } - } - - #[test] - fn nodes_added_using_share_add_with_isolated_nodes() { - let (n, add) = (3, 3); - - // generate key - let gml = generate_key(n, 1); - - // run share add session - let master = gml.0.node(0); - let node_to_isolate = gml.0.node(1); - let add = (0..add).map(|_| Random.generate().unwrap()).collect(); - let isolate = ::std::iter::once(node_to_isolate).collect(); - let ml = MessageLoop::with_gml::(gml, master, Some(add), None, Some(isolate)) - .run_at(master).unwrap(); - - // check that secret is still the same as before adding the share - ml.check_secret_is_preserved(ml.sessions.keys()); - } - - #[test] - fn nodes_add_to_the_node_with_obsolete_version() { - let (n, add) = (3, 3); - - // generate key - let gml = generate_key(n, 1); - - // run share add session - let master = gml.0.node(0); - let node_to_isolate_key_pair = gml.0.node_key_pair(1).clone(); - let node_to_isolate = gml.0.node(1); - let isolated_key_storage = gml.0.key_storage(1).clone(); - let mut oldest_nodes_set = gml.0.nodes(); - oldest_nodes_set.remove(&node_to_isolate); - let add = (0..add).map(|_| Random.generate().unwrap()).collect::>(); - let newest_nodes_set = add.iter().map(|kp| *kp.public()).collect::>(); - let isolate = ::std::iter::once(node_to_isolate).collect(); - let ml = MessageLoop::with_gml::(gml, master, Some(add), None, Some(isolate)) - .run_at(master).unwrap(); - let new_key_version = ml.ml.key_storage(0).get(&Default::default()) - .unwrap().unwrap().last_version().unwrap().hash; - - // now let's add back old node so that key becames 2-of-6 - let add = vec![node_to_isolate_key_pair.key_pair().clone()]; - let mut ml = ml.and_then::(master.clone(), Some(add), None, None); - ml.original_key_version = new_key_version; - ml.ml.replace_key_storage_of(&node_to_isolate, isolated_key_storage.clone()); - ml.sessions.get_mut(&node_to_isolate).unwrap().core.key_share = - isolated_key_storage.get(&Default::default()).unwrap(); - ml.sessions.get_mut(&node_to_isolate).unwrap().core.key_storage = isolated_key_storage; - let ml = ml.run_at(master).unwrap(); - - // check that secret is still the same as before adding the share - ml.check_secret_is_preserved(ml.sessions.keys()); - - // check that all oldest nodes have versions A, B, C - // isolated node has version A, C - // new nodes have versions B, C - let oldest_key_share = ml.ml.key_storage_of(oldest_nodes_set.iter().nth(0).unwrap()) - .get(&Default::default()).unwrap().unwrap(); - debug_assert_eq!(oldest_key_share.versions.len(), 3); - let version_a = oldest_key_share.versions[0].hash.clone(); - let version_b = oldest_key_share.versions[1].hash.clone(); - let version_c = oldest_key_share.versions[2].hash.clone(); - debug_assert!(version_a != version_b && version_b != version_c); - - debug_assert!(oldest_nodes_set.iter().all(|n| vec![version_a.clone(), version_b.clone(), version_c.clone()] == - ml.ml.key_storage_of(n).get(&Default::default()).unwrap().unwrap() - .versions.iter().map(|v| v.hash).collect::>())); - debug_assert!(::std::iter::once(&node_to_isolate).all(|n| vec![version_a.clone(), version_c.clone()] == - ml.ml.key_storage_of(n).get(&Default::default()).unwrap().unwrap() - .versions.iter().map(|v| v.hash).collect::>())); - debug_assert!(newest_nodes_set.iter().all(|n| vec![version_b.clone(), version_c.clone()] == - ml.ml.key_storage_of(n).get(&Default::default()).unwrap().unwrap() - .versions.iter().map(|v| v.hash).collect::>())); - } - - #[test] - fn nodes_add_fails_when_not_enough_share_owners_are_connected() { - let (n, add) = (3, 3); - - // generate key - let gml = generate_key(n, 1); - - // run share add session - let master = gml.0.node(0); - let add = (0..add).map(|_| Random.generate().unwrap()).collect::>(); - let isolate = vec![gml.0.node(1), gml.0.node(2)].into_iter().collect(); - assert_eq!(MessageLoop::with_gml::(gml, master, Some(add), None, Some(isolate)) - .run_at(master).unwrap_err(), Error::ConsensusUnreachable); - } -} diff --git a/secret-store/src/key_server_cluster/admin_sessions/share_change_session.rs b/secret-store/src/key_server_cluster/admin_sessions/share_change_session.rs deleted file mode 100644 index e3d58d9658a..00000000000 --- a/secret-store/src/key_server_cluster/admin_sessions/share_change_session.rs +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeSet, BTreeMap}; -use ethereum_types::H256; -use crypto::publickey::Secret; -use key_server_cluster::{Error, NodeId, SessionId, ServerKeyId, KeyStorage}; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::ClusterSession; -use key_server_cluster::math; -use key_server_cluster::jobs::servers_set_change_access_job::ServersSetChangeAccessRequest; -use key_server_cluster::jobs::job_session::JobTransport; -use key_server_cluster::message::{Message, ServersSetChangeMessage, ServersSetChangeShareAddMessage}; -use key_server_cluster::share_add_session::{SessionTransport as ShareAddSessionTransport, - SessionImpl as ShareAddSessionImpl, SessionParams as ShareAddSessionParams}; -use key_server_cluster::message::ShareAddMessage; -use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - -/// Single session meta-change session. Brief overview: -/// 1) nodes that have been already removed from cluster (isolated nodes) are removed from session -/// 2) new shares are added to the session -/// 3) shares are moved between nodes -/// 4) shares are removed from nodes -pub struct ShareChangeSession { - /// Servers set change session id. - session_id: SessionId, - /// Session nonce. - nonce: u64, - /// Share change session meta. - meta: ShareChangeSessionMeta, - /// Cluster. - cluster: Arc, - /// Key storage. - key_storage: Arc, - /// Key version. - key_version: H256, - /// Nodes that have reported version ownership. - version_holders: Option>, - /// Consensus group to use in ShareAdd session. - consensus_group: Option>, - /// Nodes to add shares for. - new_nodes_map: Option>>, - /// Share add session. - share_add_session: Option>, - /// Is finished. - is_finished: bool, -} - -/// Share change session plan. -#[derive(Debug)] -pub struct ShareChangeSessionPlan { - /// Key version that plan is valid for. - pub key_version: H256, - /// Nodes that have reported version ownership. - pub version_holders: BTreeSet, - /// Consensus group to use in ShareAdd session. - pub consensus_group: BTreeSet, - /// Nodes to add shares for. - pub new_nodes_map: BTreeMap>, -} - -/// Session parameters. -pub struct ShareChangeSessionParams { - /// Servers set change session id. - pub session_id: SessionId, - /// Session nonce. - pub nonce: u64, - /// Share change session meta. - pub meta: ShareChangeSessionMeta, - /// Cluster. - pub cluster: Arc, - /// Keys storage. - pub key_storage: Arc, - /// Session plan. - pub plan: ShareChangeSessionPlan, -} - -/// Share add session transport. -#[derive(Clone)] -pub struct ShareChangeTransport { - /// Servers set change session id. - session_id: SessionId, - /// Session nonce. - nonce: u64, - /// Cluster. - cluster: Arc, -} - -impl ShareChangeSession { - /// Create new share change session. - pub fn new(params: ShareChangeSessionParams) -> Result { - // we can't create sessions right now, because key share is read when session is created, but it can change in previous session - let key_version = params.plan.key_version; - let consensus_group = if !params.plan.consensus_group.is_empty() { Some(params.plan.consensus_group) } else { None }; - let version_holders = if !params.plan.version_holders.is_empty() { Some(params.plan.version_holders) } else { None }; - let new_nodes_map = if !params.plan.new_nodes_map.is_empty() { Some(params.plan.new_nodes_map) } else { None }; - debug_assert!(new_nodes_map.is_some()); - - let is_finished = new_nodes_map.is_none(); - Ok(ShareChangeSession { - session_id: params.session_id, - nonce: params.nonce, - meta: params.meta, - cluster: params.cluster, - key_storage: params.key_storage, - key_version: key_version, - version_holders: version_holders, - consensus_group: consensus_group, - new_nodes_map: new_nodes_map, - share_add_session: None, - is_finished: is_finished, - }) - } - - /// Is finished?. - pub fn is_finished(&self) -> bool { - self.is_finished - } - - /// Is master node?. - pub fn is_master(&self) -> bool { - self.meta.self_node_id == self.meta.master_node_id - } - - /// Initialize session (on master node). - pub fn initialize(&mut self) -> Result<(), Error> { - self.proceed_to_next_state() - } - - /// When share-add message is received. - pub fn on_share_add_message(&mut self, sender: &NodeId, message: &ShareAddMessage) -> Result<(), Error> { - if self.share_add_session.is_none() { - self.create_share_add_session()?; - } - - let change_state_needed = self.share_add_session.as_ref() - .map(|share_add_session| { - let was_finished = share_add_session.is_finished(); - share_add_session.process_message(sender, message) - .map(|_| share_add_session.is_finished() && !was_finished) - }) - .unwrap_or(Err(Error::InvalidMessage))?; - if change_state_needed { - self.proceed_to_next_state()?; - } - - Ok(()) - } - - /// Create new share add session. - fn create_share_add_session(&mut self) -> Result<(), Error> { - let consensus_group = self.consensus_group.take().ok_or(Error::InvalidStateForRequest)?; - let version_holders = self.version_holders.take().ok_or(Error::InvalidStateForRequest)?; - let new_nodes_map = self.new_nodes_map.take().ok_or(Error::InvalidStateForRequest)?; - let (share_add_session, _) = ShareAddSessionImpl::new(ShareAddSessionParams { - meta: self.meta.clone(), - nonce: self.nonce, - transport: ShareChangeTransport::new(self.session_id, self.nonce, self.cluster.clone()), - key_storage: self.key_storage.clone(), - admin_public: None, - })?; - share_add_session.set_consensus_output(&self.key_version, consensus_group, version_holders, new_nodes_map)?; - self.share_add_session = Some(share_add_session); - Ok(()) - } - - /// Proceed to the next state. - fn proceed_to_next_state(&mut self) -> Result<(), Error> { - if self.meta.self_node_id != self.meta.master_node_id { - if self.new_nodes_map.is_none() { - self.is_finished = true; - } - return Ok(()); - } - - if self.new_nodes_map.is_some() { - self.create_share_add_session()?; - return self.share_add_session.as_ref() - .expect("either create_share_add_session fails, or session is created; qed") - .initialize(None, None, None, None); - } - - self.is_finished = true; - - Ok(()) - } -} - -impl ShareChangeTransport { - pub fn new(session_id: SessionId, nonce: u64, cluster: Arc) -> Self { - ShareChangeTransport { - session_id: session_id, - nonce: nonce, - cluster: cluster, - } - } -} - -impl JobTransport for ShareChangeTransport { - type PartialJobRequest = ServersSetChangeAccessRequest; - type PartialJobResponse = bool; - - fn send_partial_request(&self, _node: &NodeId, _request: ServersSetChangeAccessRequest) -> Result<(), Error> { - unreachable!("only called when establishing consensus; this transport is never used for establishing consensus; qed") - } - - fn send_partial_response(&self, _node: &NodeId, _response: bool) -> Result<(), Error> { - unreachable!("only called when establishing consensus; this transport is never used for establishing consensus; qed") - } -} - -impl ShareAddSessionTransport for ShareChangeTransport { - fn nodes(&self) -> BTreeSet { - self.cluster.nodes() - } - - fn set_master_data(&mut self, _consensus_group: BTreeSet, _version_holders: BTreeSet, _id_numbers: BTreeMap>) { - unreachable!("only called when establishing consensus; this transport is never used for establishing consensus; qed") - } - - fn send(&self, node: &NodeId, message: ShareAddMessage) -> Result<(), Error> { - self.cluster.send(node, Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeShareAddMessage(ServersSetChangeShareAddMessage { - session: self.session_id.clone().into(), - session_nonce: self.nonce, - message: message, - }))) - } -} - -/// Prepare share change plan for moving from old `old_key_version_owners` to `new_nodes_set`. -pub fn prepare_share_change_session_plan(cluster_nodes: &BTreeSet, threshold: usize, key_id: &ServerKeyId, key_version: H256, master: &NodeId, old_key_version_owners: &BTreeSet, new_nodes_set: &BTreeSet) -> Result { - // we can't do anything if there are no enought shares - if old_key_version_owners.len() < threshold + 1 { - warn!("cannot add shares to key {} with threshold {}: only {} shares owners are available", - key_id, threshold, old_key_version_owners.len()); - return Ok(ShareChangeSessionPlan { - key_version: key_version, - version_holders: Default::default(), - consensus_group: Default::default(), - new_nodes_map: Default::default(), - }); - } - - // warn if we're loosing the key - if new_nodes_set.len() < threshold + 1 { - warn!("losing key {} with threshold {}: only {} nodes left after servers set change session", - key_id, threshold, new_nodes_set.len()); - } - - // make new nodes map, so that: - // all non-isolated old nodes will have their id number preserved - // all new nodes will have new id number - let mut new_nodes_map = new_nodes_set.difference(&old_key_version_owners) - .map(|n| math::generate_random_scalar().map(|id| (n.clone(), Some(id)))) - .collect::, _>>()?; - if !new_nodes_map.is_empty() { - for old_node in old_key_version_owners.iter().filter(|n| cluster_nodes.contains(n)) { - new_nodes_map.insert(old_node.clone(), None); - } - } - - // select consensus group if there are some nodes to add - let consensus_group = if !new_nodes_map.is_empty() { - ::std::iter::once(master.clone()) - .chain(old_key_version_owners.iter() - .filter(|n| *n != master && cluster_nodes.contains(*n)) - .take(threshold) - .cloned()) - .collect() - } else { - BTreeSet::new() - }; - - Ok(ShareChangeSessionPlan { - key_version: key_version, - version_holders: old_key_version_owners.clone(), - consensus_group: consensus_group, - new_nodes_map: new_nodes_map, - }) -} - -impl ShareChangeSessionPlan { - /// Is empty (nothing-to-do) plan? - pub fn is_empty(&self) -> bool { - self.new_nodes_map.is_empty() - } -} - -#[cfg(test)] -mod tests { - use key_server_cluster::math; - use super::prepare_share_change_session_plan; - - #[test] - fn share_change_plan_creates_empty_plan() { - let cluster_nodes: Vec<_> = (0..3).map(|_| math::generate_random_point().unwrap()).collect(); - let master = cluster_nodes[0].clone(); - let old_key_version_owners = cluster_nodes.iter().cloned().collect(); - let new_nodes_set = cluster_nodes.iter().cloned().collect(); - let plan = prepare_share_change_session_plan(&cluster_nodes.iter().cloned().collect(), - 1, &Default::default(), Default::default(), &master, &old_key_version_owners, &new_nodes_set).unwrap(); - - assert!(plan.is_empty()); - } - - #[test] - fn share_change_plan_adds_new_nodes() { - let cluster_nodes: Vec<_> = (0..3).map(|_| math::generate_random_point().unwrap()).collect(); - let master = cluster_nodes[0].clone(); - let old_key_version_owners = cluster_nodes[0..2].iter().cloned().collect(); - let new_nodes_set = cluster_nodes.iter().cloned().collect(); - let plan = prepare_share_change_session_plan(&cluster_nodes.iter().cloned().collect(), - 1, &Default::default(), Default::default(), &master, &old_key_version_owners, &new_nodes_set).unwrap(); - - assert!(!plan.is_empty()); - assert_eq!(old_key_version_owners, plan.consensus_group); - assert_eq!(new_nodes_set, plan.new_nodes_map.keys().cloned().collect()); - } -} diff --git a/secret-store/src/key_server_cluster/client_sessions/decryption_session.rs b/secret-store/src/key_server_cluster/client_sessions/decryption_session.rs deleted file mode 100644 index dba470e7cef..00000000000 --- a/secret-store/src/key_server_cluster/client_sessions/decryption_session.rs +++ /dev/null @@ -1,1480 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use std::sync::Arc; -use futures::Oneshot; -use parking_lot::Mutex; -use ethereum_types::{Address, H256}; -use crypto::publickey::Secret; -use key_server_cluster::{Error, AclStorage, DocumentKeyShare, NodeId, SessionId, Requester, - EncryptedDocumentKeyShadow, SessionMeta}; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession, CompletionSignal}; -use key_server_cluster::message::{Message, DecryptionMessage, DecryptionConsensusMessage, RequestPartialDecryption, - PartialDecryption, DecryptionSessionError, DecryptionSessionCompleted, ConsensusMessage, InitializeConsensusSession, - ConfirmConsensusInitialization, DecryptionSessionDelegation, DecryptionSessionDelegationCompleted}; -use key_server_cluster::jobs::job_session::{JobSession, JobSessionState, JobTransport}; -use key_server_cluster::jobs::key_access_job::KeyAccessJob; -use key_server_cluster::jobs::decryption_job::{PartialDecryptionRequest, PartialDecryptionResponse, DecryptionJob}; -use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; - -/// Distributed decryption session. -/// Based on "ECDKG: A Distributed Key Generation Protocol Based on Elliptic Curve Discrete Logarithm" paper: -/// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.4128&rep=rep1&type=pdf -/// Brief overview: -/// 1) initialization: master node (which has received request for decrypting the secret) requests all other nodes to decrypt the secret -/// 2) ACL check: all nodes which have received the request are querying ACL-contract to check if requestor has access to the document -/// 3) partial decryption: every node which has succussfully checked access for the requestor do a partial decryption -/// 4) decryption: master node receives all partial decryptions of the secret and restores the secret -pub struct SessionImpl { - /// Session core. - core: SessionCore, - /// Session data. - data: Mutex, -} - -/// Immutable session data. -struct SessionCore { - /// Session metadata. - pub meta: SessionMeta, - /// Decryption session access key. - pub access_key: Secret, - /// Key share. - pub key_share: Option, - /// Cluster which allows this node to send messages to other nodes in the cluster. - pub cluster: Arc, - /// Session-level nonce. - pub nonce: u64, - /// Session completion signal. - pub completed: CompletionSignal, -} - -/// Decryption consensus session type. -type DecryptionConsensusSession = ConsensusSession; -/// Broadcast decryption job session type. -type BroadcastDecryptionJobSession = JobSession; - -/// Mutable session data. -struct SessionData { - /// Key version to use for decryption. - pub version: Option, - /// Session origin (if any). - pub origin: Option
, - /// Consensus-based decryption session. - pub consensus_session: DecryptionConsensusSession, - /// Broadcast decryption job. - pub broadcast_job_session: Option, - /// Is shadow decryption requested? - pub is_shadow_decryption: Option, - /// Decryption result must be reconstructed on all participating nodes. This is useful - /// for service contract API so that all nodes from consensus group can confirm decryption. - pub is_broadcast_session: Option, - /// Delegation status. - pub delegation_status: Option, - /// Decryption result. - pub result: Option>, -} - -/// SessionImpl creation parameters -pub struct SessionParams { - /// Session metadata. - pub meta: SessionMeta, - /// Session access key. - pub access_key: Secret, - /// Key share. - pub key_share: Option, - /// ACL storage. - pub acl_storage: Arc, - /// Cluster. - pub cluster: Arc, - /// Session nonce. - pub nonce: u64, -} - -/// Decryption consensus transport. -struct DecryptionConsensusTransport { - /// Session id. - id: SessionId, - /// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Session origin (if any). - origin: Option
, - /// Selected key version (on master node). - version: Option, - /// Cluster. - cluster: Arc, -} - -/// Decryption job transport -struct DecryptionJobTransport { - /// Session id. - id: SessionId, - //// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Is this a broadcast transport? If true, requests are not send and responses are sent only to non-master nodes. - is_broadcast_transport: bool, - /// Master node id. - master_node_id: NodeId, - /// Cluster. - cluster: Arc, -} - -/// Session delegation status. -enum DelegationStatus { - /// Delegated to other node. - DelegatedTo(NodeId), - /// Delegated from other node. - DelegatedFrom(NodeId, u64), -} - -impl SessionImpl { - /// Create new decryption session. - pub fn new( - params: SessionParams, - requester: Option, - ) -> Result<(Self, Oneshot>), Error> { - debug_assert_eq!(params.meta.threshold, params.key_share.as_ref().map(|ks| ks.threshold).unwrap_or_default()); - - // check that common_point and encrypted_point are already set - if let Some(key_share) = params.key_share.as_ref() { - // encrypted data must be set - if key_share.common_point.is_none() || key_share.encrypted_point.is_none() { - return Err(Error::DocumentKeyIsNotFound); - } - } - - let consensus_transport = DecryptionConsensusTransport { - id: params.meta.id.clone(), - access_key: params.access_key.clone(), - nonce: params.nonce, - origin: None, - version: None, - cluster: params.cluster.clone(), - }; - let consensus_session = ConsensusSession::new(ConsensusSessionParams { - meta: params.meta.clone(), - consensus_executor: match requester { - Some(requester) => KeyAccessJob::new_on_master(params.meta.id.clone(), params.acl_storage.clone(), requester), - None => KeyAccessJob::new_on_slave(params.meta.id.clone(), params.acl_storage.clone()), - }, - consensus_transport: consensus_transport, - })?; - - let (completed, oneshot) = CompletionSignal::new(); - Ok((SessionImpl { - core: SessionCore { - meta: params.meta, - access_key: params.access_key, - key_share: params.key_share, - cluster: params.cluster, - nonce: params.nonce, - completed, - }, - data: Mutex::new(SessionData { - version: None, - origin: None, - consensus_session: consensus_session, - broadcast_job_session: None, - is_shadow_decryption: None, - is_broadcast_session: None, - delegation_status: None, - result: None, - }), - }, oneshot)) - } - - /// Get this node id. - #[cfg(test)] - pub fn node(&self) -> &NodeId { - &self.core.meta.self_node_id - } - - /// Get this session access key. - #[cfg(test)] - pub fn access_key(&self) -> &Secret { - &self.core.access_key - } - - /// Get session state (tests only). - #[cfg(test)] - pub fn state(&self) -> ConsensusSessionState { - self.data.lock().consensus_session.state() - } - - /// Get decrypted secret - #[cfg(test)] - pub fn decrypted_secret(&self) -> Option> { - self.data.lock().result.clone() - } - - /// Get key requester. - pub fn requester(&self) -> Option { - self.data.lock().consensus_session.consensus_job().executor().requester().cloned() - } - - /// Get session origin. - pub fn origin(&self) -> Option
{ - self.data.lock().origin.clone() - } - - /// Get session completion result (if available). - pub fn result(&self) -> Option> { - self.data.lock().result.clone() - } - - /// Get broadcasted shadows. - pub fn broadcast_shadows(&self) -> Option>> { - let data = self.data.lock(); - - if data.result.is_none() || (data.is_broadcast_session, data.is_shadow_decryption) != (Some(true), Some(true)) { - return None; - } - - let proof = "data.is_shadow_decryption is true; decrypt_shadow.is_some() is checked in DecryptionJob::check_partial_response; qed"; - Some(match self.core.meta.master_node_id == self.core.meta.self_node_id { - true => data.consensus_session.computation_job().responses().iter() - .map(|(n, r)| (n.clone(), r.decrypt_shadow.clone().expect(proof))) - .collect(), - false => data.broadcast_job_session.as_ref().expect("session completed; is_shadow_decryption == true; we're on non-master node; qed").responses().iter() - .map(|(n, r)| (n.clone(), r.decrypt_shadow.clone().expect(proof))) - .collect(), - }) - } - - /// Delegate session to other node. - pub fn delegate(&self, master: NodeId, origin: Option
, version: H256, is_shadow_decryption: bool, is_broadcast_session: bool) -> Result<(), Error> { - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Err(Error::InvalidStateForRequest); - } - - let mut data = self.data.lock(); - if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { - return Err(Error::InvalidStateForRequest); - } - - data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(false); - self.core.cluster.send(&master, Message::Decryption(DecryptionMessage::DecryptionSessionDelegation(DecryptionSessionDelegation { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - origin: origin.map(Into::into), - requester: data.consensus_session.consensus_job().executor().requester() - .expect("signature is passed to master node on creation; session can be delegated from master node only; qed") - .clone().into(), - version: version.into(), - is_shadow_decryption: is_shadow_decryption, - is_broadcast_session: is_broadcast_session, - })))?; - data.delegation_status = Some(DelegationStatus::DelegatedTo(master)); - Ok(()) - } - - /// Initialize decryption session on master node. - pub fn initialize(&self, origin: Option
, version: H256, is_shadow_decryption: bool, is_broadcast_session: bool) -> Result<(), Error> { - debug_assert_eq!(self.core.meta.self_node_id, self.core.meta.master_node_id); - - // check if version exists - let key_version = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share.version(&version)?, - }; - - let mut data = self.data.lock(); - let non_isolated_nodes = self.core.cluster.nodes(); - let mut consensus_nodes: BTreeSet<_> = key_version.id_numbers.keys() - .filter(|n| non_isolated_nodes.contains(*n)) - .cloned() - .chain(::std::iter::once(self.core.meta.self_node_id.clone())) - .collect(); - if let Some(&DelegationStatus::DelegatedFrom(delegation_master, _)) = data.delegation_status.as_ref() { - consensus_nodes.remove(&delegation_master); - } - - data.consensus_session.consensus_job_mut().transport_mut().version = Some(version.clone()); - data.consensus_session.consensus_job_mut().transport_mut().origin = origin.clone(); - data.origin = origin; - data.version = Some(version.clone()); - data.is_shadow_decryption = Some(is_shadow_decryption); - data.is_broadcast_session = Some(is_broadcast_session); - data.consensus_session.initialize(consensus_nodes)?; - - if data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished { - Self::disseminate_jobs(&self.core, &mut *data, &version, is_shadow_decryption, is_broadcast_session)?; - - debug_assert!(data.consensus_session.state() == ConsensusSessionState::Finished); - let result = data.consensus_session.result()?; - Self::set_decryption_result(&self.core, &mut *data, Ok(result)); - } - - Ok(()) - } - - /// Process decryption message. - pub fn process_message(&self, sender: &NodeId, message: &DecryptionMessage) -> Result<(), Error> { - if self.core.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &DecryptionMessage::DecryptionConsensusMessage(ref message) => - self.on_consensus_message(sender, message), - &DecryptionMessage::RequestPartialDecryption(ref message) => - self.on_partial_decryption_requested(sender, message), - &DecryptionMessage::PartialDecryption(ref message) => - self.on_partial_decryption(sender, message), - &DecryptionMessage::DecryptionSessionError(ref message) => - self.process_node_error(Some(&sender), message.error.clone()), - &DecryptionMessage::DecryptionSessionCompleted(ref message) => - self.on_session_completed(sender, message), - &DecryptionMessage::DecryptionSessionDelegation(ref message) => - self.on_session_delegated(sender, message), - &DecryptionMessage::DecryptionSessionDelegationCompleted(ref message) => - self.on_session_delegation_completed(sender, message), - } - } - - /// When session is delegated to this node. - pub fn on_session_delegated(&self, sender: &NodeId, message: &DecryptionSessionDelegation) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - { - let mut data = self.data.lock(); - if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { - return Err(Error::InvalidStateForRequest); - } - - data.consensus_session.consensus_job_mut().executor_mut().set_requester(message.requester.clone().into()); - data.delegation_status = Some(DelegationStatus::DelegatedFrom(sender.clone(), message.session_nonce)); - } - - self.initialize(message.origin.clone().map(Into::into), message.version.clone().into(), message.is_shadow_decryption, message.is_broadcast_session) - } - - /// When delegated session is completed on other node. - pub fn on_session_delegation_completed(&self, sender: &NodeId, message: &DecryptionSessionDelegationCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Err(Error::InvalidStateForRequest); - } - - let mut data = self.data.lock(); - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(ref node)) if node == sender => (), - _ => return Err(Error::InvalidMessage), - } - - Self::set_decryption_result(&self.core, &mut *data, Ok(EncryptedDocumentKeyShadow { - decrypted_secret: message.decrypted_secret.clone().into(), - common_point: message.common_point.clone().map(Into::into), - decrypt_shadows: message.decrypt_shadows.clone().map(Into::into), - })); - - Ok(()) - } - - /// When consensus-related message is received. - pub fn on_consensus_message(&self, sender: &NodeId, message: &DecryptionConsensusMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - let mut data = self.data.lock(); - let is_establishing_consensus = data.consensus_session.state() == ConsensusSessionState::EstablishingConsensus; - if let &ConsensusMessage::InitializeConsensusSession(ref msg) = &message.message { - let version = msg.version.clone().into(); - let has_key_share = self.core.key_share.as_ref() - .map(|ks| ks.version(&version).is_ok()) - .unwrap_or(false); - data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(has_key_share); - data.version = Some(version); - data.origin = message.origin.clone().map(Into::into); - } - data.consensus_session.on_consensus_message(&sender, &message.message)?; - - let is_consensus_established = data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished; - if self.core.meta.self_node_id != self.core.meta.master_node_id || !is_establishing_consensus || !is_consensus_established { - return Ok(()); - } - - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - let is_shadow_decryption = data.is_shadow_decryption - .expect("we are on master node; on master node is_shadow_decryption is filled in initialize(); on_consensus_message follows initialize (state check in consensus_session); qed"); - let is_broadcast_session = data.is_broadcast_session - .expect("we are on master node; on master node is_broadcast_session is filled in initialize(); on_consensus_message follows initialize (state check in consensus_session); qed"); - Self::disseminate_jobs(&self.core, &mut *data, &version, is_shadow_decryption, is_broadcast_session) - } - - /// When partial decryption is requested. - pub fn on_partial_decryption_requested(&self, sender: &NodeId, message: &RequestPartialDecryption) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let key_share = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let mut data = self.data.lock(); - let key_version = key_share.version(data.version.as_ref().ok_or(Error::InvalidMessage)?)?.hash.clone(); - let requester_public = data.consensus_session.consensus_job().executor().requester() - .ok_or(Error::InvalidStateForRequest)? - .public(&self.core.meta.id) - .map_err(Error::InsufficientRequesterData)?; - let decryption_job = DecryptionJob::new_on_slave(self.core.meta.self_node_id.clone(), self.core.access_key.clone(), - requester_public.clone(), key_share.clone(), key_version)?; - let decryption_transport = self.core.decryption_transport(false); - - // update flags if not on master - if self.core.meta.self_node_id != self.core.meta.master_node_id { - data.is_shadow_decryption = Some(message.is_shadow_decryption); - data.is_broadcast_session = Some(message.is_broadcast_session); - } - - // respond to request - let partial_decryption = data.consensus_session.on_job_request(sender, PartialDecryptionRequest { - id: message.request_id.clone().into(), - is_shadow_decryption: message.is_shadow_decryption, - is_broadcast_session: message.is_broadcast_session, - other_nodes_ids: message.nodes.iter().cloned().map(Into::into).collect(), - }, decryption_job, decryption_transport)?; - - // ...and prepare decryption job session if we need to broadcast result - if message.is_broadcast_session { - let consensus_group: BTreeSet<_> = message.nodes.iter().cloned().map(Into::into).collect(); - let broadcast_decryption_job = DecryptionJob::new_on_master(self.core.meta.self_node_id.clone(), - self.core.access_key.clone(), requester_public, key_share.clone(), key_version, - message.is_shadow_decryption, message.is_broadcast_session)?; - Self::create_broadcast_decryption_job(&self.core, &mut *data, consensus_group, broadcast_decryption_job, - message.request_id.clone().into(), Some(partial_decryption.take_response()))?; - } - - Ok(()) - } - - /// When partial decryption is received. - pub fn on_partial_decryption(&self, sender: &NodeId, message: &PartialDecryption) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - let is_master_node = self.core.meta.self_node_id == self.core.meta.master_node_id; - let result = if is_master_node { - data.consensus_session.on_job_response(sender, PartialDecryptionResponse { - request_id: message.request_id.clone().into(), - shadow_point: message.shadow_point.clone().into(), - decrypt_shadow: message.decrypt_shadow.clone(), - })?; - - if data.consensus_session.state() != ConsensusSessionState::Finished && - data.consensus_session.state() != ConsensusSessionState::Failed { - return Ok(()); - } - - // send completion signal to all nodes, except for rejected nodes - if is_master_node { - for node in data.consensus_session.consensus_non_rejected_nodes() { - self.core.cluster.send(&node, Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(DecryptionSessionCompleted { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - })))?; - } - } - - data.consensus_session.result() - } else { - match data.broadcast_job_session.as_mut() { - Some(broadcast_job_session) => { - broadcast_job_session.on_partial_response(sender, PartialDecryptionResponse { - request_id: message.request_id.clone().into(), - shadow_point: message.shadow_point.clone().into(), - decrypt_shadow: message.decrypt_shadow.clone(), - })?; - - if broadcast_job_session.state() != JobSessionState::Finished && - broadcast_job_session.state() != JobSessionState::Failed { - return Ok(()); - } - - broadcast_job_session.result() - }, - None => return Err(Error::InvalidMessage), - } - }; - - Self::set_decryption_result(&self.core, &mut *data, result); - - Ok(()) - } - - /// When session is completed. - pub fn on_session_completed(&self, sender: &NodeId, message: &DecryptionSessionCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - // if it is a broadcast session, wait for all answers before completing the session - let decryption_result = match data.broadcast_job_session.as_ref() { - Some(broadcast_job_session) => { - if !broadcast_job_session.is_result_ready() { - return Err(Error::TooEarlyForRequest); - } - - Some(broadcast_job_session.result()) - }, - None => None, - }; - if let Some(decryption_result) = decryption_result { - Self::set_decryption_result(&self.core, &mut *data, decryption_result); - } - - data.consensus_session.on_session_completed(sender) - } - - /// Process error from the other node. - fn process_node_error(&self, node: Option<&NodeId>, error: Error) -> Result<(), Error> { - let mut data = self.data.lock(); - let is_self_node_error = node.map(|n| n == &self.core.meta.self_node_id).unwrap_or(false); - // error is always fatal if coming from this node - if is_self_node_error { - Self::set_decryption_result(&self.core, &mut *data, Err(error.clone())); - return Err(error); - } - - match { - match node { - Some(node) => data.consensus_session.on_node_error(node, error.clone()), - None => data.consensus_session.on_session_timeout(), - } - } { - Ok(false) => Ok(()), - Ok(true) => { - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - let proof = "on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when is_shadow_decryption.is_some(); qed"; - let is_shadow_decryption = data.is_shadow_decryption.expect(proof); - let is_broadcast_session = data.is_broadcast_session.expect(proof); - let disseminate_result = Self::disseminate_jobs(&self.core, &mut *data, &version, is_shadow_decryption, is_broadcast_session); - match disseminate_result { - Ok(()) => Ok(()), - Err(err) => { - warn!("{}: decryption session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - - Self::set_decryption_result(&self.core, &mut *data, Err(err.clone())); - Err(err) - } - } - }, - Err(err) => { - warn!("{}: decryption session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - - Self::set_decryption_result(&self.core, &mut *data, Err(err.clone())); - Err(err) - }, - } - } - - /// Disseminate jobs on session master. - fn disseminate_jobs(core: &SessionCore, data: &mut SessionData, version: &H256, is_shadow_decryption: bool, is_broadcast_session: bool) -> Result<(), Error> { - let key_share = match core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let key_version = key_share.version(version)?.hash.clone(); - let requester = data.consensus_session.consensus_job().executor().requester().ok_or(Error::InvalidStateForRequest)?.clone(); - let requester_public = requester.public(&core.meta.id).map_err(Error::InsufficientRequesterData)?; - let consensus_group = data.consensus_session.select_consensus_group()?.clone(); - let decryption_job = DecryptionJob::new_on_master(core.meta.self_node_id.clone(), - core.access_key.clone(), requester_public.clone(), key_share.clone(), key_version, - is_shadow_decryption, is_broadcast_session)?; - let decryption_request_id = decryption_job.request_id().clone() - .expect("DecryptionJob always have request_id when created on master; it is created using new_on_master above; qed"); - let decryption_transport = core.decryption_transport(false); - let is_broadcast_session = data.is_broadcast_session - .expect("disseminate_jobs is called on master node only; on master node is_broadcast_session is filled during initialization; qed"); - let self_response = data.consensus_session.disseminate_jobs(decryption_job, decryption_transport, is_broadcast_session)?; - - // ...and prepare decryption job session if we need to broadcast result - if is_broadcast_session { - let broadcast_decryption_job = DecryptionJob::new_on_master(core.meta.self_node_id.clone(), - core.access_key.clone(), requester_public, key_share.clone(), key_version, is_shadow_decryption, is_broadcast_session)?; - Self::create_broadcast_decryption_job(&core, data, consensus_group, broadcast_decryption_job, - decryption_request_id, self_response)?; - } - - Ok(()) - } - - /// Create broadcast decryption job. - fn create_broadcast_decryption_job(core: &SessionCore, data: &mut SessionData, mut consensus_group: BTreeSet, mut job: DecryptionJob, request_id: Secret, self_response: Option) -> Result<(), Error> { - consensus_group.insert(core.meta.self_node_id.clone()); - job.set_request_id(request_id.clone().into()); - - let transport = core.decryption_transport(true); - let mut job_session = JobSession::new(SessionMeta { - id: core.meta.id.clone(), - master_node_id: core.meta.self_node_id.clone(), - self_node_id: core.meta.self_node_id.clone(), - threshold: core.meta.threshold, - configured_nodes_count: core.meta.configured_nodes_count, - connected_nodes_count: core.meta.connected_nodes_count, - }, job, transport); - job_session.initialize(consensus_group, self_response, core.meta.self_node_id != core.meta.master_node_id)?; - data.broadcast_job_session = Some(job_session); - - Ok(()) - } - - /// Set decryption result. - fn set_decryption_result(core: &SessionCore, data: &mut SessionData, result: Result) { - if let Some(DelegationStatus::DelegatedFrom(master, nonce)) = data.delegation_status.take() { - // error means can't communicate => ignore it - let _ = match result.as_ref() { - Ok(document_key) => core.cluster.send(&master, Message::Decryption(DecryptionMessage::DecryptionSessionDelegationCompleted(DecryptionSessionDelegationCompleted { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: nonce, - decrypted_secret: document_key.decrypted_secret.clone().into(), - common_point: document_key.common_point.clone().map(Into::into), - decrypt_shadows: document_key.decrypt_shadows.clone(), - }))), - Err(error) => core.cluster.send(&master, Message::Decryption(DecryptionMessage::DecryptionSessionError(DecryptionSessionError { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: nonce, - error: error.clone().into(), - }))), - }; - } - - data.result = Some(result.clone()); - core.completed.send(result); - } -} - -impl ClusterSession for SessionImpl { - type Id = SessionIdWithSubSession; - type CreationData = Requester; - type SuccessfulResult = EncryptedDocumentKeyShadow; - - fn type_name() -> &'static str { - "decryption" - } - - fn id(&self) -> SessionIdWithSubSession { - SessionIdWithSubSession::new(self.core.meta.id.clone(), self.core.access_key.clone()) - } - - fn is_finished(&self) -> bool { - let data = self.data.lock(); - data.consensus_session.state() == ConsensusSessionState::Failed - || data.consensus_session.state() == ConsensusSessionState::Finished - || data.result.is_some() - } - - fn on_node_timeout(&self, node: &NodeId) { - // ignore error, only state matters - let _ = self.process_node_error(Some(node), Error::NodeDisconnected); - } - - fn on_session_timeout(&self) { - // ignore error, only state matters - let _ = self.process_node_error(None, Error::NodeDisconnected); - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - let is_fatal = self.process_node_error(Some(node), error.clone()).is_err(); - let is_this_node_error = *node == self.core.meta.self_node_id; - if is_fatal || is_this_node_error { - // error in signing session is non-fatal, if occurs on slave node - // => either respond with error - // => or broadcast error - let message = Message::Decryption(DecryptionMessage::DecryptionSessionError(DecryptionSessionError { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - error: error.clone().into(), - })); - - // do not bother processing send error, as we already processing error - let _ = if self.core.meta.master_node_id == self.core.meta.self_node_id { - self.core.cluster.broadcast(message) - } else { - self.core.cluster.send(&self.core.meta.master_node_id, message) - }; - } - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::Decryption(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl SessionCore { - pub fn decryption_transport(&self, is_broadcast_transport: bool) -> DecryptionJobTransport { - DecryptionJobTransport { - id: self.meta.id.clone(), - access_key: self.access_key.clone(), - nonce: self.nonce, - is_broadcast_transport: is_broadcast_transport, - master_node_id: self.meta.master_node_id.clone(), - cluster: self.cluster.clone(), - } - } -} - -impl JobTransport for DecryptionConsensusTransport { - type PartialJobRequest=Requester; - type PartialJobResponse=bool; - - fn send_partial_request(&self, node: &NodeId, request: Requester) -> Result<(), Error> { - let version = self.version.as_ref() - .expect("send_partial_request is called on initialized master node only; version is filled in before initialization starts on master node; qed"); - self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - origin: self.origin.clone().map(Into::into), - message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requester: request.into(), - version: version.clone().into(), - }) - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { - self.cluster.send(node, Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(DecryptionConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - origin: None, - message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: response, - }) - }))) - } -} - -impl JobTransport for DecryptionJobTransport { - type PartialJobRequest=PartialDecryptionRequest; - type PartialJobResponse=PartialDecryptionResponse; - - fn send_partial_request(&self, node: &NodeId, request: PartialDecryptionRequest) -> Result<(), Error> { - if !self.is_broadcast_transport { - self.cluster.send(node, Message::Decryption(DecryptionMessage::RequestPartialDecryption(RequestPartialDecryption { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - request_id: request.id.into(), - is_shadow_decryption: request.is_shadow_decryption, - is_broadcast_session: request.is_broadcast_session, - nodes: request.other_nodes_ids.into_iter().map(Into::into).collect(), - })))?; - } - - Ok(()) - } - - fn send_partial_response(&self, node: &NodeId, response: PartialDecryptionResponse) -> Result<(), Error> { - if !self.is_broadcast_transport || *node != self.master_node_id { - self.cluster.send(node, Message::Decryption(DecryptionMessage::PartialDecryption(PartialDecryption { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - request_id: response.request_id.into(), - shadow_point: response.shadow_point.into(), - decrypt_shadow: response.decrypt_shadow, - })))?; - } - - Ok(()) - } -} - -#[cfg(test)] -pub fn create_default_decryption_session() -> Arc { - use acl_storage::DummyAclStorage; - use key_server_cluster::cluster::tests::DummyCluster; - use ethereum_types::H512; - - Arc::new(SessionImpl::new(SessionParams { - meta: SessionMeta { - id: Default::default(), - self_node_id: Default::default(), - master_node_id: Default::default(), - threshold: 0, - configured_nodes_count: 0, - connected_nodes_count: 0, - }, - access_key: Secret::zero(), - key_share: Default::default(), - acl_storage: Arc::new(DummyAclStorage::default()), - cluster: Arc::new(DummyCluster::new(Default::default())), - nonce: 0, - }, Some(Requester::Public(H512::from_low_u64_be(2)))).unwrap().0) -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::collections::{BTreeMap, VecDeque}; - use acl_storage::DummyAclStorage; - use crypto::publickey::{KeyPair, Random, Generator, Public, Secret, public_to_address}; - use key_server_cluster::{NodeId, DocumentKeyShare, DocumentKeyShareVersion, SessionId, Requester, - Error, EncryptedDocumentKeyShadow, SessionMeta}; - use key_server_cluster::cluster::tests::DummyCluster; - use key_server_cluster::cluster_sessions::ClusterSession; - use key_server_cluster::decryption_session::{SessionImpl, SessionParams}; - use key_server_cluster::message::{self, Message, DecryptionMessage}; - use key_server_cluster::math; - use key_server_cluster::jobs::consensus_session::ConsensusSessionState; - use ethereum_types::{H512, Address}; - use std::str::FromStr; - - const SECRET_PLAIN: &'static str = "d2b57ae7619e070af0af6bc8c703c0cd27814c54d5d6a999cacac0da34ede279ca0d9216e85991029e54e2f0c92ee0bd30237725fa765cbdbfc4529489864c5f"; - - fn prepare_decryption_sessions() -> (KeyPair, Vec>, Vec>, Vec) { - // prepare encrypted data + cluster configuration for scheme 4-of-5 - let session_id = SessionId::default(); - let access_key = Random.generate().unwrap().secret().clone(); - let secret_shares: Vec = vec![ - "834cb736f02d9c968dfaf0c37658a1d86ff140554fc8b59c9fdad5a8cf810eec".parse().unwrap(), - "5a3c1d90fafafa66bb808bcc464354a98b05e6b2c95b5f609d4511cdd1b17a0b".parse().unwrap(), - "71bf61e7848e08e3a8486c308ce521bdacfebcf9116a0151447eb301f3a2d0e9".parse().unwrap(), - "80c0e5e2bea66fa9b2e07f7ce09630a9563e8242446d5ee63221feb09c4338f4".parse().unwrap(), - "c06546b5669877ba579ca437a5602e89425c53808c708d44ccd6afcaa4610fad".parse().unwrap(), - ]; - let id_numbers: Vec<(NodeId, Secret)> = vec![ - (H512::from_str("b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8").unwrap(), - "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap()), - (H512::from_str("1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb").unwrap(), - "00125d85a05e5e63e214cb60fe63f132eec8a103aa29266b7e6e6c5b7597230b".parse().unwrap()), - (H512::from_str("99e82b163b062d55a64085bacfd407bb55f194ba5fb7a1af9c34b84435455520f1372e0e650a4f91aed0058cb823f62146ccb5599c8d13372c300dea866b69fc").unwrap(), - "f43ac0fba42a5b6ed95707d2244659e89ba877b1c9b82c0d0a9dcf834e80fc62".parse().unwrap()), - (H512::from_str("7e05df9dd077ec21ed4bc45c9fe9e0a43d65fa4be540630de615ced5e95cf5c3003035eb713317237d7667feeeb64335525158f5f7411f67aca9645169ea554c").unwrap(), - "5a324938dfb2516800487d25ab7289ba8ec38811f77c3df602e4e65e3c9acd9f".parse().unwrap()), - (H512::from_str("321977760d1d8e15b047a309e4c7fe6f355c10bb5a06c68472b676926427f69f229024fa2692c10da167d14cdc77eb95d0fce68af0a0f704f0d3db36baa83bb2").unwrap(), - "12cf422d50002d04e52bd4906fd7f5f235f051ca36abfe37e061f8da248008d8".parse().unwrap()), - ]; - let common_point: Public = H512::from_str("6962be696e1bcbba8e64cc7fddf140f854835354b5804f3bb95ae5a2799130371b589a131bd39699ac7174ccb35fc4342dab05331202209582fc8f3a40916ab0").unwrap(); - let encrypted_point: Public = H512::from_str("b07031982bde9890e12eff154765f03c56c3ab646ad47431db5dd2d742a9297679c4c65b998557f8008469afd0c43d40b6c5f6c6a1c7354875da4115237ed87a").unwrap(); - let encrypted_datas: Vec<_> = (0..5).map(|i| DocumentKeyShare { - author: Default::default(), - threshold: 3, - public: Default::default(), - common_point: Some(common_point.clone()), - encrypted_point: Some(encrypted_point.clone()), - versions: vec![DocumentKeyShareVersion { - hash: Default::default(), - id_numbers: id_numbers.clone().into_iter().collect(), - secret_share: secret_shares[i].clone(), - }], - }).collect(); - let acl_storages: Vec<_> = (0..5).map(|_| Arc::new(DummyAclStorage::default())).collect(); - let clusters: Vec<_> = (0..5).map(|i| { - let cluster = Arc::new(DummyCluster::new(id_numbers.iter().nth(i).clone().unwrap().0)); - for id_number in &id_numbers { - cluster.add_node(id_number.0.clone()); - } - cluster - }).collect(); - let requester = Random.generate().unwrap(); - let signature = Some(crypto::publickey::sign(requester.secret(), &SessionId::default()).unwrap()); - let sessions: Vec<_> = (0..5).map(|i| SessionImpl::new(SessionParams { - meta: SessionMeta { - id: session_id.clone(), - self_node_id: id_numbers.iter().nth(i).clone().unwrap().0, - master_node_id: id_numbers.iter().nth(0).clone().unwrap().0, - threshold: encrypted_datas[i].threshold, - configured_nodes_count: 5, - connected_nodes_count: 5, - }, - access_key: access_key.clone(), - key_share: Some(encrypted_datas[i].clone()), - acl_storage: acl_storages[i].clone(), - cluster: clusters[i].clone(), - nonce: 0, - }, if i == 0 { signature.clone().map(Into::into) } else { None }).unwrap().0).collect(); - - (requester, clusters, acl_storages, sessions) - } - - fn do_messages_exchange(clusters: &[Arc], sessions: &[SessionImpl]) -> Result<(), Error> { - do_messages_exchange_until(clusters, sessions, |_, _, _| false) - } - - fn do_messages_exchange_until(clusters: &[Arc], sessions: &[SessionImpl], mut cond: F) -> Result<(), Error> where F: FnMut(&NodeId, &NodeId, &Message) -> bool { - let mut queue: VecDeque<(NodeId, NodeId, Message)> = VecDeque::new(); - while let Some((mut from, mut to, mut message)) = clusters.iter().filter_map(|c| c.take_message().map(|(to, msg)| (c.node(), to, msg))).next() { - if cond(&from, &to, &message) { - break; - } - - let mut is_queued_message = false; - loop { - let session = &sessions[sessions.iter().position(|s| s.node() == &to).unwrap()]; - match session.on_message(&from, &message) { - Ok(_) => { - if let Some(qmessage) = queue.pop_front() { - from = qmessage.0; - to = qmessage.1; - message = qmessage.2; - is_queued_message = true; - continue; - } - break; - }, - Err(Error::TooEarlyForRequest) => { - if is_queued_message { - queue.push_front((from, to, message)); - } else { - queue.push_back((from, to, message)); - } - break; - }, - Err(err) => return Err(err), - } - } - } - - Ok(()) - } - - #[test] - fn constructs_in_cluster_of_single_node() { - let mut nodes = BTreeMap::new(); - let self_node_id = Random.generate().unwrap().public().clone(); - nodes.insert(self_node_id, Random.generate().unwrap().secret().clone()); - match SessionImpl::new(SessionParams { - meta: SessionMeta { - id: SessionId::default(), - self_node_id: self_node_id.clone(), - master_node_id: self_node_id.clone(), - threshold: 0, - configured_nodes_count: 1, - connected_nodes_count: 1, - }, - access_key: Random.generate().unwrap().secret().clone(), - key_share: Some(DocumentKeyShare { - author: Default::default(), - threshold: 0, - public: Default::default(), - common_point: Some(Random.generate().unwrap().public().clone()), - encrypted_point: Some(Random.generate().unwrap().public().clone()), - versions: vec![DocumentKeyShareVersion { - hash: Default::default(), - id_numbers: nodes, - secret_share: Random.generate().unwrap().secret().clone(), - }], - }), - acl_storage: Arc::new(DummyAclStorage::default()), - cluster: Arc::new(DummyCluster::new(self_node_id.clone())), - nonce: 0, - }, Some(Requester::Signature(crypto::publickey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()))) { - Ok(_) => (), - _ => panic!("unexpected"), - } - } - - #[test] - fn fails_to_initialize_if_does_not_have_a_share() { - let self_node_id = Random.generate().unwrap().public().clone(); - let session = SessionImpl::new(SessionParams { - meta: SessionMeta { - id: SessionId::default(), - self_node_id: self_node_id.clone(), - master_node_id: self_node_id.clone(), - threshold: 0, - configured_nodes_count: 1, - connected_nodes_count: 1, - }, - access_key: Random.generate().unwrap().secret().clone(), - key_share: None, - acl_storage: Arc::new(DummyAclStorage::default()), - cluster: Arc::new(DummyCluster::new(self_node_id.clone())), - nonce: 0, - }, Some(Requester::Signature( - crypto::publickey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap() - ))).unwrap().0; - assert_eq!(session.initialize(Default::default(), Default::default(), false, false), Err(Error::InvalidMessage)); - } - - #[test] - fn fails_to_initialize_if_threshold_is_wrong() { - let mut nodes = BTreeMap::new(); - let self_node_id = Random.generate().unwrap().public().clone(); - nodes.insert(self_node_id.clone(), Random.generate().unwrap().secret().clone()); - nodes.insert(Random.generate().unwrap().public().clone(), Random.generate().unwrap().secret().clone()); - let session = SessionImpl::new(SessionParams { - meta: SessionMeta { - id: SessionId::default(), - self_node_id: self_node_id.clone(), - master_node_id: self_node_id.clone(), - threshold: 2, - configured_nodes_count: 1, - connected_nodes_count: 1, - }, - access_key: Random.generate().unwrap().secret().clone(), - key_share: Some(DocumentKeyShare { - author: Default::default(), - threshold: 2, - public: Default::default(), - common_point: Some(Random.generate().unwrap().public().clone()), - encrypted_point: Some(Random.generate().unwrap().public().clone()), - versions: vec![DocumentKeyShareVersion { - hash: Default::default(), - id_numbers: nodes, - secret_share: Random.generate().unwrap().secret().clone(), - }], - }), - acl_storage: Arc::new(DummyAclStorage::default()), - cluster: Arc::new(DummyCluster::new(self_node_id.clone())), - nonce: 0, - }, Some(Requester::Signature( - crypto::publickey::sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap() - ))).unwrap().0; - assert_eq!(session.initialize(Default::default(), Default::default(), false, false), Err(Error::ConsensusUnreachable)); - } - - #[test] - fn fails_to_initialize_when_already_initialized() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(), ()); - assert_eq!(sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn fails_to_accept_initialization_when_already_initialized() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(), ()); - assert_eq!(sessions[0].on_consensus_message(sessions[1].node(), &message::DecryptionConsensusMessage { - session: SessionId::default().into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 0, - origin: None, - message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { - requester: Requester::Signature(crypto::publickey::sign( - Random.generate().unwrap().secret(), &SessionId::default()).unwrap()).into(), - version: Default::default(), - }), - }).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn fails_to_partial_decrypt_if_requested_by_slave() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[1].on_consensus_message(sessions[0].node(), &message::DecryptionConsensusMessage { - session: SessionId::default().into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 0, - origin: None, - message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { - requester: Requester::Signature(crypto::publickey::sign(Random.generate().unwrap().secret(), - &SessionId::default()).unwrap()).into(), - version: Default::default(), - }), - }).unwrap(), ()); - assert_eq!(sessions[1].on_partial_decryption_requested(sessions[2].node(), &message::RequestPartialDecryption { - session: SessionId::default().into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 0, - request_id: Random.generate().unwrap().secret().clone().into(), - is_shadow_decryption: false, - is_broadcast_session: false, - nodes: sessions.iter().map(|s| s.node().clone().into()).take(4).collect(), - }).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn fails_to_partial_decrypt_if_wrong_number_of_nodes_participating() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[1].on_consensus_message(sessions[0].node(), &message::DecryptionConsensusMessage { - session: SessionId::default().into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 0, - origin: None, - message: message::ConsensusMessage::InitializeConsensusSession(message::InitializeConsensusSession { - requester: Requester::Signature(crypto::publickey::sign(Random.generate().unwrap().secret(), - &SessionId::default()).unwrap()).into(), - version: Default::default(), - }), - }).unwrap(), ()); - assert_eq!(sessions[1].on_partial_decryption_requested(sessions[0].node(), &message::RequestPartialDecryption { - session: SessionId::default().into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 0, - request_id: Random.generate().unwrap().secret().clone().into(), - is_shadow_decryption: false, - is_broadcast_session: false, - nodes: sessions.iter().map(|s| s.node().clone().into()).take(2).collect(), - }).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn fails_to_accept_partial_decrypt_if_not_waiting() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[0].on_partial_decryption(sessions[1].node(), &message::PartialDecryption { - session: SessionId::default().into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 0, - request_id: Random.generate().unwrap().secret().clone().into(), - shadow_point: Random.generate().unwrap().public().clone().into(), - decrypt_shadow: None, - }).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn fails_to_accept_partial_decrypt_twice() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - let mut pd_from = None; - let mut pd_msg = None; - do_messages_exchange_until(&clusters, &sessions, |from, _, msg| match msg { - &Message::Decryption(DecryptionMessage::PartialDecryption(ref msg)) => { - pd_from = Some(from.clone()); - pd_msg = Some(msg.clone()); - true - }, - _ => false, - }).unwrap(); - - assert_eq!(sessions[0].on_partial_decryption(pd_from.as_ref().unwrap(), &pd_msg.clone().unwrap()).unwrap(), ()); - assert_eq!(sessions[0].on_partial_decryption(pd_from.as_ref().unwrap(), &pd_msg.unwrap()).unwrap_err(), Error::InvalidNodeForRequest); - } - - #[test] - fn decryption_fails_on_session_timeout() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert!(sessions[0].decrypted_secret().is_none()); - sessions[0].on_session_timeout(); - assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap_err(), Error::ConsensusTemporaryUnreachable); - } - - #[test] - fn node_is_marked_rejected_when_timed_out_during_initialization_confirmation() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - // 1 node disconnects => we still can recover secret - sessions[0].on_node_timeout(sessions[1].node()); - assert!(sessions[0].data.lock().consensus_session.consensus_job().rejects().contains_key(sessions[1].node())); - assert!(sessions[0].state() == ConsensusSessionState::EstablishingConsensus); - - // 2 node are disconnected => we can not recover secret - sessions[0].on_node_timeout(sessions[2].node()); - assert!(sessions[0].state() == ConsensusSessionState::Failed); - } - - #[test] - fn session_does_not_fail_if_rejected_node_disconnects() { - let (_, clusters, acl_storages, sessions) = prepare_decryption_sessions(); - let key_pair = Random.generate().unwrap(); - - acl_storages[1].prohibit(public_to_address(key_pair.public()), SessionId::default()); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); - - // 1st node disconnects => ignore this - sessions[0].on_node_timeout(sessions[1].node()); - assert_eq!(sessions[0].state(), ConsensusSessionState::EstablishingConsensus); - } - - #[test] - fn session_does_not_fail_if_requested_node_disconnects() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); - - // 1 node disconnects => we still can recover secret - sessions[0].on_node_timeout(sessions[1].node()); - assert!(sessions[0].state() == ConsensusSessionState::EstablishingConsensus); - - // 2 node are disconnected => we can not recover secret - sessions[0].on_node_timeout(sessions[2].node()); - assert!(sessions[0].state() == ConsensusSessionState::Failed); - } - - #[test] - fn session_does_not_fail_if_node_with_shadow_point_disconnects() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults - && sessions[0].data.lock().consensus_session.computation_job().responses().len() == 2).unwrap(); - - // disconnects from the node which has already sent us its own shadow point - let disconnected = sessions[0].data.lock(). - consensus_session.computation_job().responses().keys() - .filter(|n| *n != sessions[0].node()) - .cloned().nth(0).unwrap(); - sessions[0].on_node_timeout(&disconnected); - assert_eq!(sessions[0].state(), ConsensusSessionState::EstablishingConsensus); - } - - #[test] - fn session_restarts_if_confirmed_node_disconnects() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); - - // disconnects from the node which has already confirmed its participation - let disconnected = sessions[0].data.lock().consensus_session.computation_job().requests().iter().cloned().nth(0).unwrap(); - sessions[0].on_node_timeout(&disconnected); - assert_eq!(sessions[0].state(), ConsensusSessionState::EstablishingConsensus); - assert!(sessions[0].data.lock().consensus_session.computation_job().rejects().contains_key(&disconnected)); - assert!(!sessions[0].data.lock().consensus_session.computation_job().requests().contains(&disconnected)); - } - - #[test] - fn session_does_not_fail_if_non_master_node_disconnects_from_non_master_node() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange_until(&clusters, &sessions, |_, _, _| sessions[0].state() == ConsensusSessionState::WaitingForPartialResults).unwrap(); - - // disconnects from the node which has already confirmed its participation - sessions[1].on_node_timeout(sessions[2].node()); - assert!(sessions[0].state() == ConsensusSessionState::WaitingForPartialResults); - assert!(sessions[1].state() == ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn complete_dec_session() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - - // now let's try to do a decryption - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange(&clusters, &sessions).unwrap(); - - // now check that: - // 1) 5 of 5 sessions are in Finished state - assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 5); - // 2) 1 session has decrypted key value - assert!(sessions.iter().skip(1).all(|s| s.decrypted_secret().is_none())); - - assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), - common_point: None, - decrypt_shadows: None, - }); - } - - #[test] - fn complete_shadow_dec_session() { - let (key_pair, clusters, _, sessions) = prepare_decryption_sessions(); - - // now let's try to do a decryption - sessions[0].initialize(Default::default(), Default::default(), true, false).unwrap(); - - do_messages_exchange(&clusters, &sessions).unwrap(); - - // now check that: - // 1) 5 of 5 sessions are in Finished state - assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 5); - // 2) 1 session has decrypted key value - assert!(sessions.iter().skip(1).all(|s| s.decrypted_secret().is_none())); - - let decrypted_secret = sessions[0].decrypted_secret().unwrap().unwrap(); - // check that decrypted_secret != SECRET_PLAIN - assert!(decrypted_secret.decrypted_secret != H512::from_str(SECRET_PLAIN).unwrap()); - // check that common point && shadow coefficients are returned - assert!(decrypted_secret.common_point.is_some()); - assert!(decrypted_secret.decrypt_shadows.is_some()); - // check that KS client is able to restore original secret - use crypto::DEFAULT_MAC; - use crypto::publickey::ecies::decrypt; - let decrypt_shadows: Vec<_> = decrypted_secret.decrypt_shadows.unwrap().into_iter() - .map(|c| Secret::copy_from_slice(&decrypt(key_pair.secret(), &DEFAULT_MAC, &c).unwrap()).unwrap()) - .collect(); - let decrypted_secret = math::decrypt_with_shadow_coefficients(decrypted_secret.decrypted_secret, decrypted_secret.common_point.unwrap(), decrypt_shadows).unwrap(); - assert_eq!(decrypted_secret, H512::from_str(SECRET_PLAIN).unwrap()); - } - - #[test] - fn failed_dec_session() { - let (key_pair, clusters, acl_storages, sessions) = prepare_decryption_sessions(); - - // now let's try to do a decryption - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - // we need 4 out of 5 nodes to agree to do a decryption - // let's say that 2 of these nodes are disagree - acl_storages[1].prohibit(public_to_address(key_pair.public()), SessionId::default()); - acl_storages[2].prohibit(public_to_address(key_pair.public()), SessionId::default()); - - assert_eq!(do_messages_exchange(&clusters, &sessions).unwrap_err(), Error::ConsensusUnreachable); - - // check that 3 nodes have failed state - assert_eq!(sessions[0].state(), ConsensusSessionState::Failed); - assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Failed).count(), 3); - } - - #[test] - fn complete_dec_session_with_acl_check_failed_on_master() { - let (key_pair, clusters, acl_storages, sessions) = prepare_decryption_sessions(); - - // we need 4 out of 5 nodes to agree to do a decryption - // let's say that 1 of these nodes (master) is disagree - acl_storages[0].prohibit(public_to_address(key_pair.public()), SessionId::default()); - - // now let's try to do a decryption - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - - do_messages_exchange(&clusters, &sessions).unwrap(); - - // now check that: - // 1) 4 of 5 sessions are in Finished state - assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 5); - // 2) 1 session has decrypted key value - assert!(sessions.iter().skip(1).all(|s| s.decrypted_secret().is_none())); - assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), - common_point: None, - decrypt_shadows: None, - }); - } - - #[test] - fn decryption_message_fails_when_nonce_is_wrong() { - let (_, _, _, sessions) = prepare_decryption_sessions(); - assert_eq!(sessions[1].process_message(sessions[0].node(), &message::DecryptionMessage::DecryptionSessionCompleted( - message::DecryptionSessionCompleted { - session: SessionId::default().into(), - sub_session: sessions[0].access_key().clone().into(), - session_nonce: 10, - } - )), Err(Error::ReplayProtection)); - } - - #[test] - fn decryption_works_when_delegated_to_other_node() { - let (_, clusters, _, mut sessions) = prepare_decryption_sessions(); - - // let's say node1 doesn't have a share && delegates decryption request to node0 - // initially session is created on node1 => node1 is master for itself, but for other nodes node0 is still master - sessions[1].core.meta.master_node_id = sessions[1].core.meta.self_node_id.clone(); - sessions[1].data.lock().consensus_session.consensus_job_mut().executor_mut().set_requester( - sessions[0].data.lock().consensus_session.consensus_job().executor().requester().unwrap().clone() - ); - - // now let's try to do a decryption - sessions[1].delegate(sessions[0].core.meta.self_node_id.clone(), Default::default(), Default::default(), false, false).unwrap(); - do_messages_exchange(&clusters, &sessions).unwrap(); - - // now check that: - // 1) 4 of 5 sessions are in Finished state - assert_eq!(sessions.iter().filter(|s| s.state() == ConsensusSessionState::Finished).count(), 4); - // 2) 1 session has decrypted key value - assert_eq!(sessions[1].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), - common_point: None, - decrypt_shadows: None, - }); - } - - #[test] - fn decryption_works_when_share_owners_are_isolated() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - - // we need 4 out of 5 nodes to agree to do a decryption - // let's say that 1 of these nodes (master) is isolated - let isolated_node_id = sessions[4].core.meta.self_node_id.clone(); - for cluster in &clusters { - cluster.remove_node(&isolated_node_id); - } - - // now let's try to do a decryption - sessions[0].initialize(Default::default(), Default::default(), false, false).unwrap(); - do_messages_exchange(&clusters, &sessions).unwrap(); - - assert_eq!(sessions[0].decrypted_secret().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), - common_point: None, - decrypt_shadows: None, - }); - } - - #[test] - fn decryption_result_restored_on_all_nodes_if_broadcast_session_is_completed() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), false, true).unwrap(); - do_messages_exchange(&clusters, &sessions).unwrap(); - - // decryption result must be the same and available on 4 nodes - let result = sessions[0].decrypted_secret(); - assert!(result.clone().unwrap().is_ok()); - assert_eq!(result.clone().unwrap().unwrap(), EncryptedDocumentKeyShadow { - decrypted_secret: H512::from_str(SECRET_PLAIN).unwrap(), - common_point: None, - decrypt_shadows: None, - }); - assert_eq!(3, sessions.iter().skip(1).filter(|s| s.decrypted_secret() == result).count()); - assert_eq!(1, sessions.iter().skip(1).filter(|s| s.decrypted_secret().is_none()).count()); - } - - #[test] - fn decryption_shadows_restored_on_all_nodes_if_shadow_broadcast_session_is_completed() { - let (key_pair, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Default::default(), Default::default(), true, true).unwrap(); - do_messages_exchange(&clusters, &sessions).unwrap(); - - // decryption shadows must be the same and available on 4 nodes - let broadcast_shadows = sessions[0].broadcast_shadows(); - assert!(broadcast_shadows.is_some()); - assert_eq!(3, sessions.iter().skip(1).filter(|s| s.broadcast_shadows() == broadcast_shadows).count()); - assert_eq!(1, sessions.iter().skip(1).filter(|s| s.broadcast_shadows().is_none()).count()); - - // 4 nodes must be able to recover original secret - use crypto::DEFAULT_MAC; - use crypto::publickey::ecies::decrypt; - let result = sessions[0].decrypted_secret().unwrap().unwrap(); - assert_eq!(3, sessions.iter().skip(1).filter(|s| s.decrypted_secret() == Some(Ok(result.clone()))).count()); - let decrypt_shadows: Vec<_> = result.decrypt_shadows.unwrap().into_iter() - .map(|c| Secret::copy_from_slice(&decrypt(key_pair.secret(), &DEFAULT_MAC, &c).unwrap()).unwrap()) - .collect(); - let decrypted_secret = math::decrypt_with_shadow_coefficients(result.decrypted_secret, result.common_point.unwrap(), decrypt_shadows).unwrap(); - assert_eq!(decrypted_secret, H512::from_str(SECRET_PLAIN).unwrap()); - } - - #[test] - fn decryption_session_origin_is_known_to_all_initialized_nodes() { - let (_, clusters, _, sessions) = prepare_decryption_sessions(); - sessions[0].initialize(Some(Address::from_low_u64_be(1)), Default::default(), true, true).unwrap(); - do_messages_exchange(&clusters, &sessions).unwrap(); - - // all session must have origin set - assert_eq!(5, sessions.iter().filter(|s| s.origin() == Some(Address::from_low_u64_be(1))).count()); - } -} diff --git a/secret-store/src/key_server_cluster/client_sessions/encryption_session.rs b/secret-store/src/key_server_cluster/client_sessions/encryption_session.rs deleted file mode 100644 index 05713b1dfeb..00000000000 --- a/secret-store/src/key_server_cluster/client_sessions/encryption_session.rs +++ /dev/null @@ -1,344 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeMap; -use std::fmt::{Debug, Formatter, Error as FmtError}; -use std::sync::Arc; -use futures::Oneshot; -use parking_lot::Mutex; -use ethereum_types::Address; -use crypto::publickey::Public; -use key_server_cluster::{Error, NodeId, SessionId, Requester, KeyStorage, - DocumentKeyShare, ServerKeyId}; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{ClusterSession, CompletionSignal}; -use key_server_cluster::message::{Message, EncryptionMessage, InitializeEncryptionSession, - ConfirmEncryptionInitialization, EncryptionSessionError}; - -/// Encryption (distributed key generation) session. -/// Based on "ECDKG: A Distributed Key Generation Protocol Based on Elliptic Curve Discrete Logarithm" paper: -/// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.4128&rep=rep1&type=pdf -/// Brief overview: -/// 1) initialization: master node (which has received request for storing the secret) initializes the session on all other nodes -/// 2) master node sends common_point + encrypted_point to all other nodes -/// 3) common_point + encrypted_point are saved on all nodes -/// 4) in case of error, previous values are restored -pub struct SessionImpl { - /// Unique session id. - id: SessionId, - /// Public identifier of this node. - self_node_id: NodeId, - /// Encrypted data. - encrypted_data: Option, - /// Key storage. - key_storage: Arc, - /// Cluster which allows this node to send messages to other nodes in the cluster. - cluster: Arc, - /// Session nonce. - nonce: u64, - /// Session completion signal. - completed: CompletionSignal<()>, - /// Mutable session data. - data: Mutex, -} - -/// SessionImpl creation parameters -pub struct SessionParams { - /// SessionImpl identifier. - pub id: SessionId, - /// Id of node, on which this session is running. - pub self_node_id: Public, - /// Encrypted data (result of running generation_session::SessionImpl). - pub encrypted_data: Option, - /// Key storage. - pub key_storage: Arc, - /// Cluster - pub cluster: Arc, - /// Session nonce. - pub nonce: u64, -} - -/// Mutable data of encryption (distributed key generation) session. -#[derive(Debug)] -struct SessionData { - /// Current state of the session. - state: SessionState, - /// Nodes-specific data. - nodes: BTreeMap, - /// Encryption session result. - result: Option>, -} - -/// Mutable node-specific data. -#[derive(Debug, Clone)] -struct NodeData { - // === Values, filled during initialization phase === - /// Flags marking that node has confirmed session initialization. - pub initialization_confirmed: bool, -} - -/// Encryption (distributed key generation) session state. -#[derive(Debug, Clone, PartialEq)] -pub enum SessionState { - // === Initialization states === - /// Every node starts in this state. - WaitingForInitialization, - /// Master node waits for every other node to confirm initialization. - WaitingForInitializationConfirm, - - // === Final states of the session === - /// Encryption data is saved. - Finished, - /// Failed to save encryption data. - Failed, -} - -impl SessionImpl { - /// Create new encryption session. - pub fn new(params: SessionParams) -> Result<(Self, Oneshot>), Error> { - check_encrypted_data(params.encrypted_data.as_ref())?; - - let (completed, oneshot) = CompletionSignal::new(); - Ok((SessionImpl { - id: params.id, - self_node_id: params.self_node_id, - encrypted_data: params.encrypted_data, - key_storage: params.key_storage, - cluster: params.cluster, - nonce: params.nonce, - completed, - data: Mutex::new(SessionData { - state: SessionState::WaitingForInitialization, - nodes: BTreeMap::new(), - result: None, - }), - }, oneshot)) - } - - /// Get this node Id. - pub fn node(&self) -> &NodeId { - &self.self_node_id - } - - /// Start new session initialization. This must be called on master node. - pub fn initialize(&self, requester: Requester, common_point: Public, encrypted_point: Public) -> Result<(), Error> { - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // update state - data.state = SessionState::WaitingForInitializationConfirm; - data.nodes.extend(self.cluster.nodes().into_iter().map(|n| (n, NodeData { - initialization_confirmed: &n == self.node(), - }))); - - // TODO [Sec]: id signature is not enough here, as it was already used in key generation - // TODO [Reliability]: there could be situation when some nodes have failed to store encrypted data - // => potential problems during restore. some confirmation step is needed (2pc)? - // save encryption data - if let Some(encrypted_data) = self.encrypted_data.clone() { - let requester_address = requester.address(&self.id).map_err(Error::InsufficientRequesterData)?; - update_encrypted_data(&self.key_storage, self.id.clone(), - encrypted_data, requester_address, common_point.clone(), encrypted_point.clone())?; - } - - // start initialization - if data.nodes.len() > 1 { - self.cluster.broadcast(Message::Encryption(EncryptionMessage::InitializeEncryptionSession(InitializeEncryptionSession { - session: self.id.clone().into(), - session_nonce: self.nonce, - requester: requester.into(), - common_point: common_point.into(), - encrypted_point: encrypted_point.into(), - }))) - } else { - data.state = SessionState::Finished; - data.result = Some(Ok(())); - self.completed.send(Ok(())); - - Ok(()) - } - } - - /// When session initialization message is received. - pub fn on_initialize_session(&self, sender: NodeId, message: &InitializeEncryptionSession) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // check that the requester is the author of the encrypted data - if let Some(encrypted_data) = self.encrypted_data.clone() { - let requester: Requester = message.requester.clone().into(); - let requester_address = requester.address(&self.id).map_err(Error::InsufficientRequesterData)?; - update_encrypted_data(&self.key_storage, self.id.clone(), - encrypted_data, requester_address, message.common_point.clone().into(), message.encrypted_point.clone().into())?; - } - - // update state - data.state = SessionState::Finished; - - // send confirmation back to master node - self.cluster.send(&sender, Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(ConfirmEncryptionInitialization { - session: self.id.clone().into(), - session_nonce: self.nonce, - }))) - } - - /// When session initialization confirmation message is reeived. - pub fn on_confirm_initialization(&self, sender: NodeId, message: &ConfirmEncryptionInitialization) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - debug_assert!(data.nodes.contains_key(&sender)); - - // check if all nodes have confirmed initialization - data.nodes.get_mut(&sender) - .expect("message is received from cluster; nodes contains all cluster nodes; qed") - .initialization_confirmed = true; - if !data.nodes.values().all(|n| n.initialization_confirmed) { - return Ok(()); - } - - // update state - data.state = SessionState::Finished; - data.result = Some(Ok(())); - self.completed.send(Ok(())); - - Ok(()) - } -} - -impl ClusterSession for SessionImpl { - type Id = SessionId; - type CreationData = (); - type SuccessfulResult = (); - - fn type_name() -> &'static str { - "encryption" - } - - fn id(&self) -> SessionId { - self.id.clone() - } - - fn is_finished(&self) -> bool { - let data = self.data.lock(); - data.state == SessionState::Failed - || data.state == SessionState::Finished - } - - fn on_node_timeout(&self, node: &NodeId) { - let mut data = self.data.lock(); - - warn!("{}: encryption session failed because {} connection has timeouted", self.node(), node); - - data.state = SessionState::Failed; - data.result = Some(Err(Error::NodeDisconnected)); - self.completed.send(Err(Error::NodeDisconnected)); - } - - fn on_session_timeout(&self) { - let mut data = self.data.lock(); - - warn!("{}: encryption session failed with timeout", self.node()); - - data.state = SessionState::Failed; - data.result = Some(Err(Error::NodeDisconnected)); - self.completed.send(Err(Error::NodeDisconnected)); - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - // error in encryption session is considered fatal - // => broadcast error if error occured on this node - if *node == self.self_node_id { - // do not bother processing send error, as we already processing error - let _ = self.cluster.broadcast(Message::Encryption(EncryptionMessage::EncryptionSessionError(EncryptionSessionError { - session: self.id.clone().into(), - session_nonce: self.nonce, - error: error.clone().into(), - }))); - } - - let mut data = self.data.lock(); - - warn!("{}: encryption session failed with error: {} from {}", self.node(), error, node); - - data.state = SessionState::Failed; - data.result = Some(Err(error.clone())); - self.completed.send(Err(error)); - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - if Some(self.nonce) != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &Message::Encryption(ref message) => match message { - &EncryptionMessage::InitializeEncryptionSession(ref message) => - self.on_initialize_session(sender.clone(), message), - &EncryptionMessage::ConfirmEncryptionInitialization(ref message) => - self.on_confirm_initialization(sender.clone(), message), - &EncryptionMessage::EncryptionSessionError(ref message) => { - self.on_session_error(sender, message.error.clone()); - Ok(()) - }, - }, - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl Debug for SessionImpl { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - write!(f, "Encryption session {} on {}", self.id, self.self_node_id) - } -} - -/// Check that common_point and encrypted point are not yet set in key share. -pub fn check_encrypted_data(key_share: Option<&DocumentKeyShare>) -> Result<(), Error> { - if let Some(key_share) = key_share { - // check that common_point and encrypted_point are still not set yet - if key_share.common_point.is_some() || key_share.encrypted_point.is_some() { - return Err(Error::DocumentKeyAlreadyStored); - } - } - - Ok(()) -} - -/// Update key share with encrypted document key. -pub fn update_encrypted_data(key_storage: &Arc, key_id: ServerKeyId, mut key_share: DocumentKeyShare, author: Address, common_point: Public, encrypted_point: Public) -> Result<(), Error> { - // author must be the same - if key_share.author != author { - return Err(Error::AccessDenied); - } - - // save encryption data - key_share.common_point = Some(common_point); - key_share.encrypted_point = Some(encrypted_point); - key_storage.update(key_id, key_share) -} diff --git a/secret-store/src/key_server_cluster/client_sessions/generation_session.rs b/secret-store/src/key_server_cluster/client_sessions/generation_session.rs deleted file mode 100644 index 6d65db70a5d..00000000000 --- a/secret-store/src/key_server_cluster/client_sessions/generation_session.rs +++ /dev/null @@ -1,1286 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap, VecDeque}; -use std::fmt::{Debug, Formatter, Error as FmtError}; -use std::sync::Arc; -use futures::Oneshot; -use parking_lot::Mutex; -use ethereum_types::Address; -use crypto::publickey::{Public, Secret}; -use key_server_cluster::{Error, NodeId, SessionId, KeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; -use key_server_cluster::math; -use key_server_cluster::cluster::Cluster; -use key_server_cluster::cluster_sessions::{ClusterSession, CompletionSignal}; -use key_server_cluster::message::{Message, GenerationMessage, InitializeSession, ConfirmInitialization, CompleteInitialization, - KeysDissemination, PublicKeyShare, SessionError, SessionCompleted}; - -/// Distributed key generation session. -/// Based on "ECDKG: A Distributed Key Generation Protocol Based on Elliptic Curve Discrete Logarithm" paper: -/// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.124.4128&rep=rep1&type=pdf -/// Brief overview: -/// 1) initialization: master node (which has received request for generating joint public + secret) initializes the session on all other nodes -/// 2) key dissemination (KD): all nodes are generating secret + public values and send these to appropriate nodes -/// 3) key verification (KV): all nodes are checking values, received for other nodes -/// 4) key generation phase (KG): nodes are exchanging with information, enough to generate joint public key -pub struct SessionImpl { - /// Unique session id. - id: SessionId, - /// Public identifier of this node. - self_node_id: NodeId, - /// Key storage. - key_storage: Option>, - /// Cluster which allows this node to send messages to other nodes in the cluster. - cluster: Arc, - /// Session-level nonce. - nonce: u64, - /// Mutable session data. - data: Mutex, - /// Session completion signal. - completed: CompletionSignal, -} - -/// SessionImpl creation parameters -pub struct SessionParams { - /// SessionImpl identifier. - pub id: SessionId, - /// Id of node, on which this session is running. - pub self_node_id: Public, - /// Key storage. - pub key_storage: Option>, - /// Cluster - pub cluster: Arc, - /// Session nonce. - pub nonce: Option, -} - -/// Mutable data of distributed key generation session. -#[derive(Debug)] -struct SessionData { - /// Current state of the session. - state: SessionState, - /// Simulate faulty behaviour? - simulate_faulty_behaviour: bool, - - // === Values, filled when session initialization just starts === - /// Reference to the node, which has started this session. - master: Option, - /// Address of the creator of the session. - author: Option
, - - // === Values, filled when session initialization is completed === - /// Session origin (if any). - origin: Option
, - /// Is zero secret generation session? - is_zero: Option, - /// Threshold value for this DKG. Only `threshold + 1` will be able to collectively recreate joint secret, - /// and thus - decrypt message, encrypted with joint public. - threshold: Option, - /// Random point, jointly generated by every node in the cluster. - derived_point: Option, - /// Nodes-specific data. - nodes: BTreeMap, - - // === Values, filled during KD phase === - /// Polynom1. - polynom1: Option>, - /// Value of polynom1[0], generated by this node. - secret_coeff: Option, - - // === Values, filled during KG phase === - /// Secret share, which this node holds. Persistent + private. - secret_share: Option, - - /// === Values, filled when DKG session is completed successfully === - /// Key share. - key_share: Option>, - /// Jointly generated public key, which can be used to encrypt secret. Public. - joint_public_and_secret: Option>, -} - -/// Mutable node-specific data. -#[derive(Debug, Clone)] -struct NodeData { - /// Random unique scalar. Persistent. - pub id_number: Secret, - - // === Values, filled during KD phase === - /// Secret value1, which has been received from this node. - pub secret1: Option, - /// Secret value2, which has been received from this node. - pub secret2: Option, - /// Public values, which have been received from this node. - pub publics: Option>, - - // === Values, filled during KG phase === - /// Public share, which has been received from this node. - pub public_share: Option, - - // === Values, filled during completion phase === - /// Flags marking that node has confirmed session completion (generated key is stored). - pub completion_confirmed: bool, -} - -/// Schedule for visiting other nodes of cluster. -#[derive(Debug, Clone, PartialEq)] -pub struct EveryOtherNodeVisitor { - /// Already visited nodes. - visited: BTreeSet, - /// Not yet visited nodes. - unvisited: VecDeque, - /// Nodes, which are currently visited. - in_progress: BTreeSet, -} - -/// Distributed key generation session state. -#[derive(Debug, Clone, PartialEq)] -pub enum SessionState { - // === Initialization states === - /// Every node starts in this state. - WaitingForInitialization, - /// Master node asks every other node to confirm initialization. - /// Derived point is generated by all nodes in the cluster. - WaitingForInitializationConfirm(EveryOtherNodeVisitor), - /// Slave nodes are in this state until initialization completion is reported by master node. - WaitingForInitializationComplete, - - // === KD phase states === - /// Node is waiting for generated keys from every other node. - WaitingForKeysDissemination, - - // === KG phase states === - /// Node is waiting for joint public key share to be received from every other node. - WaitingForPublicKeyShare, - - // === Generation phase states === - /// Node is waiting for session completion/session completion confirmation. - WaitingForGenerationConfirmation, - - // === Final states of the session === - /// Joint public key generation is completed. - Finished, - /// Joint public key generation is failed. - Failed, -} - -pub enum InitializationNodes { - RandomNumbers(BTreeSet), - SpecificNumbers(BTreeMap) -} - -impl InitializationNodes { - pub fn set(&self) -> BTreeSet { - match *self { - InitializationNodes::RandomNumbers(ref nodes) => nodes.clone(), - InitializationNodes::SpecificNumbers(ref nodes) => nodes.keys().cloned().collect(), - } - } -} - -impl From> for InitializationNodes { - fn from(nodes: BTreeSet) -> Self { - InitializationNodes::RandomNumbers(nodes) - } -} - -impl From> for InitializationNodes { - fn from(nodes: BTreeMap) -> Self { - InitializationNodes::SpecificNumbers(nodes) - } -} - -impl SessionImpl { - /// Create new generation session. - pub fn new(params: SessionParams) -> (Self, Oneshot>) { - let (completed, oneshot) = CompletionSignal::new(); - (SessionImpl { - id: params.id, - self_node_id: params.self_node_id, - key_storage: params.key_storage, - cluster: params.cluster, - // when nonce.is_nonce(), generation session is wrapped - // => nonce is checked somewhere else && we can pass any value - nonce: params.nonce.unwrap_or_default(), - completed, - data: Mutex::new(SessionData { - state: SessionState::WaitingForInitialization, - simulate_faulty_behaviour: false, - master: None, - author: None, - origin: None, - is_zero: None, - threshold: None, - derived_point: None, - nodes: BTreeMap::new(), - polynom1: None, - secret_coeff: None, - secret_share: None, - key_share: None, - joint_public_and_secret: None, - }), - }, oneshot) - } - - /// Get this node Id. - pub fn node(&self) -> &NodeId { - &self.self_node_id - } - - /// Get derived point. - #[cfg(test)] - pub fn derived_point(&self) -> Option { - self.data.lock().derived_point.clone() - } - - /// Simulate faulty generation session behaviour. - pub fn simulate_faulty_behaviour(&self) { - self.data.lock().simulate_faulty_behaviour = true; - } - - /// Get session state. - pub fn state(&self) -> SessionState { - self.data.lock().state.clone() - } - - /// Get session origin. - pub fn origin(&self) -> Option
{ - self.data.lock().origin.clone() - } - - /// Get session completion result (if available). - pub fn result(&self) -> Option> { - self.data.lock().joint_public_and_secret.clone() - .map(|r| r.map(|r| r.0.clone())) - } - - /// Get generated public and secret (if any). - pub fn joint_public_and_secret(&self) -> Option> { - self.data.lock().joint_public_and_secret.clone() - } - - /// Start new session initialization. This must be called on master node. - pub fn initialize(&self, origin: Option
, author: Address, is_zero: bool, threshold: usize, nodes: InitializationNodes) -> Result<(), Error> { - check_cluster_nodes(self.node(), &nodes.set())?; - check_threshold(threshold, &nodes.set())?; - - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // update state - data.master = Some(self.node().clone()); - data.author = Some(author.clone()); - data.origin = origin.clone(); - data.is_zero = Some(is_zero); - data.threshold = Some(threshold); - match nodes { - InitializationNodes::RandomNumbers(nodes) => { - for node_id in nodes { - // generate node identification parameter - let node_id_number = math::generate_random_scalar()?; - data.nodes.insert(node_id, NodeData::with_id_number(node_id_number)); - } - }, - InitializationNodes::SpecificNumbers(nodes) => { - for (node_id, node_id_number) in nodes { - data.nodes.insert(node_id, NodeData::with_id_number(node_id_number)); - } - }, - } - - let mut visit_policy = EveryOtherNodeVisitor::new(self.node(), data.nodes.keys().cloned()); - let derived_point = math::generate_random_point()?; - match visit_policy.next_node() { - Some(next_node) => { - data.state = SessionState::WaitingForInitializationConfirm(visit_policy); - - // start initialization - self.cluster.send(&next_node, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { - session: self.id.clone().into(), - session_nonce: self.nonce, - origin: origin.map(Into::into), - author: author.into(), - nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(), - is_zero: data.is_zero.expect("is_zero is filled in initialization phase; KD phase follows initialization phase; qed"), - threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"), - derived_point: derived_point.into(), - }))) - }, - None => { - drop(data); - self.complete_initialization(derived_point)?; - self.disseminate_keys()?; - self.verify_keys()?; - self.complete_generation()?; - - let mut data = self.data.lock(); - let result = data.joint_public_and_secret.clone() - .expect("session is instantly completed on a single node; qed") - .map(|(p, _, _)| p); - data.state = SessionState::Finished; - self.completed.send(result); - - Ok(()) - } - } - } - - /// Process single message. - pub fn process_message(&self, sender: &NodeId, message: &GenerationMessage) -> Result<(), Error> { - if self.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &GenerationMessage::InitializeSession(ref message) => - self.on_initialize_session(sender.clone(), message), - &GenerationMessage::ConfirmInitialization(ref message) => - self.on_confirm_initialization(sender.clone(), message), - &GenerationMessage::CompleteInitialization(ref message) => - self.on_complete_initialization(sender.clone(), message), - &GenerationMessage::KeysDissemination(ref message) => - self.on_keys_dissemination(sender.clone(), message), - &GenerationMessage::PublicKeyShare(ref message) => - self.on_public_key_share(sender.clone(), message), - &GenerationMessage::SessionError(ref message) => { - self.on_session_error(sender, message.error.clone()); - Ok(()) - }, - &GenerationMessage::SessionCompleted(ref message) => - self.on_session_completed(sender.clone(), message), - } - } - - /// When session initialization message is received. - pub fn on_initialize_session(&self, sender: NodeId, message: &InitializeSession) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - // check message - let nodes_ids = message.nodes.keys().cloned().map(Into::into).collect(); - check_threshold(message.threshold, &nodes_ids)?; - check_cluster_nodes(self.node(), &nodes_ids)?; - - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForInitialization { - return Err(Error::InvalidStateForRequest); - } - - // update derived point with random scalar - let mut derived_point = message.derived_point.clone().into(); - math::update_random_point(&mut derived_point)?; - - // send confirmation back to master node - self.cluster.send(&sender, Message::Generation(GenerationMessage::ConfirmInitialization(ConfirmInitialization { - session: self.id.clone().into(), - session_nonce: self.nonce, - derived_point: derived_point.into(), - })))?; - - // update state - data.master = Some(sender); - data.author = Some(message.author.clone().into()); - data.state = SessionState::WaitingForInitializationComplete; - data.nodes = message.nodes.iter().map(|(id, number)| (id.clone().into(), NodeData::with_id_number(number.clone().into()))).collect(); - data.origin = message.origin.clone().map(Into::into); - data.is_zero = Some(message.is_zero); - data.threshold = Some(message.threshold); - - Ok(()) - } - - /// When session initialization confirmation message is reeived. - pub fn on_confirm_initialization(&self, sender: NodeId, message: &ConfirmInitialization) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - debug_assert!(data.nodes.contains_key(&sender)); - - // check state && select new node to be initialized - let next_receiver = match data.state { - SessionState::WaitingForInitializationConfirm(ref mut visit_policy) => { - if !visit_policy.mark_visited(&sender) { - return Err(Error::InvalidStateForRequest); - } - - visit_policy.next_node() - }, - _ => return Err(Error::InvalidStateForRequest), - }; - - // proceed message - if let Some(next_receiver) = next_receiver { - return self.cluster.send(&next_receiver, Message::Generation(GenerationMessage::InitializeSession(InitializeSession { - session: self.id.clone().into(), - session_nonce: self.nonce, - origin: data.origin.clone().map(Into::into), - author: data.author.as_ref().expect("author is filled on initialization step; confrm initialization follows initialization; qed").clone().into(), - nodes: data.nodes.iter().map(|(k, v)| (k.clone().into(), v.id_number.clone().into())).collect(), - is_zero: data.is_zero.expect("is_zero is filled in initialization phase; KD phase follows initialization phase; qed"), - threshold: data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"), - derived_point: message.derived_point.clone().into(), - }))); - } - - // now it is time for keys dissemination (KD) phase - drop(data); - self.complete_initialization(message.derived_point.clone().into())?; - self.disseminate_keys() - } - - /// When session initialization completion message is received. - pub fn on_complete_initialization(&self, sender: NodeId, message: &CompleteInitialization) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForInitializationComplete { - return Err(Error::InvalidStateForRequest); - } - if data.master != Some(sender) { - return Err(Error::InvalidMessage); - } - - // remember passed data - data.derived_point = Some(message.derived_point.clone().into()); - - // now it is time for keys dissemination (KD) phase - drop(data); - self.disseminate_keys() - } - - /// When keys dissemination message is received. - pub fn on_keys_dissemination(&self, sender: NodeId, message: &KeysDissemination) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - - // simulate failure, if required - if data.simulate_faulty_behaviour { - return Err(Error::Internal("simulated error".into())); - } - - // check state - if data.state != SessionState::WaitingForKeysDissemination { - match data.state { - SessionState::WaitingForInitializationComplete | SessionState::WaitingForInitializationConfirm(_) => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } - } - debug_assert!(data.nodes.contains_key(&sender)); - - // check message - let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KD phase follows initialization phase; qed"); - let threshold = data.threshold.expect("threshold is filled in initialization phase; KD phase follows initialization phase; qed"); - if !is_zero && message.publics.len() != threshold + 1 { - return Err(Error::InvalidMessage); - } - - // update node data - { - let node_data = data.nodes.get_mut(&sender).ok_or(Error::InvalidMessage)?; - if node_data.secret1.is_some() || node_data.secret2.is_some() || node_data.publics.is_some() { - return Err(Error::InvalidStateForRequest); - } - - node_data.secret1 = Some(message.secret1.clone().into()); - node_data.secret2 = Some(message.secret2.clone().into()); - node_data.publics = Some(message.publics.iter().cloned().map(Into::into).collect()); - } - - // check if we have received keys from every other node - if data.nodes.iter().any(|(node_id, node_data)| node_id != self.node() && (node_data.publics.is_none() || node_data.secret1.is_none() || node_data.secret2.is_none())) { - return Ok(()) - } - - drop(data); - self.verify_keys() - } - - /// When public key share is received. - pub fn on_public_key_share(&self, sender: NodeId, message: &PublicKeyShare) -> Result<(), Error> { - let mut data = self.data.lock(); - - // check state - if data.state != SessionState::WaitingForPublicKeyShare { - match data.state { - SessionState::WaitingForInitializationComplete | - SessionState::WaitingForKeysDissemination => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } - } - - // update node data with received public share - { - let node_data = &mut data.nodes.get_mut(&sender).ok_or(Error::InvalidMessage)?; - if node_data.public_share.is_some() { - return Err(Error::InvalidMessage); - } - - node_data.public_share = Some(message.public_share.clone().into()); - } - - // if there's also nodes, which has not sent us their public shares - do nothing - if data.nodes.iter().any(|(node_id, node_data)| node_id != self.node() && node_data.public_share.is_none()) { - return Ok(()); - } - - drop(data); - self.complete_generation() - } - - /// When session completion message is received. - pub fn on_session_completed(&self, sender: NodeId, message: &SessionCompleted) -> Result<(), Error> { - debug_assert!(self.id == *message.session); - debug_assert!(&sender != self.node()); - - let mut data = self.data.lock(); - debug_assert!(data.nodes.contains_key(&sender)); - - // check state - if data.state != SessionState::WaitingForGenerationConfirmation { - match data.state { - SessionState::WaitingForPublicKeyShare => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } - } - - // if we are not masters, save result and respond with confirmation - if data.master.as_ref() != Some(self.node()) { - // check that we have received message from master - if data.master.as_ref() != Some(&sender) { - return Err(Error::InvalidMessage); - } - - // calculate joint public key - let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KG phase follows initialization phase; qed"); - let joint_public = if !is_zero { - let public_shares = data.nodes.values().map(|n| n.public_share.as_ref().expect("keys received on KD phase; KG phase follows KD phase; qed")); - math::compute_joint_public(public_shares)? - } else { - Default::default() - }; - - // save encrypted data to key storage - let encrypted_data = DocumentKeyShare { - author: data.author.as_ref().expect("author is filled in initialization phase; KG phase follows initialization phase; qed").clone(), - threshold: data.threshold.expect("threshold is filled in initialization phase; KG phase follows initialization phase; qed"), - public: joint_public, - common_point: None, - encrypted_point: None, - versions: vec![DocumentKeyShareVersion::new( - data.nodes.iter().map(|(node_id, node_data)| (node_id.clone(), node_data.id_number.clone())).collect(), - data.secret_share.as_ref().expect("secret_share is filled in KG phase; we are at the end of KG phase; qed").clone(), - )], - }; - - if let Some(ref key_storage) = self.key_storage { - key_storage.insert(self.id.clone(), encrypted_data.clone())?; - } - - // then respond with confirmation - data.state = SessionState::Finished; - return self.cluster.send(&sender, Message::Generation(GenerationMessage::SessionCompleted(SessionCompleted { - session: self.id.clone().into(), - session_nonce: self.nonce, - }))); - } - - // remember that we have received confirmation from sender node - { - let sender_node = data.nodes.get_mut(&sender).expect("node is always qualified by himself; qed"); - if sender_node.completion_confirmed { - return Err(Error::InvalidMessage); - } - - sender_node.completion_confirmed = true; - } - - // check if we have received confirmations from all cluster nodes - if data.nodes.iter().any(|(_, node_data)| !node_data.completion_confirmed) { - return Ok(()) - } - - // we have received enough confirmations => complete session - let result = data.joint_public_and_secret.clone() - .expect("we're on master node; we have received last completion confirmation; qed") - .map(|(p, _, _)| p); - data.state = SessionState::Finished; - self.completed.send(result); - - Ok(()) - } - - /// Complete initialization (when all other nodex has responded with confirmation) - fn complete_initialization(&self, mut derived_point: Public) -> Result<(), Error> { - // update point once again to make sure that derived point is not generated by last node - math::update_random_point(&mut derived_point)?; - - // remember derived point - let mut data = self.data.lock(); - data.derived_point = Some(derived_point.clone().into()); - - // broadcast derived point && other session paraeters to every other node - self.cluster.broadcast(Message::Generation(GenerationMessage::CompleteInitialization(CompleteInitialization { - session: self.id.clone().into(), - session_nonce: self.nonce, - derived_point: derived_point.into(), - }))) - } - - /// Keys dissemination (KD) phase - fn disseminate_keys(&self) -> Result<(), Error> { - let mut data = self.data.lock(); - - // pick 2t + 2 random numbers as polynomial coefficients for 2 polynoms - let threshold = data.threshold.expect("threshold is filled on initialization phase; KD phase follows initialization phase; qed"); - let is_zero = data.is_zero.expect("is_zero is filled on initialization phase; KD phase follows initialization phase; qed"); - let mut polynom1 = math::generate_random_polynom(threshold)?; - if is_zero { - polynom1[0] = math::zero_scalar(); - } - let polynom2 = math::generate_random_polynom(threshold)?; - data.polynom1 = Some(polynom1.clone()); - data.secret_coeff = Some(polynom1[0].clone()); - - // compute t+1 public values - let publics = match is_zero { - false => math::public_values_generation(threshold, - data.derived_point.as_ref().expect("keys dissemination occurs after derived point is agreed; qed"), - &polynom1, - &polynom2)?, - true => Default::default(), - }; - - // compute secret values for every other node - for (node, node_data) in data.nodes.iter_mut() { - let secret1 = math::compute_polynom(&polynom1, &node_data.id_number)?; - let secret2 = math::compute_polynom(&polynom2, &node_data.id_number)?; - - // send a message containing secret1 && secret2 to other node - if node != self.node() { - self.cluster.send(&node, Message::Generation(GenerationMessage::KeysDissemination(KeysDissemination { - session: self.id.clone().into(), - session_nonce: self.nonce, - secret1: secret1.into(), - secret2: secret2.into(), - publics: publics.iter().cloned().map(Into::into).collect(), - })))?; - } else { - node_data.secret1 = Some(secret1); - node_data.secret2 = Some(secret2); - node_data.publics = Some(publics.clone()); - } - } - - // update state - data.state = SessionState::WaitingForKeysDissemination; - - Ok(()) - } - - /// Keys verification (KV) phase - fn verify_keys(&self) -> Result<(), Error> { - let mut data = self.data.lock(); - - // key verification (KV) phase: check that other nodes have passed correct secrets - let threshold = data.threshold.expect("threshold is filled in initialization phase; KV phase follows initialization phase; qed"); - let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KV phase follows initialization phase; qed"); - let self_public_share = { - if !is_zero { - let derived_point = data.derived_point.clone().expect("derived point generated on initialization phase; KV phase follows initialization phase; qed"); - let number_id = data.nodes[self.node()].id_number.clone(); - for (_ , node_data) in data.nodes.iter_mut().filter(|&(node_id, _)| node_id != self.node()) { - let secret1 = node_data.secret1.as_ref().expect("keys received on KD phase; KV phase follows KD phase; qed"); - let secret2 = node_data.secret2.as_ref().expect("keys received on KD phase; KV phase follows KD phase; qed"); - let publics = node_data.publics.as_ref().expect("keys received on KD phase; KV phase follows KD phase; qed"); - let is_key_verification_ok = math::keys_verification(threshold, &derived_point, &number_id, - secret1, secret2, publics)?; - - if !is_key_verification_ok { - // node has sent us incorrect values. In original ECDKG protocol we should have sent complaint here. - return Err(Error::InvalidMessage); - } - } - - // calculate public share - let self_public_share = { - let self_secret_coeff = data.secret_coeff.as_ref().expect("secret_coeff is generated on KD phase; KG phase follows KD phase; qed"); - math::compute_public_share(self_secret_coeff)? - }; - - self_public_share - } else { - // TODO [Trust]: add verification when available - Default::default() - } - }; - - // calculate self secret + public shares - let self_secret_share = { - let secret_values_iter = data.nodes.values() - .map(|n| n.secret1.as_ref().expect("keys received on KD phase; KG phase follows KD phase; qed")); - math::compute_secret_share(secret_values_iter)? - }; - - // update state - data.state = SessionState::WaitingForPublicKeyShare; - data.secret_share = Some(self_secret_share); - let self_node = data.nodes.get_mut(self.node()).expect("node is always qualified by himself; qed"); - self_node.public_share = Some(self_public_share.clone()); - - // broadcast self public key share - self.cluster.broadcast(Message::Generation(GenerationMessage::PublicKeyShare(PublicKeyShare { - session: self.id.clone().into(), - session_nonce: self.nonce, - public_share: self_public_share.into(), - }))) - } - - /// Complete generation - fn complete_generation(&self) -> Result<(), Error> { - let mut data = self.data.lock(); - - // calculate joint public key - let is_zero = data.is_zero.expect("is_zero is filled in initialization phase; KG phase follows initialization phase; qed"); - let joint_public = if !is_zero { - let public_shares = data.nodes.values().map(|n| n.public_share.as_ref().expect("keys received on KD phase; KG phase follows KD phase; qed")); - math::compute_joint_public(public_shares)? - } else { - Default::default() - }; - - // prepare key data - let secret_share = data.secret_share.as_ref().expect("secret_share is filled in KG phase; we are at the end of KG phase; qed").clone(); - let encrypted_data = DocumentKeyShare { - author: data.author.as_ref().expect("author is filled in initialization phase; KG phase follows initialization phase; qed").clone(), - threshold: data.threshold.expect("threshold is filled in initialization phase; KG phase follows initialization phase; qed"), - public: joint_public.clone(), - common_point: None, - encrypted_point: None, - versions: vec![DocumentKeyShareVersion::new( - data.nodes.iter().map(|(node_id, node_data)| (node_id.clone(), node_data.id_number.clone())).collect(), - secret_share.clone(), - )], - }; - - // if we are at the slave node - wait for session completion - let secret_coeff = data.secret_coeff.as_ref().expect("secret coeff is selected on initialization phase; current phase follows initialization; qed").clone(); - if data.master.as_ref() != Some(self.node()) { - data.key_share = Some(Ok(encrypted_data)); - data.joint_public_and_secret = Some(Ok((joint_public, secret_coeff, secret_share))); - data.state = SessionState::WaitingForGenerationConfirmation; - return Ok(()); - } - - // then save encrypted data to the key storage - if let Some(ref key_storage) = self.key_storage { - key_storage.insert(self.id.clone(), encrypted_data.clone())?; - } - - // then distribute encrypted data to every other node - self.cluster.broadcast(Message::Generation(GenerationMessage::SessionCompleted(SessionCompleted { - session: self.id.clone().into(), - session_nonce: self.nonce, - })))?; - - // then wait for confirmation from all other nodes - { - let self_node = data.nodes.get_mut(self.node()).expect("node is always qualified by himself; qed"); - self_node.completion_confirmed = true; - } - data.key_share = Some(Ok(encrypted_data)); - data.joint_public_and_secret = Some(Ok((joint_public, secret_coeff, secret_share))); - data.state = SessionState::WaitingForGenerationConfirmation; - - Ok(()) - } -} - -impl ClusterSession for SessionImpl { - type Id = SessionId; - type CreationData = (); - type SuccessfulResult = Public; - - fn type_name() -> &'static str { - "generation" - } - - fn id(&self) -> SessionId { - self.id.clone() - } - - fn is_finished(&self) -> bool { - let data = self.data.lock(); - data.state == SessionState::Failed - || data.state == SessionState::Finished - } - - fn on_node_timeout(&self, node: &NodeId) { - let mut data = self.data.lock(); - - // all nodes are required for generation session - // => fail without check - warn!("{}: generation session failed because {} connection has timeouted", self.node(), node); - - data.state = SessionState::Failed; - data.key_share = Some(Err(Error::NodeDisconnected)); - data.joint_public_and_secret = Some(Err(Error::NodeDisconnected)); - self.completed.send(Err(Error::NodeDisconnected)); - } - - fn on_session_timeout(&self) { - let mut data = self.data.lock(); - - warn!("{}: generation session failed with timeout", self.node()); - - data.state = SessionState::Failed; - data.key_share = Some(Err(Error::NodeDisconnected)); - data.joint_public_and_secret = Some(Err(Error::NodeDisconnected)); - self.completed.send(Err(Error::NodeDisconnected)); - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - // error in generation session is considered fatal - // => broadcast error if error occured on this node - if *node == self.self_node_id { - // do not bother processing send error, as we already processing error - let _ = self.cluster.broadcast(Message::Generation(GenerationMessage::SessionError(SessionError { - session: self.id.clone().into(), - session_nonce: self.nonce, - error: error.clone().into(), - }))); - } - - let mut data = self.data.lock(); - data.state = SessionState::Failed; - data.key_share = Some(Err(error.clone())); - data.joint_public_and_secret = Some(Err(error.clone())); - self.completed.send(Err(error)); - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::Generation(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl EveryOtherNodeVisitor { - pub fn new(self_id: &NodeId, nodes: I) -> Self where I: Iterator { - EveryOtherNodeVisitor { - visited: BTreeSet::new(), - unvisited: nodes.filter(|n| n != self_id).collect(), - in_progress: BTreeSet::new(), - } - } - - pub fn next_node(&mut self) -> Option { - let next_node = self.unvisited.pop_front(); - if let Some(ref next_node) = next_node { - self.in_progress.insert(next_node.clone()); - } - next_node - } - - pub fn mark_visited(&mut self, node: &NodeId) -> bool { - if !self.in_progress.remove(node) { - return false; - } - self.visited.insert(node.clone()) - } -} - -impl NodeData { - fn with_id_number(node_id_number: Secret) -> Self { - NodeData { - id_number: node_id_number, - secret1: None, - secret2: None, - publics: None, - public_share: None, - completion_confirmed: false, - } - } -} - -impl Debug for SessionImpl { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - write!(f, "Generation session {} on {}", self.id, self.self_node_id) - } -} - -fn check_cluster_nodes(self_node_id: &NodeId, nodes: &BTreeSet) -> Result<(), Error> { - assert!(nodes.contains(self_node_id)); - Ok(()) -} - -fn check_threshold(threshold: usize, nodes: &BTreeSet) -> Result<(), Error> { - // at least threshold + 1 nodes are required to collectively decrypt message - if threshold >= nodes.len() { - return Err(Error::NotEnoughNodesForThreshold); - } - - Ok(()) -} - -#[cfg(test)] -pub mod tests { - use std::sync::Arc; - use ethereum_types::H256; - use crypto::publickey::{Random, Generator, KeyPair, Secret}; - use key_server_cluster::{NodeId, Error, KeyStorage}; - use key_server_cluster::message::{self, Message, GenerationMessage, KeysDissemination, - PublicKeyShare, ConfirmInitialization}; - use key_server_cluster::cluster::tests::{MessageLoop as ClusterMessageLoop, make_clusters_and_preserve_sessions}; - use key_server_cluster::cluster_sessions::ClusterSession; - use key_server_cluster::generation_session::{SessionImpl, SessionState}; - use key_server_cluster::math; - use key_server_cluster::math::tests::do_encryption_and_decryption; - - #[derive(Debug)] - pub struct MessageLoop(pub ClusterMessageLoop); - - impl MessageLoop { - pub fn new(num_nodes: usize) -> Self { - MessageLoop(make_clusters_and_preserve_sessions(num_nodes)) - } - - pub fn init(self, threshold: usize) -> Result { - self.0.cluster(0).client().new_generation_session(Default::default(), None, Default::default(), threshold) - .map(|_| self) - } - - pub fn session_at(&self, idx: usize) -> Arc { - self.0.sessions(idx).generation_sessions.first().unwrap() - } - - pub fn session_of(&self, node: &NodeId) -> Arc { - self.0.sessions_of(node).generation_sessions.first().unwrap() - } - - pub fn take_message_confirm_initialization(&self) -> (NodeId, NodeId, ConfirmInitialization) { - match self.0.take_message() { - Some((from, to, Message::Generation(GenerationMessage::ConfirmInitialization(msg)))) => - (from, to, msg), - _ => panic!("unexpected"), - } - } - - pub fn take_message_keys_dissemination(&self) -> (NodeId, NodeId, KeysDissemination) { - match self.0.take_message() { - Some((from, to, Message::Generation(GenerationMessage::KeysDissemination(msg)))) => - (from, to, msg), - _ => panic!("unexpected"), - } - } - - pub fn take_message_public_key_share(&self) -> (NodeId, NodeId, PublicKeyShare) { - match self.0.take_message() { - Some((from, to, Message::Generation(GenerationMessage::PublicKeyShare(msg)))) => - (from, to, msg), - _ => panic!("unexpected"), - } - } - - pub fn nodes_id_numbers(&self) -> Vec { - let session = self.session_at(0); - let session_data = session.data.lock(); - session_data.nodes.values().map(|n| n.id_number.clone()).collect() - } - - pub fn nodes_secret_shares(&self) -> Vec { - (0..self.0.nodes().len()).map(|i| { - let session = self.session_at(i); - let session_data = session.data.lock(); - session_data.secret_share.as_ref().unwrap().clone() - }).collect() - } - - pub fn compute_key_pair(&self) -> KeyPair { - let t = self.0.key_storage(0).get(&Default::default()).unwrap().unwrap().threshold; - let secret_shares = self.nodes_secret_shares(); - let id_numbers = self.nodes_id_numbers(); - let secret_shares = secret_shares.iter().take(t + 1).collect::>(); - let id_numbers = id_numbers.iter().take(t + 1).collect::>(); - let joint_secret = math::compute_joint_secret_from_shares(t, &secret_shares, &id_numbers).unwrap(); - - KeyPair::from_secret(joint_secret).unwrap() - } - - pub fn key_version(&self) -> H256 { - self.0.key_storage(0).get(&Default::default()) - .unwrap().unwrap().versions.iter().last().unwrap().hash - } - } - - #[test] - fn initializes_in_cluster_of_single_node() { - MessageLoop::new(1).init(0).unwrap(); - } - - #[test] - fn fails_to_initialize_if_threshold_is_wrong() { - assert_eq!(MessageLoop::new(2).init(2).unwrap_err(), Error::NotEnoughNodesForThreshold); - } - - #[test] - fn fails_to_initialize_when_already_initialized() { - let ml = MessageLoop::new(2).init(0).unwrap(); - assert_eq!( - ml.session_at(0).initialize(Default::default(), Default::default(), false, 0, ml.0.nodes().into()), - Err(Error::InvalidStateForRequest), - ); - } - - #[test] - fn fails_to_accept_initialization_when_already_initialized() { - let ml = MessageLoop::new(2).init(0).unwrap(); - let (from, to, msg) = ml.0.take_message().unwrap(); - ml.0.process_message(from, to, msg.clone()); - assert_eq!( - ml.session_of(&to).on_message(&from, &msg), - Err(Error::InvalidStateForRequest), - ); - } - - #[test] - fn slave_updates_derived_point_on_initialization() { - let ml = MessageLoop::new(2).init(0).unwrap(); - let original_point = match ml.0.take_message().unwrap() { - (from, to, Message::Generation(GenerationMessage::InitializeSession(msg))) => { - let original_point = msg.derived_point.clone(); - let msg = Message::Generation(GenerationMessage::InitializeSession(msg)); - ml.0.process_message(from, to, msg); - original_point - }, - _ => panic!("unexpected"), - }; - - match ml.0.take_message().unwrap() { - (_, _, Message::Generation(GenerationMessage::ConfirmInitialization(msg))) => - assert!(original_point != msg.derived_point), - _ => panic!("unexpected"), - } - } - - #[test] - fn fails_to_accept_initialization_confirmation_if_already_accepted_from_the_same_node() { - let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); - - let (from, to, msg) = ml.take_message_confirm_initialization(); - ml.0.process_message(from, to, Message::Generation(GenerationMessage::ConfirmInitialization(msg.clone()))); - assert_eq!(ml.session_of(&to).on_confirm_initialization(from, &msg), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn fails_to_accept_initialization_confirmation_if_initialization_already_completed() { - let ml = MessageLoop::new(2).init(0).unwrap(); - ml.0.take_and_process_message(); - ml.0.take_and_process_message(); - assert_eq!(ml.session_at(0).on_confirm_initialization(ml.0.node(1), &message::ConfirmInitialization { - session: Default::default(), - session_nonce: 0, - derived_point: math::generate_random_point().unwrap().into(), - }), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn master_updates_derived_point_on_initialization_completion() { - let ml = MessageLoop::new(2).init(0).unwrap(); - ml.0.take_and_process_message(); - let original_point = match ml.0.take_message().unwrap() { - (from, to, Message::Generation(GenerationMessage::ConfirmInitialization(msg))) => { - let original_point = msg.derived_point.clone(); - let msg = Message::Generation(GenerationMessage::ConfirmInitialization(msg)); - ml.session_of(&to).on_message(&from, &msg).unwrap(); - original_point - }, - _ => panic!("unexpected"), - }; - - assert!(ml.session_at(0).derived_point().unwrap() != original_point.into()); - } - - #[test] - fn fails_to_complete_initialization_if_not_waiting_for_it() { - let ml = MessageLoop::new(2).init(0).unwrap(); - ml.0.take_and_process_message(); - assert_eq!(ml.session_at(0).on_complete_initialization(ml.0.node(1), &message::CompleteInitialization { - session: Default::default(), - session_nonce: 0, - derived_point: math::generate_random_point().unwrap().into(), - }), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn fails_to_complete_initialization_from_non_master_node() { - let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); - ml.0.take_and_process_message(); - ml.0.take_and_process_message(); - ml.0.take_and_process_message(); - assert_eq!(ml.session_at(1).on_complete_initialization(ml.0.node(2), &message::CompleteInitialization { - session: Default::default(), - session_nonce: 0, - derived_point: math::generate_random_point().unwrap().into(), - }), Err(Error::InvalidMessage)); - } - - #[test] - fn fails_to_accept_keys_dissemination_if_not_waiting_for_it() { - let ml = MessageLoop::new(2).init(0).unwrap(); - assert_eq!(ml.session_at(0).on_keys_dissemination(ml.0.node(1), &message::KeysDissemination { - session: Default::default(), - session_nonce: 0, - secret1: math::generate_random_scalar().unwrap().into(), - secret2: math::generate_random_scalar().unwrap().into(), - publics: vec![math::generate_random_point().unwrap().into()], - }), Err(Error::TooEarlyForRequest)); - } - - #[test] - fn fails_to_accept_keys_dissemination_if_wrong_number_of_publics_passed() { - let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); // m -> s1: InitializeSession - ml.0.take_and_process_message(); // m -> s2: InitializeSession - ml.0.take_and_process_message(); // s1 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // s2 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // m -> s1: CompleteInitialization - ml.0.take_and_process_message(); // m -> s2: CompleteInitialization - - let (from, to, mut msg) = ml.take_message_keys_dissemination(); - msg.publics.clear(); - assert_eq!(ml.session_of(&to).on_keys_dissemination(from, &msg), Err(Error::InvalidMessage)); - } - - #[test] - fn fails_to_accept_keys_dissemination_second_time_from_the_same_node() { - let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); // m -> s1: InitializeSession - ml.0.take_and_process_message(); // m -> s2: InitializeSession - ml.0.take_and_process_message(); // s1 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // s2 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // m -> s1: CompleteInitialization - ml.0.take_and_process_message(); // m -> s2: CompleteInitialization - - let (from, to, msg) = ml.take_message_keys_dissemination(); - ml.0.process_message(from, to, Message::Generation(GenerationMessage::KeysDissemination(msg.clone()))); - assert_eq!(ml.session_of(&to).on_keys_dissemination(from, &msg), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn should_not_accept_public_key_share_when_is_not_waiting_for_it() { - let ml = MessageLoop::new(3).init(1).unwrap(); - assert_eq!(ml.session_at(0).on_public_key_share(ml.0.node(1), &message::PublicKeyShare { - session: Default::default(), - session_nonce: 0, - public_share: math::generate_random_point().unwrap().into(), - }), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn should_not_accept_public_key_share_when_receiving_twice() { - let ml = MessageLoop::new(3).init(0).unwrap(); - ml.0.take_and_process_message(); // m -> s1: InitializeSession - ml.0.take_and_process_message(); // m -> s2: InitializeSession - ml.0.take_and_process_message(); // s1 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // s2 -> m: ConfirmInitialization - ml.0.take_and_process_message(); // m -> s1: CompleteInitialization - ml.0.take_and_process_message(); // m -> s2: CompleteInitialization - ml.0.take_and_process_message(); // m -> s1: KeysDissemination - ml.0.take_and_process_message(); // m -> s2: KeysDissemination - ml.0.take_and_process_message(); // s1 -> m: KeysDissemination - ml.0.take_and_process_message(); // s1 -> s2: KeysDissemination - ml.0.take_and_process_message(); // s2 -> m: KeysDissemination - ml.0.take_and_process_message(); // s2 -> s1: KeysDissemination - - let (from, to, msg) = ml.take_message_public_key_share(); - ml.0.process_message(from, to, Message::Generation(GenerationMessage::PublicKeyShare(msg.clone()))); - assert_eq!(ml.session_of(&to).on_public_key_share(from, &msg), Err(Error::InvalidMessage)); - } - - #[test] - fn encryption_fails_on_session_timeout() { - let ml = MessageLoop::new(2).init(0).unwrap(); - assert!(ml.session_at(0).joint_public_and_secret().is_none()); - ml.session_at(0).on_session_timeout(); - assert_eq!(ml.session_at(0).joint_public_and_secret().unwrap(), Err(Error::NodeDisconnected)); - } - - #[test] - fn encryption_fails_on_node_timeout() { - let ml = MessageLoop::new(2).init(0).unwrap(); - assert!(ml.session_at(0).joint_public_and_secret().is_none()); - ml.session_at(0).on_node_timeout(&ml.0.node(1)); - assert_eq!(ml.session_at(0).joint_public_and_secret().unwrap(), Err(Error::NodeDisconnected)); - } - - #[test] - fn complete_enc_dec_session() { - let test_cases = [(0, 5), (2, 5), (3, 5)]; - for &(threshold, num_nodes) in &test_cases { - let ml = MessageLoop::new(num_nodes).init(threshold).unwrap(); - ml.0.loop_until(|| ml.0.is_empty()); - - // check that all nodes has finished joint public generation - let joint_public_key = ml.session_at(0).joint_public_and_secret().unwrap().unwrap().0; - for i in 0..num_nodes { - let session = ml.session_at(i); - assert_eq!(session.state(), SessionState::Finished); - assert_eq!(session.joint_public_and_secret().map(|p| p.map(|p| p.0)), Some(Ok(joint_public_key))); - } - - // now let's encrypt some secret (which is a point on EC) - let document_secret_plain = Random.generate().unwrap().public().clone(); - let all_nodes_id_numbers = ml.nodes_id_numbers(); - let all_nodes_secret_shares = ml.nodes_secret_shares(); - let document_secret_decrypted = do_encryption_and_decryption(threshold, &joint_public_key, - &all_nodes_id_numbers, - &all_nodes_secret_shares, - None, - document_secret_plain.clone() - ).0; - assert_eq!(document_secret_plain, document_secret_decrypted); - } - } - - #[test] - fn generation_message_fails_when_nonce_is_wrong() { - let ml = MessageLoop::new(2).init(0).unwrap(); - ml.0.take_and_process_message(); - - let msg = message::GenerationMessage::KeysDissemination(message::KeysDissemination { - session: Default::default(), - session_nonce: 10, - secret1: math::generate_random_scalar().unwrap().into(), - secret2: math::generate_random_scalar().unwrap().into(), - publics: vec![math::generate_random_point().unwrap().into()], - }); - assert_eq!(ml.session_at(1).process_message(&ml.0.node(0), &msg).unwrap_err(), Error::ReplayProtection); - } -} diff --git a/secret-store/src/key_server_cluster/client_sessions/mod.rs b/secret-store/src/key_server_cluster/client_sessions/mod.rs deleted file mode 100644 index 536a09a7ce0..00000000000 --- a/secret-store/src/key_server_cluster/client_sessions/mod.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -pub mod decryption_session; -pub mod encryption_session; -pub mod generation_session; -pub mod signing_session_ecdsa; -pub mod signing_session_schnorr; diff --git a/secret-store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs b/secret-store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs deleted file mode 100644 index 3532af7f0a5..00000000000 --- a/secret-store/src/key_server_cluster/client_sessions/signing_session_ecdsa.rs +++ /dev/null @@ -1,1180 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use std::collections::btree_map::Entry; -use std::sync::Arc; -use futures::Oneshot; -use parking_lot::Mutex; -use crypto::publickey::{Public, Secret, Signature, sign}; -use ethereum_types::H256; -use key_server_cluster::{Error, NodeId, SessionId, SessionMeta, AclStorage, DocumentKeyShare, Requester}; -use key_server_cluster::cluster::{Cluster}; -use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession, CompletionSignal}; -use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionParams as GenerationSessionParams, - SessionState as GenerationSessionState}; -use key_server_cluster::math; -use key_server_cluster::message::{Message, EcdsaSigningMessage, EcdsaSigningConsensusMessage, EcdsaSignatureNonceGenerationMessage, - EcdsaInversionNonceGenerationMessage, EcdsaInversionZeroGenerationMessage, EcdsaSigningInversedNonceCoeffShare, - EcdsaRequestPartialSignature, EcdsaPartialSignature, EcdsaSigningSessionCompleted, GenerationMessage, - ConsensusMessage, EcdsaSigningSessionError, InitializeConsensusSession, ConfirmConsensusInitialization, - EcdsaSigningSessionDelegation, EcdsaSigningSessionDelegationCompleted}; -use key_server_cluster::jobs::job_session::JobTransport; -use key_server_cluster::jobs::key_access_job::KeyAccessJob; -use key_server_cluster::jobs::signing_job_ecdsa::{EcdsaPartialSigningRequest, EcdsaPartialSigningResponse, EcdsaSigningJob}; -use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; - -/// Distributed ECDSA-signing session. -/// Based on "A robust threshold elliptic curve digital signature providing a new verifiable secret sharing scheme" paper. -/// WARNING: can only be used if 2*t < N is true for key generation scheme -pub struct SessionImpl { - /// Session core. - core: SessionCore, - /// Session data. - data: Mutex, -} - -/// Immutable session data. -struct SessionCore { - /// Session metadata. - pub meta: SessionMeta, - /// Signing session access key. - pub access_key: Secret, - /// Key share. - pub key_share: Option, - /// Cluster which allows this node to send messages to other nodes in the cluster. - pub cluster: Arc, - /// Session-level nonce. - pub nonce: u64, - /// Session completion signal. - pub completed: CompletionSignal, -} - -/// Signing consensus session type. -type SigningConsensusSession = ConsensusSession; - -/// Mutable session data. -struct SessionData { - /// Session state. - pub state: SessionState, - /// Message hash. - pub message_hash: Option, - /// Key version to use for decryption. - pub version: Option, - /// Consensus-based signing session. - pub consensus_session: SigningConsensusSession, - /// Signature nonce generation session. - pub sig_nonce_generation_session: Option, - /// Inversion nonce generation session. - pub inv_nonce_generation_session: Option, - /// Inversion zero generation session. - pub inv_zero_generation_session: Option, - /// Inversed nonce coefficient shares. - pub inversed_nonce_coeff_shares: Option>, - /// Delegation status. - pub delegation_status: Option, - /// Decryption result. - pub result: Option>, -} - -/// Signing session state. -#[derive(Debug, PartialEq)] -pub enum SessionState { - /// Consensus is establishing. - ConsensusEstablishing, - /// Nonces (signature, inversion && zero) are generating. - NoncesGenerating, - /// Waiting for inversed nonce shares. - WaitingForInversedNonceShares, - /// State when signature is computing. - SignatureComputing, -} - -/// Session creation parameters -pub struct SessionParams { - /// Session metadata. - pub meta: SessionMeta, - /// Session access key. - pub access_key: Secret, - /// Key share. - pub key_share: Option, - /// ACL storage. - pub acl_storage: Arc, - /// Cluster - pub cluster: Arc, - /// Session nonce. - pub nonce: u64, -} - -/// Signing consensus transport. -struct SigningConsensusTransport { - /// Session id. - id: SessionId, - /// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Selected key version (on master node). - version: Option, - /// Cluster. - cluster: Arc, -} - -/// Signing key generation transport. -struct NonceGenerationTransport EcdsaSigningMessage + Send + Sync> { - /// Session id. - id: SessionId, - /// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Cluster. - cluster: Arc, - /// Other nodes ids. - other_nodes_ids: BTreeSet, - /// Message mapping function. - map: F, -} - -/// Signing job transport -struct SigningJobTransport { - /// Session id. - id: SessionId, - /// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Cluster. - cluster: Arc, -} - -/// Session delegation status. -enum DelegationStatus { - /// Delegated to other node. - DelegatedTo(NodeId), - /// Delegated from other node. - DelegatedFrom(NodeId, u64), -} - -impl SessionImpl { - /// Create new signing session. - pub fn new( - params: SessionParams, - requester: Option, - ) -> Result<(Self, Oneshot>), Error> { - debug_assert_eq!(params.meta.threshold, params.key_share.as_ref().map(|ks| ks.threshold).unwrap_or_default()); - - let consensus_transport = SigningConsensusTransport { - id: params.meta.id.clone(), - access_key: params.access_key.clone(), - nonce: params.nonce, - version: None, - cluster: params.cluster.clone(), - }; - let consensus_session = ConsensusSession::new(ConsensusSessionParams { - // this session requires responses from 2 * t nodes - meta: SessionMeta { - id: params.meta.id, - master_node_id: params.meta.master_node_id, - self_node_id: params.meta.self_node_id, - threshold: params.meta.threshold * 2, - configured_nodes_count: params.meta.configured_nodes_count, - connected_nodes_count: params.meta.connected_nodes_count, - }, - consensus_executor: match requester { - Some(requester) => KeyAccessJob::new_on_master(params.meta.id.clone(), params.acl_storage.clone(), requester), - None => KeyAccessJob::new_on_slave(params.meta.id.clone(), params.acl_storage.clone()), - }, - consensus_transport: consensus_transport, - })?; - - let (completed, oneshot) = CompletionSignal::new(); - Ok((SessionImpl { - core: SessionCore { - meta: params.meta, - access_key: params.access_key, - key_share: params.key_share, - cluster: params.cluster, - nonce: params.nonce, - completed, - }, - data: Mutex::new(SessionData { - state: SessionState::ConsensusEstablishing, - message_hash: None, - version: None, - consensus_session: consensus_session, - sig_nonce_generation_session: None, - inv_nonce_generation_session: None, - inv_zero_generation_session: None, - inversed_nonce_coeff_shares: None, - delegation_status: None, - result: None, - }), - }, oneshot)) - } - - /// Wait for session completion. - #[cfg(test)] - pub fn wait(&self) -> Result { - Self::wait_session(&self.core.completed, &self.data, None, |data| data.result.clone()) - .expect("wait_session returns Some if called without timeout; qed") - } - - /// Delegate session to other node. - pub fn delegate(&self, master: NodeId, version: H256, message_hash: H256) -> Result<(), Error> { - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Err(Error::InvalidStateForRequest); - } - - let mut data = self.data.lock(); - if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { - return Err(Error::InvalidStateForRequest); - } - - data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(false); - self.core.cluster.send(&master, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(EcdsaSigningSessionDelegation { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - requester: data.consensus_session.consensus_job().executor().requester() - .expect("requester is passed to master node on creation; session can be delegated from master node only; qed") - .clone().into(), - version: version.into(), - message_hash: message_hash.into(), - })))?; - data.delegation_status = Some(DelegationStatus::DelegatedTo(master)); - Ok(()) - } - - /// Initialize signing session on master node. - pub fn initialize(&self, version: H256, message_hash: H256) -> Result<(), Error> { - debug_assert_eq!(self.core.meta.self_node_id, self.core.meta.master_node_id); - - // check if version exists - let key_version = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share.version(&version)?, - }; - - // select nodes to participate in consensus etablish session - let mut data = self.data.lock(); - let non_isolated_nodes = self.core.cluster.nodes(); - let mut consensus_nodes: BTreeSet<_> = key_version.id_numbers.keys() - .filter(|n| non_isolated_nodes.contains(*n)) - .cloned() - .chain(::std::iter::once(self.core.meta.self_node_id.clone())) - .collect(); - if let Some(&DelegationStatus::DelegatedFrom(delegation_master, _)) = data.delegation_status.as_ref() { - consensus_nodes.remove(&delegation_master); - } - - // start consensus establish sesssion - data.consensus_session.consensus_job_mut().transport_mut().version = Some(version.clone()); - data.version = Some(version.clone()); - data.message_hash = Some(message_hash); - data.consensus_session.initialize(consensus_nodes)?; - - // consensus established => threshold is 0 => we can generate signature on this node - if data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished { - let result = sign(&key_version.secret_share, &message_hash).map_err(Into::into); - data.result = Some(result.clone()); - self.core.completed.send(result); - } - - Ok(()) - } - - /// Process signing message. - pub fn process_message(&self, sender: &NodeId, message: &EcdsaSigningMessage) -> Result<(), Error> { - if self.core.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref message) => - self.on_consensus_message(sender, message), - &EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref message) => - self.on_signature_nonce_generation_message(sender, message), - &EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref message) => - self.on_inversion_nonce_generation_message(sender, message), - &EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref message) => - self.on_inversion_zero_generation_message(sender, message), - &EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(ref message) => - self.on_inversed_nonce_coeff_share(sender, message), - &EcdsaSigningMessage::EcdsaRequestPartialSignature(ref message) => - self.on_partial_signature_requested(sender, message), - &EcdsaSigningMessage::EcdsaPartialSignature(ref message) => - self.on_partial_signature(sender, message), - &EcdsaSigningMessage::EcdsaSigningSessionError(ref message) => - self.process_node_error(Some(&sender), message.error.clone()), - &EcdsaSigningMessage::EcdsaSigningSessionCompleted(ref message) => - self.on_session_completed(sender, message), - &EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref message) => - self.on_session_delegated(sender, message), - &EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(ref message) => - self.on_session_delegation_completed(sender, message), - } - } - - /// When session is delegated to this node. - pub fn on_session_delegated(&self, sender: &NodeId, message: &EcdsaSigningSessionDelegation) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - { - let mut data = self.data.lock(); - if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { - return Err(Error::InvalidStateForRequest); - } - - data.consensus_session.consensus_job_mut().executor_mut().set_requester(message.requester.clone().into()); - data.delegation_status = Some(DelegationStatus::DelegatedFrom(sender.clone(), message.session_nonce)); - } - - self.initialize(message.version.clone().into(), message.message_hash.clone().into()) - } - - /// When delegated session is completed on other node. - pub fn on_session_delegation_completed(&self, sender: &NodeId, message: &EcdsaSigningSessionDelegationCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Err(Error::InvalidStateForRequest); - } - - let mut data = self.data.lock(); - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(ref node)) if node == sender => (), - _ => return Err(Error::InvalidMessage), - } - - Self::set_signing_result(&self.core, &mut *data, Ok(message.signature.clone().into())); - - Ok(()) - } - - /// When consensus-related message is received. - pub fn on_consensus_message(&self, sender: &NodeId, message: &EcdsaSigningConsensusMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - let is_establishing_consensus = data.consensus_session.state() == ConsensusSessionState::EstablishingConsensus; - - if let &ConsensusMessage::InitializeConsensusSession(ref msg) = &message.message { - let version = msg.version.clone().into(); - let has_key_share = self.core.key_share.as_ref() - .map(|ks| ks.version(&version).is_ok()) - .unwrap_or(false); - data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(has_key_share); - data.version = Some(version); - } - data.consensus_session.on_consensus_message(&sender, &message.message)?; - - let is_consensus_established = data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished; - if self.core.meta.self_node_id != self.core.meta.master_node_id || !is_establishing_consensus || !is_consensus_established { - return Ok(()); - } - - let key_share = self.core.key_share.as_ref() - .expect("this is master node; master node is selected so that it has key version; qed"); - let key_version = key_share.version(data.version.as_ref() - .expect("this is master node; master node is selected so that it has key version; qed"))?; - - let consensus_group = data.consensus_session.select_consensus_group()?.clone(); - let mut other_consensus_group_nodes = consensus_group.clone(); - other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - let consensus_group_map: BTreeMap<_, _> = consensus_group.iter().map(|n| (n.clone(), key_version.id_numbers[n].clone())).collect(); - - // start generation of signature nonce - let sig_nonce_generation_session = Self::start_generation_session(&self.core, &other_consensus_group_nodes, - |s, k, n, m| EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage( - EcdsaSignatureNonceGenerationMessage { - session: s.into(), - sub_session: k.into(), - session_nonce: n, - message: m, - })); - sig_nonce_generation_session.initialize(Default::default(), Default::default(), false, key_share.threshold, consensus_group_map.clone().into())?; - data.sig_nonce_generation_session = Some(sig_nonce_generation_session); - - // start generation of inversed nonce computation session - let inv_nonce_generation_session = Self::start_generation_session(&self.core, &other_consensus_group_nodes, - move |s, k, n, m| EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage( - EcdsaInversionNonceGenerationMessage { - session: s.into(), - sub_session: k.into(), - session_nonce: n, - message: m, - })); - inv_nonce_generation_session.initialize(Default::default(), Default::default(), false, key_share.threshold, consensus_group_map.clone().into())?; - data.inv_nonce_generation_session = Some(inv_nonce_generation_session); - - // start generation of zero-secret shares for inversed nonce computation session - let inv_zero_generation_session = Self::start_generation_session(&self.core, &other_consensus_group_nodes, - move |s, k, n, m| EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage( - EcdsaInversionZeroGenerationMessage { - session: s.into(), - sub_session: k.into(), - session_nonce: n, - message: m, - })); - inv_zero_generation_session.initialize(Default::default(), Default::default(), true, key_share.threshold * 2, consensus_group_map.clone().into())?; - data.inv_zero_generation_session = Some(inv_zero_generation_session); - - data.state = SessionState::NoncesGenerating; - - Ok(()) - } - - /// When signature nonce generation message is received. - pub fn on_signature_nonce_generation_message(&self, sender: &NodeId, message: &EcdsaSignatureNonceGenerationMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - if let &GenerationMessage::InitializeSession(ref message) = &message.message { - if &self.core.meta.master_node_id != sender { - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(s)) if s == *sender => (), - _ => return Err(Error::InvalidMessage), - } - } - - let consensus_group: BTreeSet = message.nodes.keys().cloned().map(Into::into).collect(); - let mut other_consensus_group_nodes = consensus_group.clone(); - other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - - data.sig_nonce_generation_session = Some(Self::start_generation_session(&self.core, &other_consensus_group_nodes, - |s, k, n, m| EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage( - EcdsaSignatureNonceGenerationMessage { - session: s.into(), - sub_session: k.into(), - session_nonce: n, - message: m, - }))); - - data.state = SessionState::NoncesGenerating; - } - - { - let generation_session = data.sig_nonce_generation_session.as_ref().ok_or(Error::InvalidStateForRequest)?; - let is_key_generating = generation_session.state() != GenerationSessionState::Finished; - generation_session.process_message(sender, &message.message)?; - - let is_key_generated = generation_session.state() == GenerationSessionState::Finished; - if !is_key_generating || !is_key_generated { - return Ok(()); - } - } - - if !Self::check_nonces_generated(&*data) { - return Ok(()); - } - - Self::send_inversed_nonce_coeff_share(&self.core, &mut*data)?; - data.state = if self.core.meta.master_node_id != self.core.meta.self_node_id { - SessionState::SignatureComputing - } else { - SessionState::WaitingForInversedNonceShares - }; - - Ok(()) - } - - /// When inversion nonce generation message is received. - pub fn on_inversion_nonce_generation_message(&self, sender: &NodeId, message: &EcdsaInversionNonceGenerationMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - if let &GenerationMessage::InitializeSession(ref message) = &message.message { - if &self.core.meta.master_node_id != sender { - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(s)) if s == *sender => (), - _ => return Err(Error::InvalidMessage), - } - } - - let consensus_group: BTreeSet = message.nodes.keys().cloned().map(Into::into).collect(); - let mut other_consensus_group_nodes = consensus_group.clone(); - other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - - data.inv_nonce_generation_session = Some(Self::start_generation_session(&self.core, &other_consensus_group_nodes, - |s, k, n, m| EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage( - EcdsaInversionNonceGenerationMessage { - session: s.into(), - sub_session: k.into(), - session_nonce: n, - message: m, - }))); - - data.state = SessionState::NoncesGenerating; - } - - { - let generation_session = data.inv_nonce_generation_session.as_ref().ok_or(Error::InvalidStateForRequest)?; - let is_key_generating = generation_session.state() != GenerationSessionState::Finished; - generation_session.process_message(sender, &message.message)?; - - let is_key_generated = generation_session.state() == GenerationSessionState::Finished; - if !is_key_generating || !is_key_generated { - return Ok(()); - } - } - - if !Self::check_nonces_generated(&*data) { - return Ok(()); - } - - Self::send_inversed_nonce_coeff_share(&self.core, &mut*data)?; - data.state = if self.core.meta.master_node_id != self.core.meta.self_node_id { - SessionState::SignatureComputing - } else { - SessionState::WaitingForInversedNonceShares - }; - - Ok(()) - } - - /// When inversion zero generation message is received. - pub fn on_inversion_zero_generation_message(&self, sender: &NodeId, message: &EcdsaInversionZeroGenerationMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - if let &GenerationMessage::InitializeSession(ref message) = &message.message { - if &self.core.meta.master_node_id != sender { - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(s)) if s == *sender => (), - _ => return Err(Error::InvalidMessage), - } - } - - let consensus_group: BTreeSet = message.nodes.keys().cloned().map(Into::into).collect(); - let mut other_consensus_group_nodes = consensus_group.clone(); - other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - - data.inv_zero_generation_session = Some(Self::start_generation_session(&self.core, &other_consensus_group_nodes, - |s, k, n, m| EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage( - EcdsaInversionZeroGenerationMessage { - session: s.into(), - sub_session: k.into(), - session_nonce: n, - message: m, - }))); - - data.state = SessionState::NoncesGenerating; - } - - { - let generation_session = data.inv_zero_generation_session.as_ref().ok_or(Error::InvalidStateForRequest)?; - let is_key_generating = generation_session.state() != GenerationSessionState::Finished; - generation_session.process_message(sender, &message.message)?; - - let is_key_generated = generation_session.state() == GenerationSessionState::Finished; - if !is_key_generating || !is_key_generated { - return Ok(()); - } - } - - if !Self::check_nonces_generated(&*data) { - return Ok(()); - } - - Self::send_inversed_nonce_coeff_share(&self.core, &mut*data)?; - data.state = if self.core.meta.master_node_id != self.core.meta.self_node_id { - SessionState::SignatureComputing - } else { - SessionState::WaitingForInversedNonceShares - }; - - Ok(()) - } - - /// When inversed nonce share is received. - pub fn on_inversed_nonce_coeff_share(&self, sender: &NodeId, message: &EcdsaSigningInversedNonceCoeffShare) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - if self.core.meta.self_node_id != self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - match data.state { - SessionState::WaitingForInversedNonceShares => (), - SessionState::NoncesGenerating => return Err(Error::TooEarlyForRequest), - _ => return Err(Error::InvalidStateForRequest), - } - - let inversed_nonce_coeff = { - let consensus_group = data.consensus_session.select_consensus_group()?.clone(); - { - let inversed_nonce_coeff_shares = data.inversed_nonce_coeff_shares.as_mut() - .expect("we are in WaitingForInversedNonceShares state; inversed_nonce_coeff_shares are filled before this state; qed"); - match inversed_nonce_coeff_shares.entry(sender.clone()) { - Entry::Occupied(_) => return Err(Error::InvalidStateForRequest), - Entry::Vacant(entry) => { - entry.insert(message.inversed_nonce_coeff_share.clone().into()); - }, - } - - if consensus_group.iter().any(|n| !inversed_nonce_coeff_shares.contains_key(n)) { - return Ok(()); - } - } - - Self::compute_inversed_nonce_coeff(&self.core, &*data)? - }; - - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - let message_hash = data.message_hash - .expect("we are on master node; on master node message_hash is filled in initialize(); on_generation_message follows initialize; qed"); - - let nonce_exists_proof = "nonce is generated before signature is computed; we are in SignatureComputing state; qed"; - let sig_nonce_public = data.sig_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.0; - let inv_nonce_share = data.inv_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.2; - - self.core.disseminate_jobs(&mut data.consensus_session, &version, sig_nonce_public, inv_nonce_share, inversed_nonce_coeff, message_hash) - } - - /// When partial signature is requested. - pub fn on_partial_signature_requested(&self, sender: &NodeId, message: &EcdsaRequestPartialSignature) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let key_share = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let mut data = self.data.lock(); - - if sender != &self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - if data.state != SessionState::SignatureComputing { - return Err(Error::InvalidStateForRequest); - } - - let nonce_exists_proof = "nonce is generated before signature is computed; we are in SignatureComputing state; qed"; - let sig_nonce_public = data.sig_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.0; - let inv_nonce_share = data.inv_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.2; - - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - let key_version = key_share.version(&version)?.hash.clone(); - - let signing_job = EcdsaSigningJob::new_on_slave(key_share.clone(), key_version, sig_nonce_public, inv_nonce_share)?; - let signing_transport = self.core.signing_transport(); - - data.consensus_session.on_job_request(sender, EcdsaPartialSigningRequest { - id: message.request_id.clone().into(), - inversed_nonce_coeff: message.inversed_nonce_coeff.clone().into(), - message_hash: message.message_hash.clone().into(), - }, signing_job, signing_transport).map(|_| ()) - } - - /// When partial signature is received. - pub fn on_partial_signature(&self, sender: &NodeId, message: &EcdsaPartialSignature) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - data.consensus_session.on_job_response(sender, EcdsaPartialSigningResponse { - request_id: message.request_id.clone().into(), - partial_signature_s: message.partial_signature_s.clone().into(), - })?; - - if data.consensus_session.state() != ConsensusSessionState::Finished { - return Ok(()); - } - - // send compeltion signal to all nodes, except for rejected nodes - for node in data.consensus_session.consensus_non_rejected_nodes() { - self.core.cluster.send(&node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionCompleted(EcdsaSigningSessionCompleted { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - })))?; - } - - let result = data.consensus_session.result()?; - Self::set_signing_result(&self.core, &mut *data, Ok(result)); - - Ok(()) - } - - /// When session is completed. - pub fn on_session_completed(&self, sender: &NodeId, message: &EcdsaSigningSessionCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - self.data.lock().consensus_session.on_session_completed(sender) - } - - /// Process error from the other node. - fn process_node_error(&self, node: Option<&NodeId>, error: Error) -> Result<(), Error> { - let mut data = self.data.lock(); - let is_self_node_error = node.map(|n| n == &self.core.meta.self_node_id).unwrap_or(false); - // error is always fatal if coming from this node - if is_self_node_error { - Self::set_signing_result(&self.core, &mut *data, Err(error.clone())); - return Err(error); - } - - match { - match node { - Some(node) => data.consensus_session.on_node_error(node, error.clone()), - None => data.consensus_session.on_session_timeout(), - } - } { - Ok(false) => { - Ok(()) - }, - Ok(true) => { - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - - let message_hash = data.message_hash.as_ref().cloned() - .expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when message_hash.is_some(); qed"); - - let nonce_exists_proof = "on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when nonces generation has completed; qed"; - let sig_nonce_public = data.sig_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.0; - let inv_nonce_share = data.inv_nonce_generation_session.as_ref().expect(nonce_exists_proof).joint_public_and_secret().expect(nonce_exists_proof)?.2; - - let inversed_nonce_coeff = Self::compute_inversed_nonce_coeff(&self.core, &*data)?; - - let disseminate_result = self.core.disseminate_jobs(&mut data.consensus_session, &version, sig_nonce_public, inv_nonce_share, inversed_nonce_coeff, message_hash); - match disseminate_result { - Ok(()) => Ok(()), - Err(err) => { - warn!("{}: ECDSA signing session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - Self::set_signing_result(&self.core, &mut *data, Err(err.clone())); - Err(err) - } - } - }, - Err(err) => { - warn!("{}: ECDSA signing session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - Self::set_signing_result(&self.core, &mut *data, Err(err.clone())); - Err(err) - }, - } - } - - /// Start generation session. - fn start_generation_session(core: &SessionCore, other_consensus_group_nodes: &BTreeSet, map_message: F) -> GenerationSession - where F: Fn(SessionId, Secret, u64, GenerationMessage) -> EcdsaSigningMessage + Send + Sync + 'static { - GenerationSession::new(GenerationSessionParams { - id: core.meta.id.clone(), - self_node_id: core.meta.self_node_id.clone(), - key_storage: None, - cluster: Arc::new(NonceGenerationTransport { - id: core.meta.id.clone(), - access_key: core.access_key.clone(), - nonce: core.nonce, - cluster: core.cluster.clone(), - other_nodes_ids: other_consensus_group_nodes.clone(), - map: map_message, - }), - nonce: None, - }).0 - } - - /// Set signing session result. - fn set_signing_result(core: &SessionCore, data: &mut SessionData, result: Result) { - if let Some(DelegationStatus::DelegatedFrom(master, nonce)) = data.delegation_status.take() { - // error means can't communicate => ignore it - let _ = match result.as_ref() { - Ok(signature) => core.cluster.send(&master, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(EcdsaSigningSessionDelegationCompleted { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: nonce, - signature: signature.clone().into(), - }))), - Err(error) => core.cluster.send(&master, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(EcdsaSigningSessionError { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: nonce, - error: error.clone().into(), - }))), - }; - } - - data.result = Some(result.clone()); - core.completed.send(result); - } - - /// Check if all nonces are generated. - fn check_nonces_generated(data: &SessionData) -> bool { - let expect_proof = "check_nonces_generated is called when som nonce-gen session is completed; - all nonce-gen sessions are created at once; qed"; - let sig_nonce_generation_session = data.sig_nonce_generation_session.as_ref().expect(expect_proof); - let inv_nonce_generation_session = data.inv_nonce_generation_session.as_ref().expect(expect_proof); - let inv_zero_generation_session = data.inv_zero_generation_session.as_ref().expect(expect_proof); - - sig_nonce_generation_session.state() == GenerationSessionState::Finished - && inv_nonce_generation_session.state() == GenerationSessionState::Finished - && inv_zero_generation_session.state() == GenerationSessionState::Finished - } - - /// Broadcast inversed nonce share. - fn send_inversed_nonce_coeff_share(core: &SessionCore, data: &mut SessionData) -> Result<(), Error> { - let proof = "inversed nonce coeff share is sent after nonces generation is completed; qed"; - - let sig_nonce_generation_session = data.sig_nonce_generation_session.as_ref().expect(proof); - let sig_nonce = sig_nonce_generation_session.joint_public_and_secret().expect(proof).expect(proof).2; - - let inv_nonce_generation_session = data.inv_nonce_generation_session.as_ref().expect(proof); - let inv_nonce = inv_nonce_generation_session.joint_public_and_secret().expect(proof).expect(proof).2; - - let inv_zero_generation_session = data.inv_zero_generation_session.as_ref().expect(proof); - let inv_zero = inv_zero_generation_session.joint_public_and_secret().expect(proof).expect(proof).2; - - let inversed_nonce_coeff_share = math::compute_ecdsa_inversed_secret_coeff_share(&sig_nonce, &inv_nonce, &inv_zero)?; - if core.meta.self_node_id == core.meta.master_node_id { - let mut inversed_nonce_coeff_shares = BTreeMap::new(); - inversed_nonce_coeff_shares.insert(core.meta.self_node_id.clone(), inversed_nonce_coeff_share); - data.inversed_nonce_coeff_shares = Some(inversed_nonce_coeff_shares); - Ok(()) - } else { - core.cluster.send(&core.meta.master_node_id, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(EcdsaSigningInversedNonceCoeffShare { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: core.nonce, - inversed_nonce_coeff_share: inversed_nonce_coeff_share.into(), - }))) - } - } - - /// Compute inversed nonce coefficient on master node. - fn compute_inversed_nonce_coeff(core: &SessionCore, data: &SessionData) -> Result { - let proof = "inversed nonce coeff is computed on master node; key version exists on master node"; - let key_share = core.key_share.as_ref().expect(proof); - let key_version = key_share.version(data.version.as_ref().expect(proof)).expect(proof); - - let proof = "inversed nonce coeff is computed after all shares are received; qed"; - let inversed_nonce_coeff_shares = data.inversed_nonce_coeff_shares.as_ref().expect(proof); - - math::compute_ecdsa_inversed_secret_coeff_from_shares(key_share.threshold, - &inversed_nonce_coeff_shares.keys().map(|n| key_version.id_numbers[n].clone()).collect::>(), - &inversed_nonce_coeff_shares.values().cloned().collect::>()) - } -} - -impl ClusterSession for SessionImpl { - type Id = SessionIdWithSubSession; - type CreationData = Requester; - type SuccessfulResult = Signature; - - fn type_name() -> &'static str { - "ecdsa_signing" - } - - fn id(&self) -> SessionIdWithSubSession { - SessionIdWithSubSession::new(self.core.meta.id.clone(), self.core.access_key.clone()) - } - - fn is_finished(&self) -> bool { - let data = self.data.lock(); - data.consensus_session.state() == ConsensusSessionState::Failed - || data.consensus_session.state() == ConsensusSessionState::Finished - || data.result.is_some() - } - - fn on_node_timeout(&self, node: &NodeId) { - // ignore error, only state matters - let _ = self.process_node_error(Some(node), Error::NodeDisconnected); - } - - fn on_session_timeout(&self) { - // ignore error, only state matters - let _ = self.process_node_error(None, Error::NodeDisconnected); - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - let is_fatal = self.process_node_error(Some(node), error.clone()).is_err(); - let is_this_node_error = *node == self.core.meta.self_node_id; - if is_fatal || is_this_node_error { - // error in signing session is non-fatal, if occurs on slave node - // => either respond with error - // => or broadcast error - let message = Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(EcdsaSigningSessionError { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - error: error.clone().into(), - })); - - // do not bother processing send error, as we already processing error - let _ = if self.core.meta.master_node_id == self.core.meta.self_node_id { - self.core.cluster.broadcast(message) - } else { - self.core.cluster.send(&self.core.meta.master_node_id, message) - }; - } - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::EcdsaSigning(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl NonceGenerationTransport where F: Fn(SessionId, Secret, u64, GenerationMessage) -> EcdsaSigningMessage + Send + Sync { - fn map_message(&self, message: Message) -> Result { - match message { - Message::Generation(message) => Ok(Message::EcdsaSigning((self.map)(self.id.clone(), self.access_key.clone(), self.nonce, message))), - _ => Err(Error::InvalidMessage), - } - } -} - -impl Cluster for NonceGenerationTransport where F: Fn(SessionId, Secret, u64, GenerationMessage) -> EcdsaSigningMessage + Send + Sync { - fn broadcast(&self, message: Message) -> Result<(), Error> { - let message = self.map_message(message)?; - for to in &self.other_nodes_ids { - self.cluster.send(to, message.clone())?; - } - Ok(()) - } - - fn send(&self, to: &NodeId, message: Message) -> Result<(), Error> { - debug_assert!(self.other_nodes_ids.contains(to)); - self.cluster.send(to, self.map_message(message)?) - } - - fn is_connected(&self, node: &NodeId) -> bool { - self.cluster.is_connected(node) - } - - fn nodes(&self) -> BTreeSet { - self.cluster.nodes() - } - - fn configured_nodes_count(&self) -> usize { - self.cluster.configured_nodes_count() - } - - fn connected_nodes_count(&self) -> usize { - self.cluster.connected_nodes_count() - } -} - -impl SessionCore { - pub fn signing_transport(&self) -> SigningJobTransport { - SigningJobTransport { - id: self.meta.id.clone(), - access_key: self.access_key.clone(), - nonce: self.nonce, - cluster: self.cluster.clone() - } - } - - pub fn disseminate_jobs(&self, consensus_session: &mut SigningConsensusSession, version: &H256, nonce_public: Public, inv_nonce_share: Secret, inversed_nonce_coeff: Secret, message_hash: H256) -> Result<(), Error> { - let key_share = match self.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let key_version = key_share.version(version)?.hash.clone(); - let signing_job = EcdsaSigningJob::new_on_master(key_share.clone(), key_version, nonce_public, inv_nonce_share, inversed_nonce_coeff, message_hash)?; - consensus_session.disseminate_jobs(signing_job, self.signing_transport(), false).map(|_| ()) - } -} - -impl JobTransport for SigningConsensusTransport { - type PartialJobRequest=Requester; - type PartialJobResponse=bool; - - fn send_partial_request(&self, node: &NodeId, request: Requester) -> Result<(), Error> { - let version = self.version.as_ref() - .expect("send_partial_request is called on initialized master node only; version is filled in before initialization starts on master node; qed"); - self.cluster.send(node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(EcdsaSigningConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requester: request.into(), - version: version.clone().into(), - }) - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { - self.cluster.send(node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(EcdsaSigningConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: response, - }) - }))) - } -} - -impl JobTransport for SigningJobTransport { - type PartialJobRequest=EcdsaPartialSigningRequest; - type PartialJobResponse=EcdsaPartialSigningResponse; - - fn send_partial_request(&self, node: &NodeId, request: EcdsaPartialSigningRequest) -> Result<(), Error> { - self.cluster.send(node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaRequestPartialSignature(EcdsaRequestPartialSignature { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - request_id: request.id.into(), - inversed_nonce_coeff: request.inversed_nonce_coeff.into(), - message_hash: request.message_hash.into(), - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: EcdsaPartialSigningResponse) -> Result<(), Error> { - self.cluster.send(node, Message::EcdsaSigning(EcdsaSigningMessage::EcdsaPartialSignature(EcdsaPartialSignature { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - request_id: response.request_id.into(), - partial_signature_s: response.partial_signature_s.into(), - }))) - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use ethereum_types::H256; - use crypto::publickey::{Random, Generator, Public, verify_public, public_to_address}; - use key_server_cluster::{SessionId, Error, KeyStorage}; - use key_server_cluster::cluster::tests::{MessageLoop as ClusterMessageLoop}; - use key_server_cluster::signing_session_ecdsa::SessionImpl; - use key_server_cluster::generation_session::tests::MessageLoop as GenerationMessageLoop; - - #[derive(Debug)] - pub struct MessageLoop(pub ClusterMessageLoop); - - impl MessageLoop { - pub fn new(num_nodes: usize, threshold: usize) -> Result { - let ml = GenerationMessageLoop::new(num_nodes).init(threshold)?; - ml.0.loop_until(|| ml.0.is_empty()); // complete generation session - - Ok(MessageLoop(ml.0)) - } - - pub fn init_with_version(self, key_version: Option) -> Result<(Self, Public, H256), Error> { - let message_hash = H256::random(); - let requester = Random.generate().unwrap(); - let signature = crypto::publickey::sign(requester.secret(), &SessionId::default()).unwrap(); - self.0.cluster(0).client() - .new_ecdsa_signing_session(Default::default(), signature.into(), key_version, message_hash) - .map(|_| (self, *requester.public(), message_hash)) - } - - pub fn init(self) -> Result<(Self, Public, H256), Error> { - let key_version = self.0.key_storage(0).get(&Default::default()) - .unwrap().unwrap().versions.iter().last().unwrap().hash; - self.init_with_version(Some(key_version)) - } - - pub fn init_delegated(self) -> Result<(Self, Public, H256), Error> { - self.0.key_storage(0).remove(&Default::default()).unwrap(); - self.init_with_version(None) - } - - pub fn init_with_isolated(self) -> Result<(Self, Public, H256), Error> { - self.0.isolate(1); - self.init() - } - - pub fn session_at(&self, idx: usize) -> Arc { - self.0.sessions(idx).ecdsa_signing_sessions.first().unwrap() - } - - pub fn ensure_completed(&self) { - self.0.loop_until(|| self.0.is_empty()); - assert!(self.session_at(0).wait().is_ok()); - } - } - - #[test] - fn failed_gen_ecdsa_sign_session_when_threshold_is_too_low() { - let test_cases = [(1, 2), (2, 4), (3, 6), (4, 6)]; - for &(threshold, num_nodes) in &test_cases { - assert_eq!(MessageLoop::new(num_nodes, threshold).unwrap().init().unwrap_err(), - Error::ConsensusUnreachable); - } - } - - #[test] - fn complete_gen_ecdsa_sign_session() { - let test_cases = [(0, 1), (2, 5), (2, 6), (3, 11), (4, 11)]; - for &(threshold, num_nodes) in &test_cases { - let (ml, _, message) = MessageLoop::new(num_nodes, threshold).unwrap().init().unwrap(); - ml.0.loop_until(|| ml.0.is_empty()); - - let signer_public = ml.0.key_storage(0).get(&Default::default()).unwrap().unwrap().public; - let signature = ml.session_at(0).wait().unwrap(); - assert!(verify_public(&signer_public, &signature, &message).unwrap()); - } - } - - #[test] - fn ecdsa_complete_signing_session_with_single_node_failing() { - let (ml, requester, _) = MessageLoop::new(4, 1).unwrap().init().unwrap(); - - // we need at least 3-of-4 nodes to agree to reach consensus - // let's say 1 of 4 nodes disagee - ml.0.acl_storage(1).prohibit(public_to_address(&requester), Default::default()); - - // then consensus reachable, but single node will disagree - ml.ensure_completed(); - } - - #[test] - fn ecdsa_complete_signing_session_with_acl_check_failed_on_master() { - let (ml, requester, _) = MessageLoop::new(4, 1).unwrap().init().unwrap(); - - // we need at least 3-of-4 nodes to agree to reach consensus - // let's say 1 of 4 nodes (here: master) disagee - ml.0.acl_storage(0).prohibit(public_to_address(&requester), Default::default()); - - // then consensus reachable, but single node will disagree - ml.ensure_completed(); - } - - #[test] - fn ecdsa_signing_works_when_delegated_to_other_node() { - MessageLoop::new(4, 1).unwrap().init_delegated().unwrap().0.ensure_completed(); - } - - #[test] - fn ecdsa_signing_works_when_share_owners_are_isolated() { - MessageLoop::new(6, 2).unwrap().init_with_isolated().unwrap().0.ensure_completed(); - } -} diff --git a/secret-store/src/key_server_cluster/client_sessions/signing_session_schnorr.rs b/secret-store/src/key_server_cluster/client_sessions/signing_session_schnorr.rs deleted file mode 100644 index 26ce3f3af9e..00000000000 --- a/secret-store/src/key_server_cluster/client_sessions/signing_session_schnorr.rs +++ /dev/null @@ -1,1119 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeSet; -use std::sync::Arc; -use futures::Oneshot; -use parking_lot::Mutex; -use crypto::publickey::{Public, Secret}; -use ethereum_types::H256; -use key_server_cluster::{Error, NodeId, SessionId, Requester, SessionMeta, AclStorage, DocumentKeyShare}; -use key_server_cluster::cluster::{Cluster}; -use key_server_cluster::cluster_sessions::{SessionIdWithSubSession, ClusterSession, CompletionSignal}; -use key_server_cluster::generation_session::{SessionImpl as GenerationSession, SessionParams as GenerationSessionParams, - SessionState as GenerationSessionState}; -use key_server_cluster::message::{Message, SchnorrSigningMessage, SchnorrSigningConsensusMessage, SchnorrSigningGenerationMessage, - SchnorrRequestPartialSignature, SchnorrPartialSignature, SchnorrSigningSessionCompleted, GenerationMessage, - ConsensusMessage, SchnorrSigningSessionError, InitializeConsensusSession, ConfirmConsensusInitialization, - SchnorrSigningSessionDelegation, SchnorrSigningSessionDelegationCompleted}; -use key_server_cluster::jobs::job_session::JobTransport; -use key_server_cluster::jobs::key_access_job::KeyAccessJob; -use key_server_cluster::jobs::signing_job_schnorr::{SchnorrPartialSigningRequest, SchnorrPartialSigningResponse, SchnorrSigningJob}; -use key_server_cluster::jobs::consensus_session::{ConsensusSessionParams, ConsensusSessionState, ConsensusSession}; - -/// Distributed Schnorr-signing session. -/// Based on "Efficient Multi-Party Digital Signature using Adaptive Secret Sharing for Low-Power Devices in Wireless Network" paper. -/// Brief overview: -/// 1) initialization: master node (which has received request for signing the message) requests all other nodes to sign the message -/// 2) ACL check: all nodes which have received the request are querying ACL-contract to check if requestor has access to the private key -/// 3) partial signing: every node which has succussfully checked access for the requestor do a partial signing -/// 4) signing: master node receives all partial signatures of the secret and computes the signature -pub struct SessionImpl { - /// Session core. - core: SessionCore, - /// Session data. - data: Mutex, -} - -/// Immutable session data. -struct SessionCore { - /// Session metadata. - pub meta: SessionMeta, - /// Signing session access key. - pub access_key: Secret, - /// Key share. - pub key_share: Option, - /// Cluster which allows this node to send messages to other nodes in the cluster. - pub cluster: Arc, - /// Session-level nonce. - pub nonce: u64, - /// SessionImpl completion signal. - pub completed: CompletionSignal<(Secret, Secret)>, -} - -/// Signing consensus session type. -type SigningConsensusSession = ConsensusSession; - -/// Mutable session data. -struct SessionData { - /// Session state. - pub state: SessionState, - /// Message hash. - pub message_hash: Option, - /// Key version to use for decryption. - pub version: Option, - /// Consensus-based signing session. - pub consensus_session: SigningConsensusSession, - /// Session key generation session. - pub generation_session: Option, - /// Delegation status. - pub delegation_status: Option, - /// Decryption result. - pub result: Option>, -} - -/// Signing session state. -#[derive(Debug, PartialEq)] -#[cfg_attr(test, derive(Clone, Copy))] -pub enum SessionState { - /// State when consensus is establishing. - ConsensusEstablishing, - /// State when session key is generating. - SessionKeyGeneration, - /// State when signature is computing. - SignatureComputing, -} - -/// Session creation parameters -pub struct SessionParams { - /// Session metadata. - pub meta: SessionMeta, - /// Session access key. - pub access_key: Secret, - /// Key share. - pub key_share: Option, - /// ACL storage. - pub acl_storage: Arc, - /// Cluster - pub cluster: Arc, - /// Session nonce. - pub nonce: u64, -} - -/// Signing consensus transport. -struct SigningConsensusTransport { - /// Session id. - id: SessionId, - /// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Selected key version (on master node). - version: Option, - /// Cluster. - cluster: Arc, -} - -/// Signing key generation transport. -struct SessionKeyGenerationTransport { - /// Session access key. - access_key: Secret, - /// Cluster. - cluster: Arc, - /// Session-level nonce. - nonce: u64, - /// Other nodes ids. - other_nodes_ids: BTreeSet, -} - -/// Signing job transport -struct SigningJobTransport { - /// Session id. - id: SessionId, - /// Session access key. - access_key: Secret, - /// Session-level nonce. - nonce: u64, - /// Cluster. - cluster: Arc, -} - -/// Session delegation status. -enum DelegationStatus { - /// Delegated to other node. - DelegatedTo(NodeId), - /// Delegated from other node. - DelegatedFrom(NodeId, u64), -} - -impl SessionImpl { - /// Create new signing session. - pub fn new( - params: SessionParams, - requester: Option, - ) -> Result<(Self, Oneshot>), Error> { - debug_assert_eq!(params.meta.threshold, params.key_share.as_ref().map(|ks| ks.threshold).unwrap_or_default()); - - let consensus_transport = SigningConsensusTransport { - id: params.meta.id.clone(), - access_key: params.access_key.clone(), - nonce: params.nonce, - version: None, - cluster: params.cluster.clone(), - }; - let consensus_session = ConsensusSession::new(ConsensusSessionParams { - meta: params.meta.clone(), - consensus_executor: match requester { - Some(requester) => KeyAccessJob::new_on_master(params.meta.id.clone(), params.acl_storage.clone(), requester), - None => KeyAccessJob::new_on_slave(params.meta.id.clone(), params.acl_storage.clone()), - }, - consensus_transport: consensus_transport, - })?; - - let (completed, oneshot) = CompletionSignal::new(); - Ok((SessionImpl { - core: SessionCore { - meta: params.meta, - access_key: params.access_key, - key_share: params.key_share, - cluster: params.cluster, - nonce: params.nonce, - completed, - }, - data: Mutex::new(SessionData { - state: SessionState::ConsensusEstablishing, - message_hash: None, - version: None, - consensus_session: consensus_session, - generation_session: None, - delegation_status: None, - result: None, - }), - }, oneshot)) - } - - /// Wait for session completion. - #[cfg(test)] - pub fn wait(&self) -> Result<(Secret, Secret), Error> { - Self::wait_session(&self.core.completed, &self.data, None, |data| data.result.clone()) - .expect("wait_session returns Some if called without timeout; qed") - } - - /// Get session state (tests only). - #[cfg(test)] - pub fn state(&self) -> SessionState { - self.data.lock().state - } - - /// Delegate session to other node. - pub fn delegate(&self, master: NodeId, version: H256, message_hash: H256) -> Result<(), Error> { - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Err(Error::InvalidStateForRequest); - } - - let mut data = self.data.lock(); - if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { - return Err(Error::InvalidStateForRequest); - } - - data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(false); - self.core.cluster.send(&master, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(SchnorrSigningSessionDelegation { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - requester: data.consensus_session.consensus_job().executor().requester() - .expect("requester is passed to master node on creation; session can be delegated from master node only; qed") - .clone().into(), - version: version.into(), - message_hash: message_hash.into(), - })))?; - data.delegation_status = Some(DelegationStatus::DelegatedTo(master)); - Ok(()) - - } - - /// Initialize signing session on master node. - pub fn initialize(&self, version: H256, message_hash: H256) -> Result<(), Error> { - debug_assert_eq!(self.core.meta.self_node_id, self.core.meta.master_node_id); - - // check if version exists - let key_version = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share.version(&version)?, - }; - - let mut data = self.data.lock(); - let non_isolated_nodes = self.core.cluster.nodes(); - let mut consensus_nodes: BTreeSet<_> = key_version.id_numbers.keys() - .filter(|n| non_isolated_nodes.contains(*n)) - .cloned() - .chain(::std::iter::once(self.core.meta.self_node_id.clone())) - .collect(); - if let Some(&DelegationStatus::DelegatedFrom(delegation_master, _)) = data.delegation_status.as_ref() { - consensus_nodes.remove(&delegation_master); - } - - data.consensus_session.consensus_job_mut().transport_mut().version = Some(version.clone()); - data.version = Some(version.clone()); - data.message_hash = Some(message_hash); - data.consensus_session.initialize(consensus_nodes)?; - - if data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished { - let generation_session = GenerationSession::new(GenerationSessionParams { - id: self.core.meta.id.clone(), - self_node_id: self.core.meta.self_node_id.clone(), - key_storage: None, - cluster: Arc::new(SessionKeyGenerationTransport { - access_key: self.core.access_key.clone(), - cluster: self.core.cluster.clone(), - nonce: self.core.nonce, - other_nodes_ids: BTreeSet::new() - }), - nonce: None, - }).0; - generation_session.initialize(Default::default(), Default::default(), false, 0, vec![self.core.meta.self_node_id.clone()].into_iter().collect::>().into())?; - - debug_assert_eq!(generation_session.state(), GenerationSessionState::Finished); - let joint_public_and_secret = generation_session - .joint_public_and_secret() - .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed")?; - data.generation_session = Some(generation_session); - data.state = SessionState::SignatureComputing; - - self.core.disseminate_jobs(&mut data.consensus_session, &version, joint_public_and_secret.0, joint_public_and_secret.1, message_hash)?; - - debug_assert!(data.consensus_session.state() == ConsensusSessionState::Finished); - let result = data.consensus_session.result()?; - Self::set_signing_result(&self.core, &mut *data, Ok(result)); - } - - Ok(()) - } - - /// Process signing message. - pub fn process_message(&self, sender: &NodeId, message: &SchnorrSigningMessage) -> Result<(), Error> { - if self.core.nonce != message.session_nonce() { - return Err(Error::ReplayProtection); - } - - match message { - &SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref message) => - self.on_consensus_message(sender, message), - &SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref message) => - self.on_generation_message(sender, message), - &SchnorrSigningMessage::SchnorrRequestPartialSignature(ref message) => - self.on_partial_signature_requested(sender, message), - &SchnorrSigningMessage::SchnorrPartialSignature(ref message) => - self.on_partial_signature(sender, message), - &SchnorrSigningMessage::SchnorrSigningSessionError(ref message) => - self.process_node_error(Some(&sender), message.error.clone()), - &SchnorrSigningMessage::SchnorrSigningSessionCompleted(ref message) => - self.on_session_completed(sender, message), - &SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref message) => - self.on_session_delegated(sender, message), - &SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(ref message) => - self.on_session_delegation_completed(sender, message), - } - } - - /// When session is delegated to this node. - pub fn on_session_delegated(&self, sender: &NodeId, message: &SchnorrSigningSessionDelegation) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - { - let mut data = self.data.lock(); - if data.consensus_session.state() != ConsensusSessionState::WaitingForInitialization || data.delegation_status.is_some() { - return Err(Error::InvalidStateForRequest); - } - - data.consensus_session.consensus_job_mut().executor_mut().set_requester(message.requester.clone().into()); - data.delegation_status = Some(DelegationStatus::DelegatedFrom(sender.clone(), message.session_nonce)); - } - - self.initialize(message.version.clone().into(), message.message_hash.clone().into()) - } - - /// When delegated session is completed on other node. - pub fn on_session_delegation_completed(&self, sender: &NodeId, message: &SchnorrSigningSessionDelegationCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Err(Error::InvalidStateForRequest); - } - - let mut data = self.data.lock(); - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(ref node)) if node == sender => (), - _ => return Err(Error::InvalidMessage), - } - - Self::set_signing_result(&self.core, &mut *data, Ok((message.signature_c.clone().into(), message.signature_s.clone().into()))); - - Ok(()) - } - - /// When consensus-related message is received. - pub fn on_consensus_message(&self, sender: &NodeId, message: &SchnorrSigningConsensusMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - let is_establishing_consensus = data.consensus_session.state() == ConsensusSessionState::EstablishingConsensus; - - if let &ConsensusMessage::InitializeConsensusSession(ref msg) = &message.message { - let version = msg.version.clone().into(); - let has_key_share = self.core.key_share.as_ref() - .map(|ks| ks.version(&version).is_ok()) - .unwrap_or(false); - data.consensus_session.consensus_job_mut().executor_mut().set_has_key_share(has_key_share); - data.version = Some(version); - } - data.consensus_session.on_consensus_message(&sender, &message.message)?; - - let is_consensus_established = data.consensus_session.state() == ConsensusSessionState::ConsensusEstablished; - if self.core.meta.self_node_id != self.core.meta.master_node_id || !is_establishing_consensus || !is_consensus_established { - return Ok(()); - } - - let consensus_group = data.consensus_session.select_consensus_group()?.clone(); - let mut other_consensus_group_nodes = consensus_group.clone(); - other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - - let key_share = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let generation_session = GenerationSession::new(GenerationSessionParams { - id: self.core.meta.id.clone(), - self_node_id: self.core.meta.self_node_id.clone(), - key_storage: None, - cluster: Arc::new(SessionKeyGenerationTransport { - access_key: self.core.access_key.clone(), - cluster: self.core.cluster.clone(), - nonce: self.core.nonce, - other_nodes_ids: other_consensus_group_nodes, - }), - nonce: None, - }).0; - - generation_session.initialize(Default::default(), Default::default(), false, key_share.threshold, consensus_group.into())?; - data.generation_session = Some(generation_session); - data.state = SessionState::SessionKeyGeneration; - - Ok(()) - } - - /// When session key related message is received. - pub fn on_generation_message(&self, sender: &NodeId, message: &SchnorrSigningGenerationMessage) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - - if let &GenerationMessage::InitializeSession(ref message) = &message.message { - if &self.core.meta.master_node_id != sender { - match data.delegation_status.as_ref() { - Some(&DelegationStatus::DelegatedTo(s)) if s == *sender => (), - _ => return Err(Error::InvalidMessage), - } - } - - let consensus_group: BTreeSet = message.nodes.keys().cloned().map(Into::into).collect(); - let mut other_consensus_group_nodes = consensus_group.clone(); - other_consensus_group_nodes.remove(&self.core.meta.self_node_id); - - let generation_session = GenerationSession::new(GenerationSessionParams { - id: self.core.meta.id.clone(), - self_node_id: self.core.meta.self_node_id.clone(), - key_storage: None, - cluster: Arc::new(SessionKeyGenerationTransport { - access_key: self.core.access_key.clone(), - cluster: self.core.cluster.clone(), - nonce: self.core.nonce, - other_nodes_ids: other_consensus_group_nodes - }), - nonce: None, - }).0; - data.generation_session = Some(generation_session); - data.state = SessionState::SessionKeyGeneration; - } - - { - let generation_session = data.generation_session.as_ref().ok_or(Error::InvalidStateForRequest)?; - let is_key_generating = generation_session.state() != GenerationSessionState::Finished; - generation_session.process_message(sender, &message.message)?; - - let is_key_generated = generation_session.state() == GenerationSessionState::Finished; - if !is_key_generating || !is_key_generated { - return Ok(()); - } - } - - data.state = SessionState::SignatureComputing; - if self.core.meta.master_node_id != self.core.meta.self_node_id { - return Ok(()); - } - - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - let message_hash = data.message_hash - .expect("we are on master node; on master node message_hash is filled in initialize(); on_generation_message follows initialize; qed"); - let joint_public_and_secret = data.generation_session.as_ref() - .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed") - .joint_public_and_secret() - .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed")?; - self.core.disseminate_jobs(&mut data.consensus_session, &version, joint_public_and_secret.0, joint_public_and_secret.1, message_hash) - } - - /// When partial signature is requested. - pub fn on_partial_signature_requested(&self, sender: &NodeId, message: &SchnorrRequestPartialSignature) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let key_share = match self.core.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let mut data = self.data.lock(); - - if sender != &self.core.meta.master_node_id { - return Err(Error::InvalidMessage); - } - if data.state != SessionState::SignatureComputing { - return Err(Error::InvalidStateForRequest); - } - - let joint_public_and_secret = data.generation_session.as_ref() - .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed") - .joint_public_and_secret() - .expect("session key is generated before signature is computed; we are in SignatureComputing state; qed")?; - let key_version = key_share.version(data.version.as_ref().ok_or(Error::InvalidMessage)?)?.hash.clone(); - let signing_job = SchnorrSigningJob::new_on_slave(self.core.meta.self_node_id.clone(), key_share.clone(), key_version, joint_public_and_secret.0, joint_public_and_secret.1)?; - let signing_transport = self.core.signing_transport(); - - data.consensus_session.on_job_request(sender, SchnorrPartialSigningRequest { - id: message.request_id.clone().into(), - message_hash: message.message_hash.clone().into(), - other_nodes_ids: message.nodes.iter().cloned().map(Into::into).collect(), - }, signing_job, signing_transport).map(|_| ()) - } - - /// When partial signature is received. - pub fn on_partial_signature(&self, sender: &NodeId, message: &SchnorrPartialSignature) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - let mut data = self.data.lock(); - data.consensus_session.on_job_response(sender, SchnorrPartialSigningResponse { - request_id: message.request_id.clone().into(), - partial_signature: message.partial_signature.clone().into(), - })?; - - if data.consensus_session.state() != ConsensusSessionState::Finished { - return Ok(()); - } - - // send compeltion signal to all nodes, except for rejected nodes - for node in data.consensus_session.consensus_non_rejected_nodes() { - self.core.cluster.send(&node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionCompleted(SchnorrSigningSessionCompleted { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - })))?; - } - - let result = data.consensus_session.result()?; - Self::set_signing_result(&self.core, &mut *data, Ok(result)); - - Ok(()) - } - - /// When session is completed. - pub fn on_session_completed(&self, sender: &NodeId, message: &SchnorrSigningSessionCompleted) -> Result<(), Error> { - debug_assert!(self.core.meta.id == *message.session); - debug_assert!(self.core.access_key == *message.sub_session); - debug_assert!(sender != &self.core.meta.self_node_id); - - self.data.lock().consensus_session.on_session_completed(sender) - } - - /// Process error from the other node. - fn process_node_error(&self, node: Option<&NodeId>, error: Error) -> Result<(), Error> { - let mut data = self.data.lock(); - let is_self_node_error = node.map(|n| n == &self.core.meta.self_node_id).unwrap_or(false); - // error is always fatal if coming from this node - if is_self_node_error { - Self::set_signing_result(&self.core, &mut *data, Err(error.clone())); - return Err(error); - } - - match { - match node { - Some(node) => data.consensus_session.on_node_error(node, error.clone()), - None => data.consensus_session.on_session_timeout(), - } - } { - Ok(false) => { - Ok(()) - }, - Ok(true) => { - let version = data.version.as_ref().ok_or(Error::InvalidMessage)?.clone(); - let message_hash = data.message_hash.as_ref().cloned() - .expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when message_hash.is_some(); qed"); - let joint_public_and_secret = data.generation_session.as_ref() - .expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when message_hash.is_some(); qed") - .joint_public_and_secret() - .expect("on_node_error returned true; this means that jobs must be REsent; this means that jobs already have been sent; jobs are sent when message_hash.is_some(); qed")?; - let disseminate_result = self.core.disseminate_jobs(&mut data.consensus_session, &version, joint_public_and_secret.0, joint_public_and_secret.1, message_hash); - match disseminate_result { - Ok(()) => Ok(()), - Err(err) => { - warn!("{}: signing session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - Self::set_signing_result(&self.core, &mut *data, Err(err.clone())); - Err(err) - } - } - }, - Err(err) => { - warn!("{}: signing session failed with error: {:?} from {:?}", &self.core.meta.self_node_id, error, node); - Self::set_signing_result(&self.core, &mut *data, Err(err.clone())); - Err(err) - }, - } - } - - /// Set signing session result. - fn set_signing_result(core: &SessionCore, data: &mut SessionData, result: Result<(Secret, Secret), Error>) { - if let Some(DelegationStatus::DelegatedFrom(master, nonce)) = data.delegation_status.take() { - // error means can't communicate => ignore it - let _ = match result.as_ref() { - Ok(signature) => core.cluster.send(&master, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(SchnorrSigningSessionDelegationCompleted { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: nonce, - signature_c: signature.0.clone().into(), - signature_s: signature.1.clone().into(), - }))), - Err(error) => core.cluster.send(&master, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(SchnorrSigningSessionError { - session: core.meta.id.clone().into(), - sub_session: core.access_key.clone().into(), - session_nonce: nonce, - error: error.clone().into(), - }))), - }; - } - - data.result = Some(result.clone()); - core.completed.send(result); - } -} - -impl ClusterSession for SessionImpl { - type Id = SessionIdWithSubSession; - type CreationData = Requester; - type SuccessfulResult = (Secret, Secret); - - fn type_name() -> &'static str { - "signing" - } - - fn id(&self) -> SessionIdWithSubSession { - SessionIdWithSubSession::new(self.core.meta.id.clone(), self.core.access_key.clone()) - } - - fn is_finished(&self) -> bool { - let data = self.data.lock(); - data.consensus_session.state() == ConsensusSessionState::Failed - || data.consensus_session.state() == ConsensusSessionState::Finished - || data.result.is_some() - } - - fn on_node_timeout(&self, node: &NodeId) { - // ignore error, only state matters - let _ = self.process_node_error(Some(node), Error::NodeDisconnected); - } - - fn on_session_timeout(&self) { - // ignore error, only state matters - let _ = self.process_node_error(None, Error::NodeDisconnected); - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - let is_fatal = self.process_node_error(Some(node), error.clone()).is_err(); - let is_this_node_error = *node == self.core.meta.self_node_id; - if is_fatal || is_this_node_error { - // error in signing session is non-fatal, if occurs on slave node - // => either respond with error - // => or broadcast error - let message = Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(SchnorrSigningSessionError { - session: self.core.meta.id.clone().into(), - sub_session: self.core.access_key.clone().into(), - session_nonce: self.core.nonce, - error: error.clone().into(), - })); - - // do not bother processing send error, as we already processing error - let _ = if self.core.meta.master_node_id == self.core.meta.self_node_id { - self.core.cluster.broadcast(message) - } else { - self.core.cluster.send(&self.core.meta.master_node_id, message) - }; - } - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *message { - Message::SchnorrSigning(ref message) => self.process_message(sender, message), - _ => unreachable!("cluster checks message to be correct before passing; qed"), - } - } -} - -impl SessionKeyGenerationTransport { - fn map_message(&self, message: Message) -> Result { - match message { - Message::Generation(message) => Ok(Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningGenerationMessage(SchnorrSigningGenerationMessage { - session: message.session_id().clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - message: message, - }))), - _ => Err(Error::InvalidMessage), - } - } -} - -impl Cluster for SessionKeyGenerationTransport { - fn broadcast(&self, message: Message) -> Result<(), Error> { - let message = self.map_message(message)?; - for to in &self.other_nodes_ids { - self.cluster.send(to, message.clone())?; - } - Ok(()) - } - - fn send(&self, to: &NodeId, message: Message) -> Result<(), Error> { - debug_assert!(self.other_nodes_ids.contains(to)); - self.cluster.send(to, self.map_message(message)?) - } - - fn is_connected(&self, node: &NodeId) -> bool { - self.cluster.is_connected(node) - } - - fn nodes(&self) -> BTreeSet { - self.cluster.nodes() - } - - fn configured_nodes_count(&self) -> usize { - self.cluster.configured_nodes_count() - } - - fn connected_nodes_count(&self) -> usize { - self.cluster.connected_nodes_count() - } -} - -impl SessionCore { - pub fn signing_transport(&self) -> SigningJobTransport { - SigningJobTransport { - id: self.meta.id.clone(), - access_key: self.access_key.clone(), - nonce: self.nonce, - cluster: self.cluster.clone() - } - } - - pub fn disseminate_jobs(&self, consensus_session: &mut SigningConsensusSession, version: &H256, session_public: Public, session_secret_share: Secret, message_hash: H256) -> Result<(), Error> { - let key_share = match self.key_share.as_ref() { - None => return Err(Error::InvalidMessage), - Some(key_share) => key_share, - }; - - let key_version = key_share.version(version)?.hash.clone(); - let signing_job = SchnorrSigningJob::new_on_master(self.meta.self_node_id.clone(), key_share.clone(), key_version, - session_public, session_secret_share, message_hash)?; - consensus_session.disseminate_jobs(signing_job, self.signing_transport(), false).map(|_| ()) - } -} - -impl JobTransport for SigningConsensusTransport { - type PartialJobRequest=Requester; - type PartialJobResponse=bool; - - fn send_partial_request(&self, node: &NodeId, request: Requester) -> Result<(), Error> { - let version = self.version.as_ref() - .expect("send_partial_request is called on initialized master node only; version is filled in before initialization starts on master node; qed"); - self.cluster.send(node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(SchnorrSigningConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requester: request.into(), - version: version.clone().into(), - }) - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: bool) -> Result<(), Error> { - self.cluster.send(node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(SchnorrSigningConsensusMessage { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: response, - }) - }))) - } -} - -impl JobTransport for SigningJobTransport { - type PartialJobRequest=SchnorrPartialSigningRequest; - type PartialJobResponse=SchnorrPartialSigningResponse; - - fn send_partial_request(&self, node: &NodeId, request: SchnorrPartialSigningRequest) -> Result<(), Error> { - self.cluster.send(node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrRequestPartialSignature(SchnorrRequestPartialSignature { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - request_id: request.id.into(), - message_hash: request.message_hash.into(), - nodes: request.other_nodes_ids.into_iter().map(Into::into).collect(), - }))) - } - - fn send_partial_response(&self, node: &NodeId, response: SchnorrPartialSigningResponse) -> Result<(), Error> { - self.cluster.send(node, Message::SchnorrSigning(SchnorrSigningMessage::SchnorrPartialSignature(SchnorrPartialSignature { - session: self.id.clone().into(), - sub_session: self.access_key.clone().into(), - session_nonce: self.nonce, - request_id: response.request_id.into(), - partial_signature: response.partial_signature.into(), - }))) - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::str::FromStr; - use std::collections::BTreeMap; - use ethereum_types::{Address, H256}; - use crypto::publickey::{Random, Generator, Public, Secret, public_to_address}; - use acl_storage::DummyAclStorage; - use key_server_cluster::{SessionId, Requester, SessionMeta, Error, KeyStorage}; - use key_server_cluster::cluster::tests::MessageLoop as ClusterMessageLoop; - use key_server_cluster::generation_session::tests::MessageLoop as GenerationMessageLoop; - use key_server_cluster::math; - use key_server_cluster::message::{SchnorrSigningMessage, SchnorrSigningConsensusMessage, - ConsensusMessage, ConfirmConsensusInitialization, SchnorrSigningGenerationMessage, GenerationMessage, - ConfirmInitialization, InitializeSession, SchnorrRequestPartialSignature}; - use key_server_cluster::signing_session_schnorr::{SessionImpl, SessionState, SessionParams}; - - #[derive(Debug)] - pub struct MessageLoop(pub ClusterMessageLoop); - - impl MessageLoop { - pub fn new(num_nodes: usize, threshold: usize) -> Result { - let ml = GenerationMessageLoop::new(num_nodes).init(threshold)?; - ml.0.loop_until(|| ml.0.is_empty()); // complete generation session - - Ok(MessageLoop(ml.0)) - } - - pub fn into_session(&self, at_node: usize) -> SessionImpl { - let requester = Some(Requester::Signature(crypto::publickey::sign(Random.generate().unwrap().secret(), - &SessionId::default()).unwrap())); - SessionImpl::new(SessionParams { - meta: SessionMeta { - id: SessionId::default(), - self_node_id: self.0.node(at_node), - master_node_id: self.0.node(0), - threshold: self.0.key_storage(at_node).get(&Default::default()).unwrap().unwrap().threshold, - configured_nodes_count: self.0.nodes().len(), - connected_nodes_count: self.0.nodes().len(), - }, - access_key: Random.generate().unwrap().secret().clone(), - key_share: self.0.key_storage(at_node).get(&Default::default()).unwrap(), - acl_storage: Arc::new(DummyAclStorage::default()), - cluster: self.0.cluster(0).view().unwrap(), - nonce: 0, - }, requester).unwrap().0 - } - - pub fn init_with_version(self, key_version: Option) -> Result<(Self, Public, H256), Error> { - let message_hash = H256::random(); - let requester = Random.generate().unwrap(); - let signature = crypto::publickey::sign(requester.secret(), &SessionId::default()).unwrap(); - self.0.cluster(0).client().new_schnorr_signing_session( - Default::default(), - signature.into(), - key_version, - message_hash).map(|_| (self, *requester.public(), message_hash)) - } - - pub fn init(self) -> Result<(Self, Public, H256), Error> { - let key_version = self.key_version(); - self.init_with_version(Some(key_version)) - } - - pub fn init_delegated(self) -> Result<(Self, Public, H256), Error> { - self.0.key_storage(0).remove(&Default::default()).unwrap(); - self.init_with_version(None) - } - - pub fn init_with_isolated(self) -> Result<(Self, Public, H256), Error> { - self.0.isolate(1); - self.init() - } - - pub fn init_without_share(self) -> Result<(Self, Public, H256), Error> { - let key_version = self.key_version(); - self.0.key_storage(0).remove(&Default::default()).unwrap(); - self.init_with_version(Some(key_version)) - } - - pub fn session_at(&self, idx: usize) -> Arc { - self.0.sessions(idx).schnorr_signing_sessions.first().unwrap() - } - - pub fn ensure_completed(&self) { - self.0.loop_until(|| self.0.is_empty()); - assert!(self.session_at(0).wait().is_ok()); - } - - pub fn key_version(&self) -> H256 { - self.0.key_storage(0).get(&Default::default()) - .unwrap().unwrap().versions.iter().last().unwrap().hash - } - } - - #[test] - fn schnorr_complete_gen_sign_session() { - let test_cases = [(0, 1), (0, 5), (2, 5), (3, 5)]; - for &(threshold, num_nodes) in &test_cases { - let (ml, _, message) = MessageLoop::new(num_nodes, threshold).unwrap().init().unwrap(); - ml.0.loop_until(|| ml.0.is_empty()); - - let signer_public = ml.0.key_storage(0).get(&Default::default()).unwrap().unwrap().public; - let signature = ml.session_at(0).wait().unwrap(); - assert!(math::verify_schnorr_signature(&signer_public, &signature, &message).unwrap()); - } - } - - #[test] - fn schnorr_constructs_in_cluster_of_single_node() { - MessageLoop::new(1, 0).unwrap().init().unwrap(); - } - - #[test] - fn schnorr_fails_to_initialize_if_does_not_have_a_share() { - assert!(MessageLoop::new(2, 1).unwrap().init_without_share().is_err()); - } - - #[test] - fn schnorr_fails_to_initialize_if_threshold_is_wrong() { - let mut ml = MessageLoop::new(3, 2).unwrap(); - ml.0.exclude(2); - assert_eq!(ml.init().unwrap_err(), Error::ConsensusUnreachable); - } - - #[test] - fn schnorr_fails_to_initialize_when_already_initialized() { - let (ml, _, _) = MessageLoop::new(1, 0).unwrap().init().unwrap(); - assert_eq!(ml.session_at(0).initialize(ml.key_version(), H256::from_low_u64_be(777)), - Err(Error::InvalidStateForRequest)); - } - - #[test] - fn schnorr_does_not_fail_when_consensus_message_received_after_consensus_established() { - let (ml, _, _) = MessageLoop::new(3, 1).unwrap().init().unwrap(); - - // consensus is established - let session = ml.session_at(0); - ml.0.loop_until(|| session.state() == SessionState::SessionKeyGeneration); - - // but 3rd node continues to send its messages - // this should not fail session - let consensus_group = session.data.lock().consensus_session.select_consensus_group().unwrap().clone(); - let mut had_3rd_message = false; - while let Some((from, to, message)) = ml.0.take_message() { - if !consensus_group.contains(&from) { - had_3rd_message = true; - ml.0.process_message(from, to, message); - } - } - assert!(had_3rd_message); - } - - #[test] - fn schnorr_fails_when_consensus_message_is_received_when_not_initialized() { - let ml = MessageLoop::new(3, 1).unwrap(); - let session = ml.into_session(0); - assert_eq!(session.on_consensus_message(&ml.0.node(1), &SchnorrSigningConsensusMessage { - session: SessionId::default().into(), - sub_session: session.core.access_key.clone().into(), - session_nonce: 0, - message: ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - }), - }), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn schnorr_fails_when_generation_message_is_received_when_not_initialized() { - let ml = MessageLoop::new(3, 1).unwrap(); - let session = ml.into_session(0); - assert_eq!(session.on_generation_message(&ml.0.node(1), &SchnorrSigningGenerationMessage { - session: SessionId::default().into(), - sub_session: session.core.access_key.clone().into(), - session_nonce: 0, - message: GenerationMessage::ConfirmInitialization(ConfirmInitialization { - session: SessionId::default().into(), - session_nonce: 0, - derived_point: Public::default().into(), - }), - }), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn schnorr_fails_when_generation_sesson_is_initialized_by_slave_node() { - let (ml, _, _) = MessageLoop::new(3, 1).unwrap().init().unwrap(); - let session = ml.session_at(0); - ml.0.loop_until(|| session.state() == SessionState::SessionKeyGeneration); - - let slave2_id = ml.0.node(2); - let slave1_session = ml.session_at(1); - - assert_eq!(slave1_session.on_generation_message(&slave2_id, &SchnorrSigningGenerationMessage { - session: SessionId::default().into(), - sub_session: session.core.access_key.clone().into(), - session_nonce: 0, - message: GenerationMessage::InitializeSession(InitializeSession { - session: SessionId::default().into(), - session_nonce: 0, - origin: None, - author: Address::zero().into(), - nodes: BTreeMap::new(), - is_zero: false, - threshold: 1, - derived_point: Public::default().into(), - }) - }), Err(Error::InvalidMessage)); - } - - #[test] - fn schnorr_fails_when_signature_requested_when_not_initialized() { - let ml = MessageLoop::new(3, 1).unwrap(); - let session = ml.into_session(1); - assert_eq!(session.on_partial_signature_requested(&ml.0.node(0), &SchnorrRequestPartialSignature { - session: SessionId::default().into(), - sub_session: session.core.access_key.clone().into(), - session_nonce: 0, - request_id: Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap().into(), - message_hash: H256::zero().into(), - nodes: Default::default(), - }), Err(Error::InvalidStateForRequest)); - } - - #[test] - fn schnorr_fails_when_signature_requested_by_slave_node() { - let ml = MessageLoop::new(3, 1).unwrap(); - let session = ml.into_session(0); - assert_eq!(session.on_partial_signature_requested(&ml.0.node(1), &SchnorrRequestPartialSignature { - session: SessionId::default().into(), - sub_session: session.core.access_key.clone().into(), - session_nonce: 0, - request_id: Secret::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap().into(), - message_hash: H256::zero().into(), - nodes: Default::default(), - }), Err(Error::InvalidMessage)); - } - - #[test] - fn schnorr_failed_signing_session() { - let (ml, requester, _) = MessageLoop::new(3, 1).unwrap().init().unwrap(); - - // we need at least 2-of-3 nodes to agree to reach consensus - // let's say 2 of 3 nodes disagee - ml.0.acl_storage(1).prohibit(public_to_address(&requester), SessionId::default()); - ml.0.acl_storage(2).prohibit(public_to_address(&requester), SessionId::default()); - - // then consensus is unreachable - ml.0.loop_until(|| ml.0.is_empty()); - assert_eq!(ml.session_at(0).wait().unwrap_err(), Error::ConsensusUnreachable); - } - - #[test] - fn schnorr_complete_signing_session_with_single_node_failing() { - let (ml, requester, _) = MessageLoop::new(3, 1).unwrap().init().unwrap(); - - // we need at least 2-of-3 nodes to agree to reach consensus - // let's say 1 of 3 nodes disagee - ml.0.acl_storage(1).prohibit(public_to_address(&requester), SessionId::default()); - - // then consensus reachable, but single node will disagree - ml.ensure_completed(); - } - - #[test] - fn schnorr_complete_signing_session_with_acl_check_failed_on_master() { - let (ml, requester, _) = MessageLoop::new(3, 1).unwrap().init().unwrap(); - - // we need at least 2-of-3 nodes to agree to reach consensus - // let's say 1 of 3 nodes disagee - ml.0.acl_storage(0).prohibit(public_to_address(&requester), SessionId::default()); - - // then consensus reachable, but single node will disagree - ml.ensure_completed(); - } - - #[test] - fn schnorr_signing_message_fails_when_nonce_is_wrong() { - let ml = MessageLoop::new(3, 1).unwrap(); - let session = ml.into_session(1); - let msg = SchnorrSigningMessage::SchnorrSigningGenerationMessage(SchnorrSigningGenerationMessage { - session: SessionId::default().into(), - sub_session: session.core.access_key.clone().into(), - session_nonce: 10, - message: GenerationMessage::ConfirmInitialization(ConfirmInitialization { - session: SessionId::default().into(), - session_nonce: 0, - derived_point: Public::default().into(), - }), - }); - assert_eq!(session.process_message(&ml.0.node(1), &msg), Err(Error::ReplayProtection)); - } - - #[test] - fn schnorr_signing_works_when_delegated_to_other_node() { - let (ml, _, _) = MessageLoop::new(3, 1).unwrap().init_delegated().unwrap(); - ml.ensure_completed(); - } - - #[test] - fn schnorr_signing_works_when_share_owners_are_isolated() { - let (ml, _, _) = MessageLoop::new(3, 1).unwrap().init_with_isolated().unwrap(); - ml.ensure_completed(); - } -} diff --git a/secret-store/src/key_server_cluster/cluster.rs b/secret-store/src/key_server_cluster/cluster.rs deleted file mode 100644 index 18f83075323..00000000000 --- a/secret-store/src/key_server_cluster/cluster.rs +++ /dev/null @@ -1,1262 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeMap, BTreeSet}; -use parking_lot::RwLock; -use crypto::publickey::{Public, Signature, Random, Generator}; -use ethereum_types::{Address, H256}; -use parity_runtime::Executor; -use blockchain::SigningKeyPair; -use key_server_cluster::{Error, NodeId, SessionId, Requester, AclStorage, KeyStorage, KeyServerSet}; -use key_server_cluster::cluster_sessions::{WaitableSession, ClusterSession, AdminSession, ClusterSessions, - SessionIdWithSubSession, ClusterSessionsContainer, SERVERS_SET_CHANGE_SESSION_ID, create_cluster_view, - AdminSessionCreationData, ClusterSessionsListener}; -use key_server_cluster::cluster_sessions_creator::ClusterSessionCreator; -use key_server_cluster::cluster_connections::{ConnectionProvider, ConnectionManager}; -use key_server_cluster::cluster_connections_net::{NetConnectionsManager, - NetConnectionsContainer, NetConnectionsManagerConfig}; -use key_server_cluster::cluster_message_processor::{MessageProcessor, SessionsMessageProcessor}; -use key_server_cluster::message::Message; -use key_server_cluster::generation_session::{SessionImpl as GenerationSession}; -use key_server_cluster::decryption_session::{SessionImpl as DecryptionSession}; -use key_server_cluster::encryption_session::{SessionImpl as EncryptionSession}; -use key_server_cluster::signing_session_ecdsa::{SessionImpl as EcdsaSigningSession}; -use key_server_cluster::signing_session_schnorr::{SessionImpl as SchnorrSigningSession}; -use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSession, - IsolatedSessionTransport as KeyVersionNegotiationSessionTransport, ContinueAction}; -use key_server_cluster::connection_trigger::{ConnectionTrigger, - SimpleConnectionTrigger, ServersSetChangeSessionCreatorConnector}; -use key_server_cluster::connection_trigger_with_migration::ConnectionTriggerWithMigration; - -#[cfg(test)] -use key_server_cluster::cluster_connections::tests::{MessagesQueue, TestConnections, new_test_connections}; - -/// Cluster interface for external clients. -pub trait ClusterClient: Send + Sync { - /// Start new generation session. - fn new_generation_session( - &self, - session_id: SessionId, - origin: Option
, - author: Address, - threshold: usize, - ) -> Result, Error>; - /// Start new encryption session. - fn new_encryption_session( - &self, - session_id: SessionId, - author: Requester, - common_point: Public, - encrypted_point: Public, - ) -> Result, Error>; - /// Start new decryption session. - fn new_decryption_session( - &self, - session_id: SessionId, - origin: Option
, - requester: Requester, - version: Option, - is_shadow_decryption: bool, - is_broadcast_decryption: bool, - ) -> Result, Error>; - /// Start new Schnorr signing session. - fn new_schnorr_signing_session( - &self, - session_id: SessionId, - requester: Requester, - version: Option, - message_hash: H256, - ) -> Result, Error>; - /// Start new ECDSA session. - fn new_ecdsa_signing_session( - &self, - session_id: SessionId, - requester: Requester, - version: Option, - message_hash: H256, - ) -> Result, Error>; - /// Start new key version negotiation session. - fn new_key_version_negotiation_session( - &self, - session_id: SessionId, - ) -> Result>, Error>; - /// Start new servers set change session. - fn new_servers_set_change_session( - &self, - session_id: Option, - migration_id: Option, - new_nodes_set: BTreeSet, - old_set_signature: Signature, - new_set_signature: Signature, - ) -> Result, Error>; - - /// Listen for new generation sessions. - fn add_generation_listener(&self, listener: Arc>); - /// Listen for new decryption sessions. - fn add_decryption_listener(&self, listener: Arc>); - /// Listen for new key version negotiation sessions. - fn add_key_version_negotiation_listener(&self, listener: Arc>>); - - /// Ask node to make 'faulty' generation sessions. - #[cfg(test)] - fn make_faulty_generation_sessions(&self); - /// Get active generation session with given id. - #[cfg(test)] - fn generation_session(&self, session_id: &SessionId) -> Option>; - #[cfg(test)] - fn is_fully_connected(&self) -> bool; - /// Try connect to disconnected nodes. - #[cfg(test)] - fn connect(&self); -} - -/// Cluster access for single session participant. -pub trait Cluster: Send + Sync { - /// Broadcast message to all other nodes. - fn broadcast(&self, message: Message) -> Result<(), Error>; - /// Send message to given node. - fn send(&self, to: &NodeId, message: Message) -> Result<(), Error>; - /// Is connected to given node? - fn is_connected(&self, node: &NodeId) -> bool; - /// Get a set of connected nodes. - fn nodes(&self) -> BTreeSet; - /// Get total count of configured key server nodes (valid at the time of ClusterView creation). - fn configured_nodes_count(&self) -> usize; - /// Get total count of connected key server nodes (valid at the time of ClusterView creation). - fn connected_nodes_count(&self) -> usize; -} - -/// Cluster initialization parameters. -#[derive(Clone)] -pub struct ClusterConfiguration { - /// KeyPair this node holds. - pub self_key_pair: Arc, - /// Cluster nodes set. - pub key_server_set: Arc, - /// Reference to key storage - pub key_storage: Arc, - /// Reference to ACL storage - pub acl_storage: Arc, - /// Administrator public key. - pub admin_public: Option, - /// Do not remove sessions from container. - pub preserve_sessions: bool, -} - -/// Network cluster implementation. -pub struct ClusterCore { - /// Cluster data. - data: Arc>, -} - -/// Network cluster client interface implementation. -pub struct ClusterClientImpl { - /// Cluster data. - data: Arc>, -} - -/// Network cluster view. It is a communication channel, required in single session. -pub struct ClusterView { - configured_nodes_count: usize, - connected_nodes: BTreeSet, - connections: Arc, - self_key_pair: Arc, -} - -/// Cross-thread shareable cluster data. -pub struct ClusterData { - /// Cluster configuration. - pub config: ClusterConfiguration, - /// KeyPair this node holds. - pub self_key_pair: Arc, - /// Connections data. - pub connections: C, - /// Active sessions data. - pub sessions: Arc, - // Messages processor. - pub message_processor: Arc, - /// Link between servers set chnage session and the connections manager. - pub servers_set_change_creator_connector: Arc, -} - -/// Create new network-backed cluster. -pub fn new_network_cluster( - executor: Executor, - config: ClusterConfiguration, - net_config: NetConnectionsManagerConfig -) -> Result>, Error> { - let mut nodes = config.key_server_set.snapshot().current_set; - let is_isolated = nodes.remove(config.self_key_pair.public()).is_none(); - let connections_data = Arc::new(RwLock::new(NetConnectionsContainer { - is_isolated, - nodes, - connections: BTreeMap::new(), - })); - - let connection_trigger: Box = match net_config.auto_migrate_enabled { - false => Box::new(SimpleConnectionTrigger::with_config(&config)), - true if config.admin_public.is_none() => Box::new(ConnectionTriggerWithMigration::with_config(&config)), - true => return Err(Error::Internal( - "secret store admininstrator public key is specified with auto-migration enabled".into() - )), - }; - - let servers_set_change_creator_connector = connection_trigger.servers_set_change_creator_connector(); - let sessions = Arc::new(ClusterSessions::new(&config, servers_set_change_creator_connector.clone())); - let message_processor = Arc::new(SessionsMessageProcessor::new( - config.self_key_pair.clone(), - servers_set_change_creator_connector.clone(), - sessions.clone(), - connections_data.clone())); - - let connections = NetConnectionsManager::new( - executor, - message_processor.clone(), - connection_trigger, - connections_data, - &config, - net_config)?; - connections.start()?; - - ClusterCore::new(sessions, message_processor, connections, servers_set_change_creator_connector, config) -} - -/// Create new in-memory backed cluster -#[cfg(test)] -pub fn new_test_cluster( - messages: MessagesQueue, - config: ClusterConfiguration, -) -> Result>>, Error> { - let nodes = config.key_server_set.snapshot().current_set; - let connections = new_test_connections(messages, *config.self_key_pair.public(), nodes.keys().cloned().collect()); - - let connection_trigger = Box::new(SimpleConnectionTrigger::with_config(&config)); - let servers_set_change_creator_connector = connection_trigger.servers_set_change_creator_connector(); - let mut sessions = ClusterSessions::new(&config, servers_set_change_creator_connector.clone()); - if config.preserve_sessions { - sessions.preserve_sessions(); - } - let sessions = Arc::new(sessions); - - let message_processor = Arc::new(SessionsMessageProcessor::new( - config.self_key_pair.clone(), - servers_set_change_creator_connector.clone(), - sessions.clone(), - connections.provider(), - )); - - ClusterCore::new(sessions, message_processor, connections, servers_set_change_creator_connector, config) -} - -impl ClusterCore { - pub fn new( - sessions: Arc, - message_processor: Arc, - connections: C, - servers_set_change_creator_connector: Arc, - config: ClusterConfiguration, - ) -> Result, Error> { - Ok(Arc::new(ClusterCore { - data: Arc::new(ClusterData { - self_key_pair: config.self_key_pair.clone(), - connections, - sessions: sessions.clone(), - config, - message_processor, - servers_set_change_creator_connector - }), - })) - } - - /// Create new client interface. - pub fn client(&self) -> Arc { - Arc::new(ClusterClientImpl::new(self.data.clone())) - } - - /// Run cluster. - pub fn run(&self) -> Result<(), Error> { - self.data.connections.connect(); - Ok(()) - } - - #[cfg(test)] - pub fn view(&self) -> Result, Error> { - let connections = self.data.connections.provider(); - let mut connected_nodes = connections.connected_nodes()?; - let disconnected_nodes = connections.disconnected_nodes(); - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let connected_nodes_count = connected_nodes.len(); - let disconnected_nodes_count = disconnected_nodes.len(); - Ok(Arc::new(ClusterView::new( - self.data.self_key_pair.clone(), - connections, - connected_nodes, - connected_nodes_count + disconnected_nodes_count))) - } -} - -impl ClusterView { - pub fn new( - self_key_pair: Arc, - connections: Arc, - nodes: BTreeSet, - configured_nodes_count: usize - ) -> Self { - ClusterView { - configured_nodes_count: configured_nodes_count, - connected_nodes: nodes, - connections, - self_key_pair, - } - } -} - -impl Cluster for ClusterView { - fn broadcast(&self, message: Message) -> Result<(), Error> { - for node in self.connected_nodes.iter().filter(|n| *n != self.self_key_pair.public()) { - trace!(target: "secretstore_net", "{}: sent message {} to {}", self.self_key_pair.public(), message, node); - let connection = self.connections.connection(node).ok_or(Error::NodeDisconnected)?; - connection.send_message(message.clone()); - } - Ok(()) - } - - fn send(&self, to: &NodeId, message: Message) -> Result<(), Error> { - trace!(target: "secretstore_net", "{}: sent message {} to {}", self.self_key_pair.public(), message, to); - let connection = self.connections.connection(to).ok_or(Error::NodeDisconnected)?; - connection.send_message(message); - Ok(()) - } - - fn is_connected(&self, node: &NodeId) -> bool { - self.connected_nodes.contains(node) - } - - fn nodes(&self) -> BTreeSet { - self.connected_nodes.clone() - } - - fn configured_nodes_count(&self) -> usize { - self.configured_nodes_count - } - - fn connected_nodes_count(&self) -> usize { - self.connected_nodes.len() - } -} - -impl ClusterClientImpl { - pub fn new(data: Arc>) -> Self { - ClusterClientImpl { - data: data, - } - } - - fn create_key_version_negotiation_session( - &self, - session_id: SessionId, - ) -> Result>, Error> { - let mut connected_nodes = self.data.connections.provider().connected_nodes()?; - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let access_key = Random.generate()?.secret().clone(); - let session_id = SessionIdWithSubSession::new(session_id, access_key); - let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), false)?; - let session = self.data.sessions.negotiation_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id.clone(), None, false, None)?; - match session.session.initialize(connected_nodes) { - Ok(()) => Ok(session), - Err(error) => { - self.data.sessions.negotiation_sessions.remove(&session.session.id()); - Err(error) - } - } - } -} - -impl ClusterClient for ClusterClientImpl { - fn new_generation_session( - &self, - session_id: SessionId, - origin: Option
, - author: Address, - threshold: usize, - ) -> Result, Error> { - let mut connected_nodes = self.data.connections.provider().connected_nodes()?; - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), true)?; - let session = self.data.sessions.generation_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id, None, false, None)?; - process_initialization_result( - session.session.initialize(origin, author, false, threshold, connected_nodes.into()), - session, &self.data.sessions.generation_sessions) - } - - fn new_encryption_session( - &self, - session_id: SessionId, - requester: Requester, - common_point: Public, - encrypted_point: Public, - ) -> Result, Error> { - let mut connected_nodes = self.data.connections.provider().connected_nodes()?; - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), true)?; - let session = self.data.sessions.encryption_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id, None, false, None)?; - process_initialization_result( - session.session.initialize(requester, common_point, encrypted_point), - session, &self.data.sessions.encryption_sessions) - } - - fn new_decryption_session( - &self, - session_id: SessionId, - origin: Option
, - requester: Requester, - version: Option, - is_shadow_decryption: bool, - is_broadcast_decryption: bool, - ) -> Result, Error> { - let mut connected_nodes = self.data.connections.provider().connected_nodes()?; - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let access_key = Random.generate()?.secret().clone(); - let session_id = SessionIdWithSubSession::new(session_id, access_key); - let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), false)?; - let session = self.data.sessions.decryption_sessions.insert(cluster, self.data.self_key_pair.public().clone(), - session_id.clone(), None, false, Some(requester))?; - - let initialization_result = match version { - Some(version) => session.session.initialize(origin, version, is_shadow_decryption, is_broadcast_decryption), - None => { - self.create_key_version_negotiation_session(session_id.id.clone()) - .map(|version_session| { - let continue_action = ContinueAction::Decrypt( - session.session.clone(), - origin, - is_shadow_decryption, - is_broadcast_decryption, - ); - version_session.session.set_continue_action(continue_action); - self.data.message_processor.try_continue_session(Some(version_session.session)); - }) - }, - }; - - process_initialization_result( - initialization_result, - session, &self.data.sessions.decryption_sessions) - } - - fn new_schnorr_signing_session( - &self, - session_id: SessionId, - requester: Requester, - version: Option, - message_hash: H256, - ) -> Result, Error> { - let mut connected_nodes = self.data.connections.provider().connected_nodes()?; - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let access_key = Random.generate()?.secret().clone(); - let session_id = SessionIdWithSubSession::new(session_id, access_key); - let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), false)?; - let session = self.data.sessions.schnorr_signing_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id.clone(), None, false, Some(requester))?; - - let initialization_result = match version { - Some(version) => session.session.initialize(version, message_hash), - None => { - self.create_key_version_negotiation_session(session_id.id.clone()) - .map(|version_session| { - let continue_action = ContinueAction::SchnorrSign(session.session.clone(), message_hash); - version_session.session.set_continue_action(continue_action); - self.data.message_processor.try_continue_session(Some(version_session.session)); - }) - }, - }; - - process_initialization_result( - initialization_result, - session, &self.data.sessions.schnorr_signing_sessions) - } - - fn new_ecdsa_signing_session( - &self, - session_id: SessionId, - requester: Requester, - version: Option, - message_hash: H256, - ) -> Result, Error> { - let mut connected_nodes = self.data.connections.provider().connected_nodes()?; - connected_nodes.insert(self.data.self_key_pair.public().clone()); - - let access_key = Random.generate()?.secret().clone(); - let session_id = SessionIdWithSubSession::new(session_id, access_key); - let cluster = create_cluster_view(self.data.self_key_pair.clone(), self.data.connections.provider(), false)?; - let session = self.data.sessions.ecdsa_signing_sessions.insert(cluster, self.data.self_key_pair.public().clone(), session_id.clone(), None, false, Some(requester))?; - - let initialization_result = match version { - Some(version) => session.session.initialize(version, message_hash), - None => { - self.create_key_version_negotiation_session(session_id.id.clone()) - .map(|version_session| { - let continue_action = ContinueAction::EcdsaSign(session.session.clone(), message_hash); - version_session.session.set_continue_action(continue_action); - self.data.message_processor.try_continue_session(Some(version_session.session)); - }) - }, - }; - - process_initialization_result( - initialization_result, - session, &self.data.sessions.ecdsa_signing_sessions) - } - - fn new_key_version_negotiation_session( - &self, - session_id: SessionId, - ) -> Result>, Error> { - self.create_key_version_negotiation_session(session_id) - } - - fn new_servers_set_change_session( - &self, - session_id: Option, - migration_id: Option, - new_nodes_set: BTreeSet, - old_set_signature: Signature, - new_set_signature: Signature, - ) -> Result, Error> { - new_servers_set_change_session( - self.data.self_key_pair.clone(), - &self.data.sessions, - self.data.connections.provider(), - self.data.servers_set_change_creator_connector.clone(), - ServersSetChangeParams { - session_id, - migration_id, - new_nodes_set, - old_set_signature, - new_set_signature, - }) - } - - fn add_generation_listener(&self, listener: Arc>) { - self.data.sessions.generation_sessions.add_listener(listener); - } - - fn add_decryption_listener(&self, listener: Arc>) { - self.data.sessions.decryption_sessions.add_listener(listener); - } - - fn add_key_version_negotiation_listener(&self, listener: Arc>>) { - self.data.sessions.negotiation_sessions.add_listener(listener); - } - - #[cfg(test)] - fn make_faulty_generation_sessions(&self) { - self.data.sessions.make_faulty_generation_sessions(); - } - - #[cfg(test)] - fn generation_session(&self, session_id: &SessionId) -> Option> { - self.data.sessions.generation_sessions.get(session_id, false) - } - - #[cfg(test)] - fn is_fully_connected(&self) -> bool { - self.data.connections.provider().disconnected_nodes().is_empty() - } - - #[cfg(test)] - fn connect(&self) { - self.data.connections.connect() - } -} - -pub struct ServersSetChangeParams { - pub session_id: Option, - pub migration_id: Option, - pub new_nodes_set: BTreeSet, - pub old_set_signature: Signature, - pub new_set_signature: Signature, -} - -pub fn new_servers_set_change_session( - self_key_pair: Arc, - sessions: &ClusterSessions, - connections: Arc, - servers_set_change_creator_connector: Arc, - params: ServersSetChangeParams, -) -> Result, Error> { - let session_id = match params.session_id { - Some(session_id) if session_id == *SERVERS_SET_CHANGE_SESSION_ID => session_id, - Some(_) => return Err(Error::InvalidMessage), - None => *SERVERS_SET_CHANGE_SESSION_ID, - }; - - let cluster = create_cluster_view(self_key_pair.clone(), connections, true)?; - let creation_data = AdminSessionCreationData::ServersSetChange(params.migration_id, params.new_nodes_set.clone()); - let session = sessions.admin_sessions - .insert(cluster, *self_key_pair.public(), session_id, None, true, Some(creation_data))?; - let initialization_result = session.session.as_servers_set_change().expect("servers set change session is created; qed") - .initialize(params.new_nodes_set, params.old_set_signature, params.new_set_signature); - - if initialization_result.is_ok() { - servers_set_change_creator_connector.set_key_servers_set_change_session(session.session.clone()); - } - - process_initialization_result( - initialization_result, - session, &sessions.admin_sessions) -} - -fn process_initialization_result( - result: Result<(), Error>, - session: WaitableSession, - sessions: &ClusterSessionsContainer -) -> Result, Error> - where - S: ClusterSession, - SC: ClusterSessionCreator -{ - match result { - Ok(()) if session.session.is_finished() => { - sessions.remove(&session.session.id()); - Ok(session) - }, - Ok(()) => Ok(session), - Err(error) => { - sessions.remove(&session.session.id()); - Err(error) - }, - } -} - -#[cfg(test)] -pub mod tests { - use std::sync::Arc; - use std::sync::atomic::{AtomicUsize, Ordering}; - use std::collections::{BTreeMap, BTreeSet, VecDeque}; - use futures::Future; - use parking_lot::{Mutex, RwLock}; - use ethereum_types::{Address, H256}; - use crypto::publickey::{Random, Generator, Public, Signature, sign}; - use blockchain::SigningKeyPair; - use key_server_cluster::{NodeId, SessionId, Requester, Error, DummyAclStorage, DummyKeyStorage, - MapKeyServerSet, PlainNodeKeyPair}; - use key_server_cluster::message::Message; - use key_server_cluster::cluster::{new_test_cluster, Cluster, ClusterCore, ClusterConfiguration, ClusterClient}; - use key_server_cluster::cluster_connections::ConnectionManager; - use key_server_cluster::cluster_connections::tests::{MessagesQueue, TestConnections}; - use key_server_cluster::cluster_sessions::{WaitableSession, ClusterSession, ClusterSessions, AdminSession, - ClusterSessionsListener}; - use key_server_cluster::generation_session::{SessionImpl as GenerationSession, - SessionState as GenerationSessionState}; - use key_server_cluster::decryption_session::{SessionImpl as DecryptionSession}; - use key_server_cluster::encryption_session::{SessionImpl as EncryptionSession}; - use key_server_cluster::signing_session_ecdsa::{SessionImpl as EcdsaSigningSession}; - use key_server_cluster::signing_session_schnorr::{SessionImpl as SchnorrSigningSession}; - use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSession, - IsolatedSessionTransport as KeyVersionNegotiationSessionTransport}; - - #[derive(Default)] - pub struct DummyClusterClient { - pub generation_requests_count: AtomicUsize, - } - - #[derive(Debug)] - pub struct DummyCluster { - id: NodeId, - data: RwLock, - } - - #[derive(Debug, Default)] - struct DummyClusterData { - nodes: BTreeSet, - messages: VecDeque<(NodeId, Message)>, - } - - impl ClusterClient for DummyClusterClient { - fn new_generation_session( - &self, - _session_id: SessionId, - _origin: Option
, - _author: Address, - _threshold: usize, - ) -> Result, Error> { - self.generation_requests_count.fetch_add(1, Ordering::Relaxed); - Err(Error::Internal("test-error".into())) - } - fn new_encryption_session( - &self, - _session_id: SessionId, - _requester: Requester, - _common_point: Public, - _encrypted_point: Public, - ) -> Result, Error> { - unimplemented!("test-only") - } - fn new_decryption_session( - &self, - _session_id: SessionId, - _origin: Option
, - _requester: Requester, - _version: Option, - _is_shadow_decryption: bool, - _is_broadcast_session: bool, - ) -> Result, Error> { - unimplemented!("test-only") - } - fn new_schnorr_signing_session( - &self, - _session_id: SessionId, - _requester: Requester, - _version: Option, - _message_hash: H256, - ) -> Result, Error> { - unimplemented!("test-only") - } - fn new_ecdsa_signing_session( - &self, - _session_id: SessionId, - _requester: Requester, - _version: Option, - _message_hash: H256, - ) -> Result, Error> { - unimplemented!("test-only") - } - - fn new_key_version_negotiation_session( - &self, - _session_id: SessionId, - ) -> Result>, Error> { - unimplemented!("test-only") - } - fn new_servers_set_change_session( - &self, - _session_id: Option, - _migration_id: Option, - _new_nodes_set: BTreeSet, - _old_set_signature: Signature, - _new_set_signature: Signature, - ) -> Result, Error> { - unimplemented!("test-only") - } - - fn add_generation_listener(&self, _listener: Arc>) {} - fn add_decryption_listener(&self, _listener: Arc>) {} - fn add_key_version_negotiation_listener(&self, _listener: Arc>>) {} - - fn make_faulty_generation_sessions(&self) { unimplemented!("test-only") } - fn generation_session(&self, _session_id: &SessionId) -> Option> { unimplemented!("test-only") } - fn is_fully_connected(&self) -> bool { true } - fn connect(&self) {} - } - - impl DummyCluster { - pub fn new(id: NodeId) -> Self { - DummyCluster { - id: id, - data: RwLock::new(DummyClusterData::default()) - } - } - - pub fn node(&self) -> NodeId { - self.id.clone() - } - - pub fn add_node(&self, node: NodeId) { - self.data.write().nodes.insert(node); - } - - pub fn add_nodes>(&self, nodes: I) { - self.data.write().nodes.extend(nodes) - } - - pub fn remove_node(&self, node: &NodeId) { - self.data.write().nodes.remove(node); - } - - pub fn take_message(&self) -> Option<(NodeId, Message)> { - self.data.write().messages.pop_front() - } - } - - impl Cluster for DummyCluster { - fn broadcast(&self, message: Message) -> Result<(), Error> { - let mut data = self.data.write(); - let all_nodes: Vec<_> = data.nodes.iter().cloned().filter(|n| n != &self.id).collect(); - for node in all_nodes { - data.messages.push_back((node, message.clone())); - } - Ok(()) - } - - fn send(&self, to: &NodeId, message: Message) -> Result<(), Error> { - debug_assert!(&self.id != to); - self.data.write().messages.push_back((to.clone(), message)); - Ok(()) - } - - fn is_connected(&self, node: &NodeId) -> bool { - let data = self.data.read(); - &self.id == node || data.nodes.contains(node) - } - - fn nodes(&self) -> BTreeSet { - self.data.read().nodes.iter().cloned().collect() - } - - fn configured_nodes_count(&self) -> usize { - self.data.read().nodes.len() - } - - fn connected_nodes_count(&self) -> usize { - self.data.read().nodes.len() - } - } - - /// Test message loop. - pub struct MessageLoop { - messages: MessagesQueue, - preserve_sessions: bool, - key_pairs_map: BTreeMap>, - acl_storages_map: BTreeMap>, - key_storages_map: BTreeMap>, - clusters_map: BTreeMap>>>, - } - - impl ::std::fmt::Debug for MessageLoop { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - write!(f, "MessageLoop({})", self.clusters_map.len()) - } - } - - impl MessageLoop { - /// Returns set of all nodes ids. - pub fn nodes(&self) -> BTreeSet { - self.clusters_map.keys().cloned().collect() - } - - /// Returns nodes id by its index. - pub fn node(&self, idx: usize) -> NodeId { - *self.clusters_map.keys().nth(idx).unwrap() - } - - /// Returns key pair of the node by its idx. - pub fn node_key_pair(&self, idx: usize) -> &Arc { - self.key_pairs_map.values().nth(idx).unwrap() - } - - /// Get cluster reference by its index. - pub fn cluster(&self, idx: usize) -> &Arc>> { - self.clusters_map.values().nth(idx).unwrap() - } - - /// Get keys storage reference by its index. - pub fn key_storage(&self, idx: usize) -> &Arc { - self.key_storages_map.values().nth(idx).unwrap() - } - - /// Get keys storage reference by node id. - pub fn key_storage_of(&self, node: &NodeId) -> &Arc { - &self.key_storages_map[node] - } - - /// Replace key storage of the node by its id. - pub fn replace_key_storage_of(&mut self, node: &NodeId, key_storage: Arc) { - *self.key_storages_map.get_mut(node).unwrap() = key_storage; - } - - /// Get ACL storage reference by its index. - pub fn acl_storage(&self, idx: usize) -> &Arc { - self.acl_storages_map.values().nth(idx).unwrap() - } - - /// Get sessions container reference by its index. - pub fn sessions(&self, idx: usize) -> &Arc { - &self.cluster(idx).data.sessions - } - - /// Get sessions container reference by node id. - pub fn sessions_of(&self, node: &NodeId) -> &Arc { - &self.clusters_map[node].data.sessions - } - - /// Isolate node from others. - pub fn isolate(&self, idx: usize) { - let node = self.node(idx); - for (i, cluster) in self.clusters_map.values().enumerate() { - if i == idx { - cluster.data.connections.isolate(); - } else { - cluster.data.connections.disconnect(node); - } - } - } - - /// Exclude node from cluster. - pub fn exclude(&mut self, idx: usize) { - let node = self.node(idx); - for (i, cluster) in self.clusters_map.values().enumerate() { - if i != idx { - cluster.data.connections.exclude(node); - } - } - self.key_storages_map.remove(&node); - self.acl_storages_map.remove(&node); - self.key_pairs_map.remove(&node); - self.clusters_map.remove(&node); - } - - /// Include new node to the cluster. - pub fn include(&mut self, node_key_pair: Arc) -> usize { - let key_storage = Arc::new(DummyKeyStorage::default()); - let acl_storage = Arc::new(DummyAclStorage::default()); - let cluster_params = ClusterConfiguration { - self_key_pair: node_key_pair.clone(), - key_server_set: Arc::new(MapKeyServerSet::new(false, self.nodes().iter() - .chain(::std::iter::once(node_key_pair.public())) - .map(|n| (*n, format!("127.0.0.1:{}", 13).parse().unwrap())) - .collect())), - key_storage: key_storage.clone(), - acl_storage: acl_storage.clone(), - admin_public: None, - preserve_sessions: self.preserve_sessions, - }; - let cluster = new_test_cluster(self.messages.clone(), cluster_params).unwrap(); - - for cluster in self.clusters_map.values(){ - cluster.data.connections.include(node_key_pair.public().clone()); - } - self.acl_storages_map.insert(*node_key_pair.public(), acl_storage); - self.key_storages_map.insert(*node_key_pair.public(), key_storage); - self.clusters_map.insert(*node_key_pair.public(), cluster); - self.key_pairs_map.insert(*node_key_pair.public(), node_key_pair.clone()); - self.clusters_map.keys().position(|k| k == node_key_pair.public()).unwrap() - } - - /// Is empty message queue? - pub fn is_empty(&self) -> bool { - self.messages.lock().is_empty() - } - - /// Takes next message from the queue. - pub fn take_message(&self) -> Option<(NodeId, NodeId, Message)> { - self.messages.lock().pop_front() - } - - /// Process single message. - pub fn process_message(&self, from: NodeId, to: NodeId, message: Message) { - let cluster_data = &self.clusters_map[&to].data; - let connection = cluster_data.connections.provider().connection(&from).unwrap(); - cluster_data.message_processor.process_connection_message(connection, message); - } - - /// Take next message and process it. - pub fn take_and_process_message(&self) -> bool { - let (from, to, message) = match self.take_message() { - Some((from, to, message)) => (from, to, message), - None => return false, - }; - - self.process_message(from, to, message); - true - } - - /// Loops until `predicate` returns `true` or there are no messages in the queue. - pub fn loop_until(&self, predicate: F) where F: Fn() -> bool { - while !predicate() { - if !self.take_and_process_message() { - panic!("message queue is empty but goal is not achieved"); - } - } - } - } - - pub fn make_clusters(num_nodes: usize) -> MessageLoop { - do_make_clusters(num_nodes, false) - } - - pub fn make_clusters_and_preserve_sessions(num_nodes: usize) -> MessageLoop { - do_make_clusters(num_nodes, true) - } - - fn do_make_clusters(num_nodes: usize, preserve_sessions: bool) -> MessageLoop { - let ports_begin = 0; - let messages = Arc::new(Mutex::new(VecDeque::new())); - let key_pairs: Vec<_> = (0..num_nodes) - .map(|_| Arc::new(PlainNodeKeyPair::new(Random.generate().unwrap()))).collect(); - let key_storages: Vec<_> = (0..num_nodes).map(|_| Arc::new(DummyKeyStorage::default())).collect(); - let acl_storages: Vec<_> = (0..num_nodes).map(|_| Arc::new(DummyAclStorage::default())).collect(); - let cluster_params: Vec<_> = (0..num_nodes).map(|i| ClusterConfiguration { - self_key_pair: key_pairs[i].clone(), - key_server_set: Arc::new(MapKeyServerSet::new(false, key_pairs.iter().enumerate() - .map(|(j, kp)| (*kp.public(), format!("127.0.0.1:{}", ports_begin + j as u16).parse().unwrap())) - .collect())), - key_storage: key_storages[i].clone(), - acl_storage: acl_storages[i].clone(), - admin_public: None, - preserve_sessions, - }).collect(); - let clusters: Vec<_> = cluster_params.into_iter() - .map(|params| new_test_cluster(messages.clone(), params).unwrap()) - .collect(); - - let clusters_map = clusters.iter().map(|c| (*c.data.config.self_key_pair.public(), c.clone())).collect(); - let key_pairs_map = key_pairs.into_iter().map(|kp| (*kp.public(), kp)).collect(); - let key_storages_map = clusters.iter().zip(key_storages.into_iter()) - .map(|(c, ks)| (*c.data.config.self_key_pair.public(), ks)).collect(); - let acl_storages_map = clusters.iter().zip(acl_storages.into_iter()) - .map(|(c, acls)| (*c.data.config.self_key_pair.public(), acls)).collect(); - MessageLoop { preserve_sessions, messages, key_pairs_map, acl_storages_map, key_storages_map, clusters_map } - } - - #[test] - fn cluster_wont_start_generation_session_if_not_fully_connected() { - let ml = make_clusters(3); - ml.cluster(0).data.connections.disconnect(*ml.cluster(0).data.self_key_pair.public()); - match ml.cluster(0).client().new_generation_session(SessionId::default(), Default::default(), Default::default(), 1) { - Err(Error::NodeDisconnected) => (), - Err(e) => panic!("unexpected error {:?}", e), - _ => panic!("unexpected success"), - } - } - - #[test] - fn error_in_generation_session_broadcasted_to_all_other_nodes() { - let _ = ::env_logger::try_init(); - let ml = make_clusters(3); - - // ask one of nodes to produce faulty generation sessions - ml.cluster(1).client().make_faulty_generation_sessions(); - - // start && wait for generation session to fail - let session = ml.cluster(0).client() - .new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap().session; - ml.loop_until(|| session.joint_public_and_secret().is_some() - && ml.cluster(0).client().generation_session(&SessionId::default()).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_err()); - - // check that faulty session is either removed from all nodes, or nonexistent (already removed) - for i in 1..3 { - if let Some(session) = ml.cluster(i).client().generation_session(&SessionId::default()) { - // wait for both session completion && session removal (session completion event is fired - // before session is removed from its own container by cluster) - ml.loop_until(|| session.joint_public_and_secret().is_some() - && ml.cluster(i).client().generation_session(&SessionId::default()).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_err()); - } - } - } - - #[test] - fn generation_session_completion_signalled_if_failed_on_master() { - let _ = ::env_logger::try_init(); - let ml = make_clusters(3); - - // ask one of nodes to produce faulty generation sessions - ml.cluster(0).client().make_faulty_generation_sessions(); - - // start && wait for generation session to fail - let session = ml.cluster(0).client() - .new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap().session; - ml.loop_until(|| session.joint_public_and_secret().is_some() - && ml.cluster(0).client().generation_session(&SessionId::default()).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_err()); - - // check that faulty session is either removed from all nodes, or nonexistent (already removed) - for i in 1..3 { - if let Some(session) = ml.cluster(i).client().generation_session(&SessionId::default()) { - let session = session.clone(); - // wait for both session completion && session removal (session completion event is fired - // before session is removed from its own container by cluster) - ml.loop_until(|| session.joint_public_and_secret().is_some() - && ml.cluster(i).client().generation_session(&SessionId::default()).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_err()); - } - } - } - - #[test] - fn generation_session_is_removed_when_succeeded() { - let _ = ::env_logger::try_init(); - let ml = make_clusters(3); - - // start && wait for generation session to complete - let session = ml.cluster(0).client() - .new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap().session; - ml.loop_until(|| (session.state() == GenerationSessionState::Finished - || session.state() == GenerationSessionState::Failed) - && ml.cluster(0).client().generation_session(&SessionId::default()).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_ok()); - - // check that on non-master nodes session is either: - // already removed - // or it is removed right after completion - for i in 1..3 { - if let Some(session) = ml.cluster(i).client().generation_session(&SessionId::default()) { - // run to completion if completion message is still on the way - // AND check that it is actually removed from cluster sessions - ml.loop_until(|| (session.state() == GenerationSessionState::Finished - || session.state() == GenerationSessionState::Failed) - && ml.cluster(i).client().generation_session(&SessionId::default()).is_none()); - } - } - } - - #[test] - fn sessions_are_removed_when_initialization_fails() { - let ml = make_clusters(3); - let client = ml.cluster(0).client(); - - // generation session - { - // try to start generation session => fail in initialization - assert_eq!( - client.new_generation_session(SessionId::default(), None, Default::default(), 100).map(|_| ()), - Err(Error::NotEnoughNodesForThreshold)); - - // try to start generation session => fails in initialization - assert_eq!( - client.new_generation_session(SessionId::default(), None, Default::default(), 100).map(|_| ()), - Err(Error::NotEnoughNodesForThreshold)); - - assert!(ml.cluster(0).data.sessions.generation_sessions.is_empty()); - } - - // decryption session - { - // try to start decryption session => fails in initialization - assert_eq!( - client.new_decryption_session( - Default::default(), Default::default(), Default::default(), Some(Default::default()), false, false - ).map(|_| ()), - Err(Error::InvalidMessage)); - - // try to start generation session => fails in initialization - assert_eq!( - client.new_decryption_session( - Default::default(), Default::default(), Default::default(), Some(Default::default()), false, false - ).map(|_| ()), - Err(Error::InvalidMessage)); - - assert!(ml.cluster(0).data.sessions.decryption_sessions.is_empty()); - assert!(ml.cluster(0).data.sessions.negotiation_sessions.is_empty()); - } - } - - #[test] - fn schnorr_signing_session_completes_if_node_does_not_have_a_share() { - let _ = ::env_logger::try_init(); - let ml = make_clusters(3); - - // start && wait for generation session to complete - let session = ml.cluster(0).client(). - new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap().session; - ml.loop_until(|| (session.state() == GenerationSessionState::Finished - || session.state() == GenerationSessionState::Failed) - && ml.cluster(0).client().generation_session(&SessionId::default()).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_ok()); - - // now remove share from node2 - assert!((0..3).all(|i| ml.cluster(i).data.sessions.generation_sessions.is_empty())); - ml.cluster(2).data.config.key_storage.remove(&Default::default()).unwrap(); - - // and try to sign message with generated key - let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); - let session0 = ml.cluster(0).client() - .new_schnorr_signing_session(Default::default(), signature.into(), None, Default::default()).unwrap(); - let session = ml.cluster(0).data.sessions.schnorr_signing_sessions.first().unwrap(); - - ml.loop_until(|| session.is_finished() && (0..3).all(|i| - ml.cluster(i).data.sessions.schnorr_signing_sessions.is_empty())); - session0.into_wait_future().wait().unwrap(); - - // and try to sign message with generated key using node that has no key share - let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); - let session2 = ml.cluster(2).client() - .new_schnorr_signing_session(Default::default(), signature.into(), None, Default::default()).unwrap(); - let session = ml.cluster(2).data.sessions.schnorr_signing_sessions.first().unwrap(); - - ml.loop_until(|| session.is_finished() && (0..3).all(|i| - ml.cluster(i).data.sessions.schnorr_signing_sessions.is_empty())); - session2.into_wait_future().wait().unwrap(); - - // now remove share from node1 - ml.cluster(1).data.config.key_storage.remove(&Default::default()).unwrap(); - - // and try to sign message with generated key - let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); - let session1 = ml.cluster(0).client() - .new_schnorr_signing_session(Default::default(), signature.into(), None, Default::default()).unwrap(); - let session = ml.cluster(0).data.sessions.schnorr_signing_sessions.first().unwrap(); - - ml.loop_until(|| session.is_finished()); - session1.into_wait_future().wait().unwrap_err(); - } - - #[test] - fn ecdsa_signing_session_completes_if_node_does_not_have_a_share() { - let _ = ::env_logger::try_init(); - let ml = make_clusters(4); - - // start && wait for generation session to complete - let session = ml.cluster(0).client() - .new_generation_session(SessionId::default(), Default::default(), Default::default(), 1).unwrap().session; - ml.loop_until(|| (session.state() == GenerationSessionState::Finished - || session.state() == GenerationSessionState::Failed) - && ml.cluster(0).client().generation_session(&SessionId::default()).is_none()); - assert!(session.joint_public_and_secret().unwrap().is_ok()); - - // now remove share from node2 - assert!((0..3).all(|i| ml.cluster(i).data.sessions.generation_sessions.is_empty())); - ml.cluster(2).data.config.key_storage.remove(&Default::default()).unwrap(); - - // and try to sign message with generated key - let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); - let session0 = ml.cluster(0).client() - .new_ecdsa_signing_session(Default::default(), signature.into(), None, H256::random()).unwrap(); - let session = ml.cluster(0).data.sessions.ecdsa_signing_sessions.first().unwrap(); - - ml.loop_until(|| session.is_finished() && (0..3).all(|i| - ml.cluster(i).data.sessions.ecdsa_signing_sessions.is_empty())); - session0.into_wait_future().wait().unwrap(); - - // and try to sign message with generated key using node that has no key share - let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); - let session2 = ml.cluster(2).client() - .new_ecdsa_signing_session(Default::default(), signature.into(), None, H256::random()).unwrap(); - let session = ml.cluster(2).data.sessions.ecdsa_signing_sessions.first().unwrap(); - ml.loop_until(|| session.is_finished() && (0..3).all(|i| - ml.cluster(i).data.sessions.ecdsa_signing_sessions.is_empty())); - session2.into_wait_future().wait().unwrap(); - - // now remove share from node1 - ml.cluster(1).data.config.key_storage.remove(&Default::default()).unwrap(); - - // and try to sign message with generated key - let signature = sign(Random.generate().unwrap().secret(), &Default::default()).unwrap(); - let session1 = ml.cluster(0).client() - .new_ecdsa_signing_session(Default::default(), signature.into(), None, H256::random()).unwrap(); - let session = ml.cluster(0).data.sessions.ecdsa_signing_sessions.first().unwrap(); - ml.loop_until(|| session.is_finished()); - session1.into_wait_future().wait().unwrap_err(); - } -} diff --git a/secret-store/src/key_server_cluster/cluster_connections.rs b/secret-store/src/key_server_cluster/cluster_connections.rs deleted file mode 100644 index 6a5d417d65d..00000000000 --- a/secret-store/src/key_server_cluster/cluster_connections.rs +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use std::collections::BTreeSet; -use std::sync::Arc; -use key_server_cluster::{Error, NodeId}; -use key_server_cluster::message::Message; - -/// Connection to the single node. Provides basic information about connected node and -/// allows sending messages to this node. -pub trait Connection: Send + Sync { - /// Is this inbound connection? This only matters when both nodes are simultaneously establishing - /// two connections to each other. The agreement is that the inbound connection from the node with - /// lower NodeId is used and the other connection is closed. - fn is_inbound(&self) -> bool; - /// Returns id of the connected node. - fn node_id(&self) -> &NodeId; - /// Returns 'address' of the node to use in traces. - fn node_address(&self) -> String; - /// Send message to the connected node. - fn send_message(&self, message: Message); -} - -/// Connections manager. Responsible for keeping us connected to all required nodes. -pub trait ConnectionManager: 'static + Send + Sync { - /// Returns shared reference to connections provider. - fn provider(&self) -> Arc; - /// Try to reach all disconnected nodes immediately. This method is exposed mostly for - /// tests, where all 'nodes' are starting listening for incoming connections first and - /// only after this, they're actually start connecting to each other. - fn connect(&self); -} - -/// Connections provider. Holds all active connections and the set of nodes that we need to -/// connect to. At any moment connection could be lost and the set of connected/disconnected -/// nodes could change (at behalf of the connection manager). -/// Clone operation should be cheap (Arc). -pub trait ConnectionProvider: Send + Sync { - /// Returns the set of currently connected nodes. Error is returned when our node is - /// not a part of the cluster ('isolated' node). - fn connected_nodes(&self) -> Result, Error>; - /// Returns the set of currently disconnected nodes. - fn disconnected_nodes(&self) -> BTreeSet; - /// Returns the reference to the active node connection or None if the node is not connected. - fn connection(&self, node: &NodeId) -> Option>; -} - -#[cfg(test)] -pub mod tests { - use std::collections::{BTreeSet, VecDeque}; - use std::sync::Arc; - use std::sync::atomic::{AtomicBool, Ordering}; - use parking_lot::Mutex; - use key_server_cluster::{Error, NodeId}; - use key_server_cluster::message::Message; - use super::{ConnectionManager, Connection, ConnectionProvider}; - - /// Shared messages queue. - pub type MessagesQueue = Arc>>; - - /// Single node connections. - pub struct TestConnections { - node: NodeId, - is_isolated: AtomicBool, - connected_nodes: Mutex>, - disconnected_nodes: Mutex>, - messages: MessagesQueue, - } - - /// Single connection. - pub struct TestConnection { - from: NodeId, - to: NodeId, - messages: MessagesQueue, - } - - impl TestConnections { - pub fn isolate(&self) { - let connected_nodes = ::std::mem::replace(&mut *self.connected_nodes.lock(), Default::default()); - self.is_isolated.store(true, Ordering::Relaxed); - self.disconnected_nodes.lock().extend(connected_nodes) - } - - pub fn disconnect(&self, node: NodeId) { - self.connected_nodes.lock().remove(&node); - self.disconnected_nodes.lock().insert(node); - } - - pub fn exclude(&self, node: NodeId) { - self.connected_nodes.lock().remove(&node); - self.disconnected_nodes.lock().remove(&node); - } - - pub fn include(&self, node: NodeId) { - self.connected_nodes.lock().insert(node); - } - } - - impl ConnectionManager for Arc { - fn provider(&self) -> Arc { - self.clone() - } - - fn connect(&self) {} - } - - impl ConnectionProvider for TestConnections { - fn connected_nodes(&self) -> Result, Error> { - match self.is_isolated.load(Ordering::Relaxed) { - false => Ok(self.connected_nodes.lock().clone()), - true => Err(Error::NodeDisconnected), - } - } - - fn disconnected_nodes(&self) -> BTreeSet { - self.disconnected_nodes.lock().clone() - } - - fn connection(&self, node: &NodeId) -> Option> { - match self.connected_nodes.lock().contains(node) { - true => Some(Arc::new(TestConnection { - from: self.node, - to: *node, - messages: self.messages.clone(), - })), - false => None, - } - } - } - - impl Connection for TestConnection { - fn is_inbound(&self) -> bool { - false - } - - fn node_id(&self) -> &NodeId { - &self.to - } - - fn node_address(&self) -> String { - format!("{}", self.to) - } - - fn send_message(&self, message: Message) { - self.messages.lock().push_back((self.from, self.to, message)) - } - } - - pub fn new_test_connections( - messages: MessagesQueue, - node: NodeId, - mut nodes: BTreeSet - ) -> Arc { - let is_isolated = !nodes.remove(&node); - Arc::new(TestConnections { - node, - is_isolated: AtomicBool::new(is_isolated), - connected_nodes: Mutex::new(nodes), - disconnected_nodes: Default::default(), - messages, - }) - } -} diff --git a/secret-store/src/key_server_cluster/cluster_connections_net.rs b/secret-store/src/key_server_cluster/cluster_connections_net.rs deleted file mode 100644 index cd46b11aab3..00000000000 --- a/secret-store/src/key_server_cluster/cluster_connections_net.rs +++ /dev/null @@ -1,543 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use std::collections::{BTreeMap, BTreeSet}; -use std::collections::btree_map::Entry; -use std::io; -use std::net::{SocketAddr, IpAddr}; -use std::sync::Arc; -use std::time::{Duration, Instant}; -use futures::{future, Future, Stream}; -use parking_lot::{Mutex, RwLock}; -use tokio::net::{TcpListener, TcpStream}; -use tokio::timer::{Interval, timeout::Error as TimeoutError}; -use tokio_io::IoFuture; -use crypto::publickey::KeyPair; -use parity_runtime::Executor; -use blockchain::SigningKeyPair; -use key_server_cluster::{Error, NodeId, ClusterConfiguration}; -use key_server_cluster::cluster_connections::{ConnectionProvider, Connection, ConnectionManager}; -use key_server_cluster::connection_trigger::{Maintain, ConnectionTrigger}; -use key_server_cluster::cluster_message_processor::MessageProcessor; -use key_server_cluster::io::{DeadlineStatus, ReadMessage, SharedTcpStream, - read_encrypted_message, WriteMessage, write_encrypted_message}; -use key_server_cluster::message::{self, ClusterMessage, Message}; -use key_server_cluster::net::{accept_connection as io_accept_connection, - connect as io_connect, Connection as IoConnection}; - -/// Empty future. -pub type BoxedEmptyFuture = Box + Send>; - -/// Maintain interval (seconds). Every MAINTAIN_INTERVAL seconds node: -/// 1) checks if connected nodes are responding to KeepAlive messages -/// 2) tries to connect to disconnected nodes -/// 3) checks if enc/dec sessions are time-outed -const MAINTAIN_INTERVAL: u64 = 10; - -/// When no messages have been received from node within KEEP_ALIVE_SEND_INTERVAL seconds, -/// we must send KeepAlive message to the node to check if it still responds to messages. -const KEEP_ALIVE_SEND_INTERVAL: Duration = Duration::from_secs(30); -/// When no messages have been received from node within KEEP_ALIVE_DISCONNECT_INTERVAL seconds, -/// we must treat this node as non-responding && disconnect from it. -const KEEP_ALIVE_DISCONNECT_INTERVAL: Duration = Duration::from_secs(60); - -/// Network connection manager configuration. -pub struct NetConnectionsManagerConfig { - /// Allow connecting to 'higher' nodes. - pub allow_connecting_to_higher_nodes: bool, - /// Interface to listen to. - pub listen_address: (String, u16), - /// True if we should autostart key servers set change session when servers set changes? - /// This will only work when servers set is configured using KeyServerSet contract. - pub auto_migrate_enabled: bool, -} - -/// Network connections manager. -pub struct NetConnectionsManager { - /// Address we're listening for incoming connections. - listen_address: SocketAddr, - /// Shared cluster connections data reference. - data: Arc, -} - -/// Network connections data. Shared among NetConnectionsManager and spawned futures. -struct NetConnectionsData { - /// Allow connecting to 'higher' nodes. - allow_connecting_to_higher_nodes: bool, - /// Reference to tokio task executor. - executor: Executor, - /// Key pair of this node. - self_key_pair: Arc, - /// Network messages processor. - message_processor: Arc, - /// Connections trigger. - trigger: Mutex>, - /// Mutable connection data. - container: Arc>, -} - -/// Network connections container. This is the only mutable data of NetConnectionsManager. -/// The set of nodes is mutated by the connection trigger and the connections set is also -/// mutated by spawned futures. -pub struct NetConnectionsContainer { - /// Is this node isolated from cluster? - pub is_isolated: bool, - /// Current key servers set. - pub nodes: BTreeMap, - /// Active connections to key servers. - pub connections: BTreeMap>, -} - -/// Network connection to single key server node. -pub struct NetConnection { - executor: Executor, - /// Id of the peer node. - node_id: NodeId, - /// Address of the peer node. - node_address: SocketAddr, - /// Is this inbound (true) or outbound (false) connection? - is_inbound: bool, - /// Key pair that is used to encrypt connection' messages. - key: KeyPair, - /// Last message time. - last_message_time: RwLock, - /// Underlying TCP stream. - stream: SharedTcpStream, -} - -impl NetConnectionsManager { - /// Create new network connections manager. - pub fn new( - executor: Executor, - message_processor: Arc, - trigger: Box, - container: Arc>, - config: &ClusterConfiguration, - net_config: NetConnectionsManagerConfig, - ) -> Result { - let listen_address = make_socket_address( - &net_config.listen_address.0, - net_config.listen_address.1)?; - - Ok(NetConnectionsManager { - listen_address, - data: Arc::new(NetConnectionsData { - allow_connecting_to_higher_nodes: net_config.allow_connecting_to_higher_nodes, - executor, - message_processor, - self_key_pair: config.self_key_pair.clone(), - trigger: Mutex::new(trigger), - container, - }), - }) - } - - /// Start listening for connections and schedule connections maintenance. - pub fn start(&self) -> Result<(), Error> { - net_listen(&self.listen_address, self.data.clone())?; - net_schedule_maintain(self.data.clone()); - Ok(()) - } -} - -impl ConnectionManager for NetConnectionsManager { - fn provider(&self) -> Arc { - self.data.container.clone() - } - - fn connect(&self) { - net_connect_disconnected(self.data.clone()); - } -} - -impl ConnectionProvider for RwLock { - fn connected_nodes(&self) -> Result, Error> { - let connections = self.read(); - if connections.is_isolated { - return Err(Error::NodeDisconnected); - } - - Ok(connections.connections.keys().cloned().collect()) - } - - fn disconnected_nodes(&self) -> BTreeSet { - let connections = self.read(); - connections.nodes.keys() - .filter(|node_id| !connections.connections.contains_key(node_id)) - .cloned() - .collect() - } - - fn connection(&self, node: &NodeId) -> Option> { - match self.read().connections.get(node).cloned() { - Some(connection) => Some(connection), - None => None, - } - } -} - -impl NetConnection { - /// Create new connection. - pub fn new(executor: Executor, is_inbound: bool, connection: IoConnection) -> NetConnection { - NetConnection { - executor, - node_id: connection.node_id, - node_address: connection.address, - is_inbound: is_inbound, - stream: connection.stream, - key: connection.key, - last_message_time: RwLock::new(Instant::now()), - } - } - - /// Get last message time. - pub fn last_message_time(&self) -> Instant { - *self.last_message_time.read() - } - - /// Update last message time - pub fn set_last_message_time(&self, last_message_time: Instant) { - *self.last_message_time.write() = last_message_time - } - - /// Returns future that sends encrypted message over this connection. - pub fn send_message_future(&self, message: Message) -> WriteMessage { - write_encrypted_message(self.stream.clone(), &self.key, message) - } - - /// Returns future that reads encrypted message from this connection. - pub fn read_message_future(&self) -> ReadMessage { - read_encrypted_message(self.stream.clone(), self.key.clone()) - } -} - -impl Connection for NetConnection { - fn is_inbound(&self) -> bool { - self.is_inbound - } - - fn node_id(&self) -> &NodeId { - &self.node_id - } - - fn node_address(&self) -> String { - format!("{}", self.node_address) - } - - fn send_message(&self, message: Message) { - execute(&self.executor, self.send_message_future(message).then(|_| Ok(()))); - } -} - -impl NetConnectionsData { - /// Executes closure for each active connection. - pub fn active_connections(&self) -> Vec> { - self.container.read().connections.values().cloned().collect() - } - - /// Executes closure for each disconnected node. - pub fn disconnected_nodes(&self) -> Vec<(NodeId, SocketAddr)> { - let container = self.container.read(); - container.nodes.iter() - .filter(|(node_id, _)| !container.connections.contains_key(node_id)) - .map(|(node_id, addr)| (*node_id, *addr)) - .collect() - } - - /// Try to insert new connection. Returns true if connection has been inserted. - /// Returns false (and ignores connections) if: - /// - we do not expect connection from this node - /// - we are already connected to the node and existing connection 'supersede' - /// new connection by agreement - pub fn insert(&self, connection: Arc) -> bool { - let node = *connection.node_id(); - let mut container = self.container.write(); - if !container.nodes.contains_key(&node) { - trace!(target: "secretstore_net", "{}: ignoring unknown connection from {} at {}", - self.self_key_pair.public(), node, connection.node_address()); - return false; - } - - if container.connections.contains_key(&node) { - // we have already connected to the same node - // the agreement is that node with lower id must establish connection to node with higher id - if (*self.self_key_pair.public() < node && connection.is_inbound()) - || (*self.self_key_pair.public() > node && !connection.is_inbound()) { - return false; - } - } - - trace!(target: "secretstore_net", - "{}: inserting connection to {} at {}. Connected to {} of {} nodes", - self.self_key_pair.public(), node, connection.node_address(), - container.connections.len() + 1, container.nodes.len()); - container.connections.insert(node, connection); - - true - } - - /// Tries to remove connection. Returns true if connection has been removed. - /// Returns false if we do not know this connection. - pub fn remove(&self, connection: &NetConnection) -> bool { - let node_id = *connection.node_id(); - let is_inbound = connection.is_inbound(); - let mut container = self.container.write(); - if let Entry::Occupied(entry) = container.connections.entry(node_id) { - if entry.get().is_inbound() != is_inbound { - return false; - } - - trace!(target: "secretstore_net", "{}: removing connection to {} at {}", - self.self_key_pair.public(), node_id, entry.get().node_address()); - entry.remove_entry(); - - true - } else { - false - } - } -} - -/// Listen incoming connections. -fn net_listen( - listen_address: &SocketAddr, - data: Arc, -) -> Result<(), Error> { - execute(&data.executor, net_listen_future(listen_address, data.clone())?); - Ok(()) -} - -/// Listen incoming connections future. -fn net_listen_future( - listen_address: &SocketAddr, - data: Arc, -) -> Result { - Ok(Box::new(TcpListener::bind(listen_address)? - .incoming() - .and_then(move |stream| { - net_accept_connection(data.clone(), stream); - Ok(()) - }) - .for_each(|_| Ok(())) - .then(|_| future::ok(())))) -} - -/// Accept incoming connection. -fn net_accept_connection( - data: Arc, - stream: TcpStream, -) { - execute(&data.executor, net_accept_connection_future(data.clone(), stream)); -} - -/// Accept incoming connection future. -fn net_accept_connection_future(data: Arc, stream: TcpStream) -> BoxedEmptyFuture { - Box::new(io_accept_connection(stream, data.self_key_pair.clone()) - .then(move |result| net_process_connection_result(data, None, result)) - .then(|_| future::ok(()))) -} - -/// Connect to remote node. -fn net_connect( - data: Arc, - remote: SocketAddr, -) { - execute(&data.executor, net_connect_future(data.clone(), remote)); -} - -/// Connect to remote node future. -fn net_connect_future( - data: Arc, - remote: SocketAddr, -) -> BoxedEmptyFuture { - let disconnected_nodes = data.container.disconnected_nodes(); - Box::new(io_connect(&remote, data.self_key_pair.clone(), disconnected_nodes) - .then(move |result| net_process_connection_result(data, Some(remote), result)) - .then(|_| future::ok(()))) -} - -/// Process network connection result. -fn net_process_connection_result( - data: Arc, - outbound_addr: Option, - result: Result>, TimeoutError>, -) -> IoFuture> { - match result { - Ok(DeadlineStatus::Meet(Ok(connection))) => { - let connection = Arc::new(NetConnection::new(data.executor.clone(), outbound_addr.is_none(), connection)); - if data.insert(connection.clone()) { - let maintain_action = data.trigger.lock().on_connection_established(connection.node_id()); - maintain_connection_trigger(data.clone(), maintain_action); - - return net_process_connection_messages(data, connection); - } - }, - Ok(DeadlineStatus::Meet(Err(err))) => { - warn!(target: "secretstore_net", "{}: protocol error '{}' when establishing {} connection{}", - data.self_key_pair.public(), err, if outbound_addr.is_some() { "outbound" } else { "inbound" }, - outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default()); - }, - Ok(DeadlineStatus::Timeout) => { - warn!(target: "secretstore_net", "{}: timeout when establishing {} connection{}", - data.self_key_pair.public(), if outbound_addr.is_some() { "outbound" } else { "inbound" }, - outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default()); - }, - Err(err) => { - warn!(target: "secretstore_net", "{}: network error '{}' when establishing {} connection{}", - data.self_key_pair.public(), err, if outbound_addr.is_some() { "outbound" } else { "inbound" }, - outbound_addr.map(|a| format!(" with {}", a)).unwrap_or_default()); - }, - } - - Box::new(future::ok(Ok(()))) -} - -/// Process connection messages. -fn net_process_connection_messages( - data: Arc, - connection: Arc, -) -> IoFuture> { - Box::new(connection - .read_message_future() - .then(move |result| - match result { - Ok((_, Ok(message))) => { - connection.set_last_message_time(Instant::now()); - data.message_processor.process_connection_message(connection.clone(), message); - // continue serving connection - let process_messages_future = net_process_connection_messages( - data.clone(), connection).then(|_| Ok(())); - execute(&data.executor, process_messages_future); - Box::new(future::ok(Ok(()))) - }, - Ok((_, Err(err))) => { - warn!(target: "secretstore_net", "{}: protocol error '{}' when reading message from node {}", - data.self_key_pair.public(), err, connection.node_id()); - // continue serving connection - let process_messages_future = net_process_connection_messages( - data.clone(), connection).then(|_| Ok(())); - execute(&data.executor, process_messages_future); - Box::new(future::ok(Err(err))) - }, - Err(err) => { - let node_id = *connection.node_id(); - warn!(target: "secretstore_net", "{}: network error '{}' when reading message from node {}", - data.self_key_pair.public(), err, node_id); - // close connection - if data.remove(&*connection) { - let maintain_action = data.trigger.lock().on_connection_closed(&node_id); - maintain_connection_trigger(data, maintain_action); - } - Box::new(future::err(err)) - }, - } - )) -} - -/// Schedule connections. maintain. -fn net_schedule_maintain(data: Arc) { - let closure_data = data.clone(); - execute(&data.executor, Interval::new_interval(Duration::new(MAINTAIN_INTERVAL, 0)) - .and_then(move |_| Ok(net_maintain(closure_data.clone()))) - .for_each(|_| Ok(())) - .then(|_| future::ok(()))); -} - -/// Maintain network connections. -fn net_maintain(data: Arc) { - trace!(target: "secretstore_net", "{}: executing maintain procedures", data.self_key_pair.public()); - - update_nodes_set(data.clone()); - data.message_processor.maintain_sessions(); - net_keep_alive(data.clone()); - net_connect_disconnected(data); -} - -/// Send keep alive messages to remote nodes. -fn net_keep_alive(data: Arc) { - let active_connections = data.active_connections(); - for connection in active_connections { - // the last_message_time could change after active_connections() call - // => we always need to call Instant::now() after getting last_message_time - let last_message_time = connection.last_message_time(); - let now = Instant::now(); - let last_message_diff = now - last_message_time; - if last_message_diff > KEEP_ALIVE_DISCONNECT_INTERVAL { - warn!(target: "secretstore_net", "{}: keep alive timeout for node {}", - data.self_key_pair.public(), connection.node_id()); - - let node_id = *connection.node_id(); - if data.remove(&*connection) { - let maintain_action = data.trigger.lock().on_connection_closed(&node_id); - maintain_connection_trigger(data.clone(), maintain_action); - } - data.message_processor.process_disconnect(&node_id); - } - else if last_message_diff > KEEP_ALIVE_SEND_INTERVAL { - connection.send_message(Message::Cluster(ClusterMessage::KeepAlive(message::KeepAlive {}))); - } - } -} - -/// Connect disconnected nodes. -fn net_connect_disconnected(data: Arc) { - let disconnected_nodes = data.disconnected_nodes(); - for (node_id, address) in disconnected_nodes { - if data.allow_connecting_to_higher_nodes || *data.self_key_pair.public() < node_id { - net_connect(data.clone(), address); - } - } -} - -/// Schedule future execution. -fn execute + Send + 'static>(executor: &Executor, f: F) { - if let Err(err) = future::Executor::execute(executor, Box::new(f)) { - error!("Secret store runtime unable to spawn task. Runtime is shutting down. ({:?})", err); - } -} - -/// Try to update active nodes set from connection trigger. -fn update_nodes_set(data: Arc) { - let maintain_action = data.trigger.lock().on_maintain(); - maintain_connection_trigger(data, maintain_action); -} - -/// Execute maintain procedures of connections trigger. -fn maintain_connection_trigger(data: Arc, maintain_action: Option) { - if maintain_action == Some(Maintain::SessionAndConnections) || maintain_action == Some(Maintain::Session) { - let session_params = data.trigger.lock().maintain_session(); - if let Some(session_params) = session_params { - let session = data.message_processor.start_servers_set_change_session(session_params); - match session { - Ok(_) => trace!(target: "secretstore_net", "{}: started auto-migrate session", - data.self_key_pair.public()), - Err(err) => trace!(target: "secretstore_net", "{}: failed to start auto-migrate session with: {}", - data.self_key_pair.public(), err), - } - } - } - if maintain_action == Some(Maintain::SessionAndConnections) || maintain_action == Some(Maintain::Connections) { - let mut trigger = data.trigger.lock(); - let mut data = data.container.write(); - trigger.maintain_connections(&mut *data); - } -} - -/// Compose SocketAddr from configuration' address and port. -fn make_socket_address(address: &str, port: u16) -> Result { - let ip_address: IpAddr = address.parse().map_err(|_| Error::InvalidNodeAddress)?; - Ok(SocketAddr::new(ip_address, port)) -} diff --git a/secret-store/src/key_server_cluster/cluster_message_processor.rs b/secret-store/src/key_server_cluster/cluster_message_processor.rs deleted file mode 100644 index df1211f8a0e..00000000000 --- a/secret-store/src/key_server_cluster/cluster_message_processor.rs +++ /dev/null @@ -1,358 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity. - -// Parity is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity. If not, see . - -use std::sync::Arc; -use blockchain::SigningKeyPair; -use key_server_cluster::{Error, NodeId}; -use key_server_cluster::cluster::{ServersSetChangeParams, new_servers_set_change_session}; -use key_server_cluster::cluster_sessions::{AdminSession}; -use key_server_cluster::cluster_connections::{ConnectionProvider, Connection}; -use key_server_cluster::cluster_sessions::{ClusterSession, ClusterSessions, ClusterSessionsContainer, - create_cluster_view}; -use key_server_cluster::cluster_sessions_creator::{ClusterSessionCreator, IntoSessionId}; -use key_server_cluster::message::{self, Message, ClusterMessage}; -use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSession, - IsolatedSessionTransport as KeyVersionNegotiationSessionTransport, ContinueAction}; -use key_server_cluster::connection_trigger::ServersSetChangeSessionCreatorConnector; - -/// Something that is able to process signals/messages from other nodes. -pub trait MessageProcessor: Send + Sync { - /// Process disconnect from the remote node. - fn process_disconnect(&self, node: &NodeId); - /// Process single message from the connection. - fn process_connection_message(&self, connection: Arc, message: Message); - - /// Start servers set change session. This is typically used by ConnectionManager when - /// it detects that auto-migration session needs to be started. - fn start_servers_set_change_session(&self, params: ServersSetChangeParams) -> Result, Error>; - /// Try to continue session after key version negotiation session is completed. - fn try_continue_session( - &self, - session: Option>> - ); - /// Maintain active sessions. Typically called by the ConnectionManager at some intervals. - /// Should cancel stalled sessions and send keep-alive messages for sessions that support it. - fn maintain_sessions(&self); -} - -/// Bridge between ConnectionManager and ClusterSessions. -pub struct SessionsMessageProcessor { - self_key_pair: Arc, - servers_set_change_creator_connector: Arc, - sessions: Arc, - connections: Arc, -} - -impl SessionsMessageProcessor { - /// Create new instance of SessionsMessageProcessor. - pub fn new( - self_key_pair: Arc, - servers_set_change_creator_connector: Arc, - sessions: Arc, - connections: Arc, - ) -> Self { - SessionsMessageProcessor { - self_key_pair, - servers_set_change_creator_connector, - sessions, - connections, - } - } - - /// Process single session message from connection. - fn process_message>( - &self, - sessions: &ClusterSessionsContainer, - connection: Arc, - mut message: Message, - ) -> Option> - where - Message: IntoSessionId - { - // get or create new session, if required - let mut sender = *connection.node_id(); - let session = self.prepare_session(sessions, &sender, &message); - // send error if session is not found, or failed to create - let session = match session { - Ok(session) => session, - Err(error) => { - // this is new session => it is not yet in container - warn!(target: "secretstore_net", - "{}: {} session read error '{}' when requested for session from node {}", - self.self_key_pair.public(), S::type_name(), error, sender); - if !message.is_error_message() { - let qed = "session_id only fails for cluster messages; - only session messages are passed to process_message; - qed"; - let session_id = message.into_session_id().expect(qed); - let session_nonce = message.session_nonce().expect(qed); - - connection.send_message(SC::make_error_message(session_id, session_nonce, error)); - } - return None; - }, - }; - - let session_id = session.id(); - let mut is_queued_message = false; - loop { - let message_result = session.on_message(&sender, &message); - match message_result { - Ok(_) => { - // if session is completed => stop - if session.is_finished() { - info!(target: "secretstore_net", - "{}: {} session completed", self.self_key_pair.public(), S::type_name()); - sessions.remove(&session_id); - return Some(session); - } - - // try to dequeue message - match sessions.dequeue_message(&session_id) { - Some((msg_sender, msg)) => { - is_queued_message = true; - sender = msg_sender; - message = msg; - }, - None => return Some(session), - } - }, - Err(Error::TooEarlyForRequest) => { - sessions.enqueue_message(&session_id, sender, message, is_queued_message); - return Some(session); - }, - Err(err) => { - warn!( - target: "secretstore_net", - "{}: {} session error '{}' when processing message {} from node {}", - self.self_key_pair.public(), - S::type_name(), - err, - message, - sender); - session.on_session_error(self.self_key_pair.public(), err); - sessions.remove(&session_id); - return Some(session); - }, - } - } - } - - /// Get or insert new session. - fn prepare_session>( - &self, - sessions: &ClusterSessionsContainer, - sender: &NodeId, - message: &Message - ) -> Result, Error> - where - Message: IntoSessionId - { - fn requires_all_connections(message: &Message) -> bool { - match *message { - Message::Generation(_) => true, - Message::ShareAdd(_) => true, - Message::ServersSetChange(_) => true, - _ => false, - } - } - - // get or create new session, if required - let session_id = message.into_session_id() - .expect("into_session_id fails for cluster messages only; - only session messages are passed to prepare_session; - qed"); - let is_initialization_message = message.is_initialization_message(); - let is_delegation_message = message.is_delegation_message(); - match is_initialization_message || is_delegation_message { - false => sessions.get(&session_id, true).ok_or(Error::NoActiveSessionWithId), - true => { - let creation_data = SC::creation_data_from_message(&message)?; - let master = if is_initialization_message { - *sender - } else { - *self.self_key_pair.public() - }; - let cluster = create_cluster_view( - self.self_key_pair.clone(), - self.connections.clone(), - requires_all_connections(&message))?; - - let nonce = Some(message.session_nonce().ok_or(Error::InvalidMessage)?); - let exclusive = message.is_exclusive_session_message(); - sessions.insert(cluster, master, session_id, nonce, exclusive, creation_data).map(|s| s.session) - }, - } - } - - /// Process single cluster message from the connection. - fn process_cluster_message(&self, connection: Arc, message: ClusterMessage) { - match message { - ClusterMessage::KeepAlive(_) => { - let msg = Message::Cluster(ClusterMessage::KeepAliveResponse(message::KeepAliveResponse { - session_id: None, - })); - connection.send_message(msg) - }, - ClusterMessage::KeepAliveResponse(msg) => if let Some(session_id) = msg.session_id { - self.sessions.on_session_keep_alive(connection.node_id(), session_id.into()); - }, - _ => warn!(target: "secretstore_net", "{}: received unexpected message {} from node {} at {}", - self.self_key_pair.public(), message, connection.node_id(), connection.node_address()), - } - } -} - -impl MessageProcessor for SessionsMessageProcessor { - fn process_disconnect(&self, node: &NodeId) { - self.sessions.on_connection_timeout(node); - } - - fn process_connection_message(&self, connection: Arc, message: Message) { - trace!(target: "secretstore_net", "{}: received message {} from {}", - self.self_key_pair.public(), message, connection.node_id()); - - // error is ignored as we only process errors on session level - match message { - Message::Generation(message) => self - .process_message(&self.sessions.generation_sessions, connection, Message::Generation(message)) - .map(|_| ()).unwrap_or_default(), - Message::Encryption(message) => self - .process_message(&self.sessions.encryption_sessions, connection, Message::Encryption(message)) - .map(|_| ()).unwrap_or_default(), - Message::Decryption(message) => self - .process_message(&self.sessions.decryption_sessions, connection, Message::Decryption(message)) - .map(|_| ()).unwrap_or_default(), - Message::SchnorrSigning(message) => self - .process_message(&self.sessions.schnorr_signing_sessions, connection, Message::SchnorrSigning(message)) - .map(|_| ()).unwrap_or_default(), - Message::EcdsaSigning(message) => self - .process_message(&self.sessions.ecdsa_signing_sessions, connection, Message::EcdsaSigning(message)) - .map(|_| ()).unwrap_or_default(), - Message::ServersSetChange(message) => { - let message = Message::ServersSetChange(message); - let is_initialization_message = message.is_initialization_message(); - let session = self.process_message(&self.sessions.admin_sessions, connection, message); - if is_initialization_message { - if let Some(session) = session { - self.servers_set_change_creator_connector - .set_key_servers_set_change_session(session.clone()); - } - } - }, - Message::KeyVersionNegotiation(message) => { - let session = self.process_message( - &self.sessions.negotiation_sessions, connection, Message::KeyVersionNegotiation(message)); - self.try_continue_session(session); - }, - Message::ShareAdd(message) => self.process_message( - &self.sessions.admin_sessions, connection, Message::ShareAdd(message)) - .map(|_| ()).unwrap_or_default(), - Message::Cluster(message) => self.process_cluster_message(connection, message), - } - } - - fn try_continue_session( - &self, - session: Option>> - ) { - if let Some(session) = session { - let meta = session.meta(); - let is_master_node = meta.self_node_id == meta.master_node_id; - if is_master_node && session.is_finished() { - self.sessions.negotiation_sessions.remove(&session.id()); - match session.result() { - Some(Ok(Some((version, master)))) => match session.take_continue_action() { - Some(ContinueAction::Decrypt( - session, origin, is_shadow_decryption, is_broadcast_decryption - )) => { - let initialization_error = if self.self_key_pair.public() == &master { - session.initialize( - origin, version, is_shadow_decryption, is_broadcast_decryption) - } else { - session.delegate( - master, origin, version, is_shadow_decryption, is_broadcast_decryption) - }; - - if let Err(error) = initialization_error { - session.on_session_error(&meta.self_node_id, error); - self.sessions.decryption_sessions.remove(&session.id()); - } - }, - Some(ContinueAction::SchnorrSign(session, message_hash)) => { - let initialization_error = if self.self_key_pair.public() == &master { - session.initialize(version, message_hash) - } else { - session.delegate(master, version, message_hash) - }; - - if let Err(error) = initialization_error { - session.on_session_error(&meta.self_node_id, error); - self.sessions.schnorr_signing_sessions.remove(&session.id()); - } - }, - Some(ContinueAction::EcdsaSign(session, message_hash)) => { - let initialization_error = if self.self_key_pair.public() == &master { - session.initialize(version, message_hash) - } else { - session.delegate(master, version, message_hash) - }; - - if let Err(error) = initialization_error { - session.on_session_error(&meta.self_node_id, error); - self.sessions.ecdsa_signing_sessions.remove(&session.id()); - } - }, - None => (), - }, - Some(Err(error)) => match session.take_continue_action() { - Some(ContinueAction::Decrypt(session, _, _, _)) => { - session.on_session_error(&meta.self_node_id, error); - self.sessions.decryption_sessions.remove(&session.id()); - }, - Some(ContinueAction::SchnorrSign(session, _)) => { - session.on_session_error(&meta.self_node_id, error); - self.sessions.schnorr_signing_sessions.remove(&session.id()); - }, - Some(ContinueAction::EcdsaSign(session, _)) => { - session.on_session_error(&meta.self_node_id, error); - self.sessions.ecdsa_signing_sessions.remove(&session.id()); - }, - None => (), - }, - None | Some(Ok(None)) => unreachable!("is_master_node; session is finished; - negotiation version always finished with result on master; - qed"), - } - } - } - } - - fn maintain_sessions(&self) { - self.sessions.stop_stalled_sessions(); - self.sessions.sessions_keep_alive(); - } - - fn start_servers_set_change_session(&self, params: ServersSetChangeParams) -> Result, Error> { - new_servers_set_change_session( - self.self_key_pair.clone(), - &*self.sessions, - self.connections.clone(), - self.servers_set_change_creator_connector.clone(), - params, - ).map(|s| s.session) - } -} diff --git a/secret-store/src/key_server_cluster/cluster_sessions.rs b/secret-store/src/key_server_cluster/cluster_sessions.rs deleted file mode 100644 index 7311f0e1028..00000000000 --- a/secret-store/src/key_server_cluster/cluster_sessions.rs +++ /dev/null @@ -1,786 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::time::{Duration, Instant}; -use std::sync::{Arc, Weak}; -use std::sync::atomic::AtomicBool; -use std::collections::{VecDeque, BTreeMap, BTreeSet}; -use futures::{oneshot, Oneshot, Complete, Future}; -use parking_lot::{Mutex, RwLock, Condvar}; -use ethereum_types::H256; -use crypto::publickey::Secret; -use blockchain::SigningKeyPair; -use key_server_cluster::{Error, NodeId, SessionId}; -use key_server_cluster::cluster::{Cluster, ClusterConfiguration, ClusterView}; -use key_server_cluster::cluster_connections::ConnectionProvider; -use key_server_cluster::connection_trigger::ServersSetChangeSessionCreatorConnector; -use key_server_cluster::message::{self, Message}; -use key_server_cluster::generation_session::{SessionImpl as GenerationSessionImpl}; -use key_server_cluster::decryption_session::{SessionImpl as DecryptionSessionImpl}; -use key_server_cluster::encryption_session::{SessionImpl as EncryptionSessionImpl}; -use key_server_cluster::signing_session_ecdsa::{SessionImpl as EcdsaSigningSessionImpl}; -use key_server_cluster::signing_session_schnorr::{SessionImpl as SchnorrSigningSessionImpl}; -use key_server_cluster::share_add_session::{SessionImpl as ShareAddSessionImpl, IsolatedSessionTransport as ShareAddTransport}; -use key_server_cluster::servers_set_change_session::{SessionImpl as ServersSetChangeSessionImpl}; -use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSessionImpl, - IsolatedSessionTransport as VersionNegotiationTransport}; - -use key_server_cluster::cluster_sessions_creator::{GenerationSessionCreator, EncryptionSessionCreator, DecryptionSessionCreator, - SchnorrSigningSessionCreator, KeyVersionNegotiationSessionCreator, AdminSessionCreator, SessionCreatorCore, - EcdsaSigningSessionCreator, ClusterSessionCreator}; - -/// When there are no session-related messages for SESSION_TIMEOUT_INTERVAL seconds, -/// we must treat this session as stalled && finish it with an error. -/// This timeout is for cases when node is responding to KeepAlive messages, but intentionally ignores -/// session messages. -const SESSION_TIMEOUT_INTERVAL: Duration = Duration::from_secs(60); -/// Interval to send session-level KeepAlive-messages. -const SESSION_KEEP_ALIVE_INTERVAL: Duration = Duration::from_secs(30); - -lazy_static! { - /// Servers set change session id (there could be at most 1 session => hardcoded id). - pub static ref SERVERS_SET_CHANGE_SESSION_ID: SessionId = "10b7af423bb551d5dc8645db754163a2145d37d78d468fa7330435ed77064c1c" - .parse() - .expect("hardcoded id should parse without errors; qed"); -} - -/// Session id with sub session. -#[derive(Debug, Clone, PartialEq, Eq)] -pub struct SessionIdWithSubSession { - /// Key id. - pub id: SessionId, - /// Sub session id. - pub access_key: Secret, -} - -/// Generic cluster session. -pub trait ClusterSession { - /// Session identifier type. - type Id: ::std::fmt::Debug + Ord + Clone; - /// Session creation data type. - type CreationData; - /// Session (successful) result type. - type SuccessfulResult: Send + 'static; - - /// Session type name. - fn type_name() -> &'static str; - /// Get session id. - fn id(&self) -> Self::Id; - /// If session is finished (either with succcess or not). - fn is_finished(&self) -> bool; - /// When it takes too much time to complete session. - fn on_session_timeout(&self); - /// When it takes too much time to receive response from the node. - fn on_node_timeout(&self, node_id: &NodeId); - /// Process error that has occured during session + propagate this error to required nodes. - fn on_session_error(&self, sender: &NodeId, error: Error); - /// Process session message. - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error>; - - /// 'Wait for session completion' helper. - #[cfg(test)] - fn wait_session Option>>( - completion: &CompletionSignal, - session_data: &Mutex, - timeout: Option, - result_reader: F - ) -> Option> { - let mut locked_data = session_data.lock(); - match result_reader(&locked_data) { - Some(result) => Some(result), - None => { - let completion_condvar = completion.completion_condvar.as_ref().expect("created in test mode"); - match timeout { - None => completion_condvar.wait(&mut locked_data), - Some(timeout) => { - completion_condvar.wait_for(&mut locked_data, timeout); - }, - } - - result_reader(&locked_data) - }, - } - } -} - -/// Waitable cluster session. -pub struct WaitableSession { - /// Session handle. - pub session: Arc, - /// Session result oneshot. - pub oneshot: Oneshot>, -} - -/// Session completion signal. -pub struct CompletionSignal { - /// Completion future. - pub completion_future: Mutex>>>, - - /// Completion condvar. - pub completion_condvar: Option, -} - -/// Administrative session. -pub enum AdminSession { - /// Share add session. - ShareAdd(ShareAddSessionImpl), - /// Servers set change session. - ServersSetChange(ServersSetChangeSessionImpl), -} - -/// Administrative session creation data. -pub enum AdminSessionCreationData { - /// Share add session (key id). - ShareAdd(H256), - /// Servers set change session (block id, new_server_set). - ServersSetChange(Option, BTreeSet), -} - -/// Active sessions on this cluster. -pub struct ClusterSessions { - /// Key generation sessions. - pub generation_sessions: ClusterSessionsContainer, - /// Encryption sessions. - pub encryption_sessions: ClusterSessionsContainer, - /// Decryption sessions. - pub decryption_sessions: ClusterSessionsContainer, - /// Schnorr signing sessions. - pub schnorr_signing_sessions: ClusterSessionsContainer, - /// ECDSA signing sessions. - pub ecdsa_signing_sessions: ClusterSessionsContainer, - /// Key version negotiation sessions. - pub negotiation_sessions: ClusterSessionsContainer< - KeyVersionNegotiationSessionImpl, - KeyVersionNegotiationSessionCreator - >, - /// Administrative sessions. - pub admin_sessions: ClusterSessionsContainer, - /// Self node id. - self_node_id: NodeId, - /// Creator core. - creator_core: Arc, -} - -/// Active sessions container listener. -pub trait ClusterSessionsListener: Send + Sync { - /// When new session is inserted to the container. - fn on_session_inserted(&self, _session: Arc) {} - /// When session is removed from the container. - fn on_session_removed(&self, _session: Arc) {} -} - -/// Active sessions container. -pub struct ClusterSessionsContainer> { - /// Sessions creator. - pub creator: SC, - /// Active sessions. - sessions: RwLock>>, - /// Listeners. Lock order: sessions -> listeners. - listeners: Mutex>>>, - /// Sessions container state. - container_state: Arc>, - /// Do not actually remove sessions. - preserve_sessions: bool, -} - -/// Session and its message queue. -pub struct QueuedSession { - /// Session master. - pub master: NodeId, - /// Cluster view. - pub cluster_view: Arc, - /// Last keep alive time. - pub last_keep_alive_time: Instant, - /// Last received message time. - pub last_message_time: Instant, - /// Generation session. - pub session: Arc, - /// Messages queue. - pub queue: VecDeque<(NodeId, Message)>, -} - -/// Cluster sessions container state. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum ClusterSessionsContainerState { - /// There's no active sessions => any session can be started. - Idle, - /// There are active sessions => exclusive session can't be started right now. - Active(usize), - /// Exclusive session is active => can't start any other sessions. - Exclusive, -} - -impl ClusterSessions { - /// Create new cluster sessions container. - pub fn new(config: &ClusterConfiguration, servers_set_change_session_creator_connector: Arc) -> Self { - let container_state = Arc::new(Mutex::new(ClusterSessionsContainerState::Idle)); - let creator_core = Arc::new(SessionCreatorCore::new(config)); - ClusterSessions { - self_node_id: config.self_key_pair.public().clone(), - generation_sessions: ClusterSessionsContainer::new(GenerationSessionCreator { - core: creator_core.clone(), - make_faulty_generation_sessions: AtomicBool::new(false), - }, container_state.clone()), - encryption_sessions: ClusterSessionsContainer::new(EncryptionSessionCreator { - core: creator_core.clone(), - }, container_state.clone()), - decryption_sessions: ClusterSessionsContainer::new(DecryptionSessionCreator { - core: creator_core.clone(), - }, container_state.clone()), - schnorr_signing_sessions: ClusterSessionsContainer::new(SchnorrSigningSessionCreator { - core: creator_core.clone(), - }, container_state.clone()), - ecdsa_signing_sessions: ClusterSessionsContainer::new(EcdsaSigningSessionCreator { - core: creator_core.clone(), - }, container_state.clone()), - negotiation_sessions: ClusterSessionsContainer::new(KeyVersionNegotiationSessionCreator { - core: creator_core.clone(), - }, container_state.clone()), - admin_sessions: ClusterSessionsContainer::new(AdminSessionCreator { - core: creator_core.clone(), - servers_set_change_session_creator_connector: servers_set_change_session_creator_connector, - admin_public: config.admin_public.clone(), - }, container_state), - creator_core: creator_core, - } - } - - #[cfg(test)] - pub fn make_faulty_generation_sessions(&self) { - self.generation_sessions.creator.make_faulty_generation_sessions(); - } - - #[cfg(test)] - pub fn preserve_sessions(&mut self) { - self.generation_sessions.preserve_sessions = true; - self.encryption_sessions.preserve_sessions = true; - self.decryption_sessions.preserve_sessions = true; - self.schnorr_signing_sessions.preserve_sessions = true; - self.ecdsa_signing_sessions.preserve_sessions = true; - self.negotiation_sessions.preserve_sessions = true; - self.admin_sessions.preserve_sessions = true; - } - - /// Send session-level keep-alive messages. - pub fn sessions_keep_alive(&self) { - self.admin_sessions.send_keep_alive(&*SERVERS_SET_CHANGE_SESSION_ID, &self.self_node_id); - } - - /// When session-level keep-alive response is received. - pub fn on_session_keep_alive(&self, sender: &NodeId, session_id: SessionId) { - if session_id == *SERVERS_SET_CHANGE_SESSION_ID { - self.admin_sessions.on_keep_alive(&session_id, sender); - } - } - - /// Stop sessions that are stalling. - pub fn stop_stalled_sessions(&self) { - self.generation_sessions.stop_stalled_sessions(); - self.encryption_sessions.stop_stalled_sessions(); - self.decryption_sessions.stop_stalled_sessions(); - self.schnorr_signing_sessions.stop_stalled_sessions(); - self.ecdsa_signing_sessions.stop_stalled_sessions(); - self.negotiation_sessions.stop_stalled_sessions(); - self.admin_sessions.stop_stalled_sessions(); - } - - /// When connection to node is lost. - pub fn on_connection_timeout(&self, node_id: &NodeId) { - self.generation_sessions.on_connection_timeout(node_id); - self.encryption_sessions.on_connection_timeout(node_id); - self.decryption_sessions.on_connection_timeout(node_id); - self.schnorr_signing_sessions.on_connection_timeout(node_id); - self.ecdsa_signing_sessions.on_connection_timeout(node_id); - self.negotiation_sessions.on_connection_timeout(node_id); - self.admin_sessions.on_connection_timeout(node_id); - self.creator_core.on_connection_timeout(node_id); - } -} - -impl ClusterSessionsContainer where S: ClusterSession, SC: ClusterSessionCreator { - pub fn new(creator: SC, container_state: Arc>) -> Self { - ClusterSessionsContainer { - creator: creator, - sessions: RwLock::new(BTreeMap::new()), - listeners: Mutex::new(Vec::new()), - container_state: container_state, - preserve_sessions: false, - } - } - - pub fn add_listener(&self, listener: Arc>) { - self.listeners.lock().push(Arc::downgrade(&listener)); - } - - #[cfg(test)] - pub fn is_empty(&self) -> bool { - self.sessions.read().is_empty() - } - - pub fn get(&self, session_id: &S::Id, update_last_message_time: bool) -> Option> { - let mut sessions = self.sessions.write(); - sessions.get_mut(session_id) - .map(|s| { - if update_last_message_time { - s.last_message_time = Instant::now(); - } - s.session.clone() - }) - } - - #[cfg(test)] - pub fn first(&self) -> Option> { - self.sessions.read().values().nth(0).map(|s| s.session.clone()) - } - - pub fn insert( - &self, - cluster: Arc, - master: NodeId, - session_id: S::Id, - session_nonce: Option, - is_exclusive_session: bool, - creation_data: Option, - ) -> Result, Error> { - let mut sessions = self.sessions.write(); - if sessions.contains_key(&session_id) { - return Err(Error::DuplicateSessionId); - } - - // create cluster - // let cluster = create_cluster_view(data, requires_all_connections)?; - // create session - let session = self.creator.create(cluster.clone(), master.clone(), session_nonce, session_id.clone(), creation_data)?; - // check if session can be started - self.container_state.lock().on_session_starting(is_exclusive_session)?; - - // insert session - let queued_session = QueuedSession { - master: master, - cluster_view: cluster, - last_keep_alive_time: Instant::now(), - last_message_time: Instant::now(), - session: session.session.clone(), - queue: VecDeque::new(), - }; - sessions.insert(session_id, queued_session); - self.notify_listeners(|l| l.on_session_inserted(session.session.clone())); - - Ok(session) - } - - pub fn remove(&self, session_id: &S::Id) { - self.do_remove(session_id, &mut *self.sessions.write()); - } - - pub fn enqueue_message(&self, session_id: &S::Id, sender: NodeId, message: Message, is_queued_message: bool) { - self.sessions.write().get_mut(session_id) - .map(|session| if is_queued_message { session.queue.push_front((sender, message)) } - else { session.queue.push_back((sender, message)) }); - } - - pub fn dequeue_message(&self, session_id: &S::Id) -> Option<(NodeId, Message)> { - self.sessions.write().get_mut(session_id) - .and_then(|session| session.queue.pop_front()) - } - - pub fn stop_stalled_sessions(&self) { - let mut sessions = self.sessions.write(); - for sid in sessions.keys().cloned().collect::>() { - let remove_session = { - let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); - if Instant::now() - session.last_message_time > SESSION_TIMEOUT_INTERVAL { - session.session.on_session_timeout(); - session.session.is_finished() - } else { - false - } - }; - - if remove_session { - self.do_remove(&sid, &mut *sessions); - } - } - } - - pub fn on_connection_timeout(&self, node_id: &NodeId) { - let mut sessions = self.sessions.write(); - for sid in sessions.keys().cloned().collect::>() { - let remove_session = { - let session = sessions.get(&sid).expect("enumerating only existing sessions; qed"); - session.session.on_node_timeout(node_id); - session.session.is_finished() - }; - - if remove_session { - self.do_remove(&sid, &mut *sessions); - } - } - } - - fn do_remove(&self, session_id: &S::Id, sessions: &mut BTreeMap>) { - if !self.preserve_sessions { - if let Some(session) = sessions.remove(session_id) { - self.container_state.lock().on_session_completed(); - self.notify_listeners(|l| l.on_session_removed(session.session.clone())); - } - } - } - - fn notify_listeners) -> ()>(&self, callback: F) { - let mut listeners = self.listeners.lock(); - let mut listener_index = 0; - while listener_index < listeners.len() { - match listeners[listener_index].upgrade() { - Some(listener) => { - callback(&*listener); - listener_index += 1; - }, - None => { - listeners.swap_remove(listener_index); - }, - } - } - } -} - -impl ClusterSessionsContainer - where - S: ClusterSession, - SC: ClusterSessionCreator, - SessionId: From, -{ - pub fn send_keep_alive(&self, session_id: &S::Id, self_node_id: &NodeId) { - if let Some(session) = self.sessions.write().get_mut(session_id) { - let now = Instant::now(); - if self_node_id == &session.master && now - session.last_keep_alive_time > SESSION_KEEP_ALIVE_INTERVAL { - session.last_keep_alive_time = now; - // since we send KeepAlive message to prevent nodes from disconnecting - // && worst thing that can happen if node is disconnected is that session is failed - // => ignore error here, because probably this node is not need for the rest of the session at all - let _ = session.cluster_view.broadcast(Message::Cluster(message::ClusterMessage::KeepAliveResponse(message::KeepAliveResponse { - session_id: Some(session_id.clone().into()), - }))); - } - } - } - - pub fn on_keep_alive(&self, session_id: &S::Id, sender: &NodeId) { - if let Some(session) = self.sessions.write().get_mut(session_id) { - let now = Instant::now(); - // we only accept keep alive from master node of ServersSetChange session - if sender == &session.master { - session.last_keep_alive_time = now; - } - } - } -} - -impl ClusterSessionsContainerState { - /// When session is starting. - pub fn on_session_starting(&mut self, is_exclusive_session: bool) -> Result<(), Error> { - match *self { - ClusterSessionsContainerState::Idle if is_exclusive_session => { - ::std::mem::replace(self, ClusterSessionsContainerState::Exclusive); - }, - ClusterSessionsContainerState::Idle => { - ::std::mem::replace(self, ClusterSessionsContainerState::Active(1)); - }, - ClusterSessionsContainerState::Active(_) if is_exclusive_session => - return Err(Error::HasActiveSessions), - ClusterSessionsContainerState::Active(sessions_count) => { - ::std::mem::replace(self, ClusterSessionsContainerState::Active(sessions_count + 1)); - }, - ClusterSessionsContainerState::Exclusive => - return Err(Error::ExclusiveSessionActive), - } - Ok(()) - } - - /// When session is completed. - pub fn on_session_completed(&mut self) { - match *self { - ClusterSessionsContainerState::Idle => - unreachable!("idle means that there are no active sessions; on_session_completed is only called once after active session is completed; qed"), - ClusterSessionsContainerState::Active(sessions_count) if sessions_count == 1 => { - ::std::mem::replace(self, ClusterSessionsContainerState::Idle); - }, - ClusterSessionsContainerState::Active(sessions_count) => { - ::std::mem::replace(self, ClusterSessionsContainerState::Active(sessions_count - 1)); - } - ClusterSessionsContainerState::Exclusive => { - ::std::mem::replace(self, ClusterSessionsContainerState::Idle); - }, - } - } -} - -impl SessionIdWithSubSession { - /// Create new decryption session Id. - pub fn new(session_id: SessionId, sub_session_id: Secret) -> Self { - SessionIdWithSubSession { - id: session_id, - access_key: sub_session_id, - } - } -} - -impl PartialOrd for SessionIdWithSubSession { - fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { - Some(self.cmp(other)) - } -} - -impl Ord for SessionIdWithSubSession { - fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { - match self.id.cmp(&other.id) { - ::std::cmp::Ordering::Equal => self.access_key.cmp(&other.access_key), - r @ _ => r, - } - } -} - -impl AdminSession { - pub fn as_servers_set_change(&self) -> Option<&ServersSetChangeSessionImpl> { - match *self { - AdminSession::ServersSetChange(ref session) => Some(session), - _ => None - } - } -} - -impl ClusterSession for AdminSession { - type Id = SessionId; - type CreationData = AdminSessionCreationData; - type SuccessfulResult = (); - - fn type_name() -> &'static str { - "admin" - } - - fn id(&self) -> SessionId { - match *self { - AdminSession::ShareAdd(ref session) => session.id().clone(), - AdminSession::ServersSetChange(ref session) => session.id().clone(), - } - } - - fn is_finished(&self) -> bool { - match *self { - AdminSession::ShareAdd(ref session) => session.is_finished(), - AdminSession::ServersSetChange(ref session) => session.is_finished(), - } - } - - fn on_session_timeout(&self) { - match *self { - AdminSession::ShareAdd(ref session) => session.on_session_timeout(), - AdminSession::ServersSetChange(ref session) => session.on_session_timeout(), - } - } - - fn on_node_timeout(&self, node_id: &NodeId) { - match *self { - AdminSession::ShareAdd(ref session) => session.on_node_timeout(node_id), - AdminSession::ServersSetChange(ref session) => session.on_node_timeout(node_id), - } - } - - fn on_session_error(&self, node: &NodeId, error: Error) { - match *self { - AdminSession::ShareAdd(ref session) => session.on_session_error(node, error), - AdminSession::ServersSetChange(ref session) => session.on_session_error(node, error), - } - } - - fn on_message(&self, sender: &NodeId, message: &Message) -> Result<(), Error> { - match *self { - AdminSession::ShareAdd(ref session) => session.on_message(sender, message), - AdminSession::ServersSetChange(ref session) => session.on_message(sender, message), - } - } -} - -impl WaitableSession { - pub fn new(session: S, oneshot: Oneshot>) -> Self { - WaitableSession { - session: Arc::new(session), - oneshot, - } - } - - pub fn into_wait_future(self) -> Box + Send> { - Box::new(self.oneshot - .map_err(|e| Error::Internal(e.to_string())) - .and_then(|res| res)) - } -} - -impl CompletionSignal { - pub fn new() -> (Self, Oneshot>) { - let (complete, oneshot) = oneshot(); - let completion_condvar = if cfg!(test) { Some(Condvar::new()) } else { None }; - (CompletionSignal { - completion_future: Mutex::new(Some(complete)), - completion_condvar, - }, oneshot) - } - - pub fn send(&self, result: Result) { - let completion_future = ::std::mem::replace(&mut *self.completion_future.lock(), None); - completion_future.map(|c| c.send(result)); - if let Some(ref completion_condvar) = self.completion_condvar { - completion_condvar.notify_all(); - } - } -} - -pub fn create_cluster_view(self_key_pair: Arc, connections: Arc, requires_all_connections: bool) -> Result, Error> { - let mut connected_nodes = connections.connected_nodes()?; - let disconnected_nodes = connections.disconnected_nodes(); - - let disconnected_nodes_count = disconnected_nodes.len(); - if requires_all_connections { - if disconnected_nodes_count != 0 { - return Err(Error::NodeDisconnected); - } - } - - connected_nodes.insert(self_key_pair.public().clone()); - - let connected_nodes_count = connected_nodes.len(); - Ok(Arc::new(ClusterView::new(self_key_pair, connections, connected_nodes, connected_nodes_count + disconnected_nodes_count))) -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::sync::atomic::{AtomicUsize, Ordering}; - use crypto::publickey::{Random, Generator}; - use key_server_cluster::{Error, DummyAclStorage, DummyKeyStorage, MapKeyServerSet, PlainNodeKeyPair}; - use key_server_cluster::cluster::ClusterConfiguration; - use key_server_cluster::connection_trigger::SimpleServersSetChangeSessionCreatorConnector; - use key_server_cluster::cluster::tests::DummyCluster; - use key_server_cluster::generation_session::{SessionImpl as GenerationSession}; - use super::{ClusterSessions, AdminSessionCreationData, ClusterSessionsListener, - ClusterSessionsContainerState, SESSION_TIMEOUT_INTERVAL}; - - pub fn make_cluster_sessions() -> ClusterSessions { - let key_pair = Random.generate().unwrap(); - let config = ClusterConfiguration { - self_key_pair: Arc::new(PlainNodeKeyPair::new(key_pair.clone())), - key_server_set: Arc::new(MapKeyServerSet::new(false, vec![(key_pair.public().clone(), format!("127.0.0.1:{}", 100).parse().unwrap())].into_iter().collect())), - key_storage: Arc::new(DummyKeyStorage::default()), - acl_storage: Arc::new(DummyAclStorage::default()), - admin_public: Some(Random.generate().unwrap().public().clone()), - preserve_sessions: false, - }; - ClusterSessions::new(&config, Arc::new(SimpleServersSetChangeSessionCreatorConnector { - admin_public: Some(Random.generate().unwrap().public().clone()), - })) - } - - #[test] - fn cluster_session_cannot_be_started_if_exclusive_session_is_active() { - let sessions = make_cluster_sessions(); - sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); - match sessions.admin_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, true, Some(AdminSessionCreationData::ShareAdd(Default::default()))) { - Err(Error::HasActiveSessions) => (), - Err(e) => unreachable!(format!("{}", e)), - Ok(_) => unreachable!("OK"), - } - } - - #[test] - fn exclusive_session_cannot_be_started_if_other_session_is_active() { - let sessions = make_cluster_sessions(); - - sessions.admin_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, true, Some(AdminSessionCreationData::ShareAdd(Default::default()))).unwrap(); - match sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None) { - Err(Error::ExclusiveSessionActive) => (), - Err(e) => unreachable!(format!("{}", e)), - Ok(_) => unreachable!("OK"), - } - } - - #[test] - fn session_listener_works() { - #[derive(Default)] - struct GenerationSessionListener { - inserted: AtomicUsize, - removed: AtomicUsize, - } - - impl ClusterSessionsListener for GenerationSessionListener { - fn on_session_inserted(&self, _session: Arc) { - self.inserted.fetch_add(1, Ordering::Relaxed); - } - - fn on_session_removed(&self, _session: Arc) { - self.removed.fetch_add(1, Ordering::Relaxed); - } - } - - let listener = Arc::new(GenerationSessionListener::default()); - let sessions = make_cluster_sessions(); - sessions.generation_sessions.add_listener(listener.clone()); - - sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); - assert_eq!(listener.inserted.load(Ordering::Relaxed), 1); - assert_eq!(listener.removed.load(Ordering::Relaxed), 0); - - sessions.generation_sessions.remove(&Default::default()); - assert_eq!(listener.inserted.load(Ordering::Relaxed), 1); - assert_eq!(listener.removed.load(Ordering::Relaxed), 1); - } - - #[test] - fn last_session_removal_sets_container_state_to_idle() { - let sessions = make_cluster_sessions(); - - sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); - assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Active(1)); - - sessions.generation_sessions.remove(&Default::default()); - assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Idle); - } - - #[test] - fn last_session_removal_by_timeout_sets_container_state_to_idle() { - let sessions = make_cluster_sessions(); - - sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); - assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Active(1)); - - sessions.generation_sessions.sessions.write().get_mut(&Default::default()).unwrap().last_message_time -= SESSION_TIMEOUT_INTERVAL * 2; - - sessions.generation_sessions.stop_stalled_sessions(); - assert_eq!(sessions.generation_sessions.sessions.read().len(), 0); - assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Idle); - } - - #[test] - fn last_session_removal_by_node_timeout_sets_container_state_to_idle() { - let sessions = make_cluster_sessions(); - - sessions.generation_sessions.insert(Arc::new(DummyCluster::new(Default::default())), Default::default(), Default::default(), None, false, None).unwrap(); - assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Active(1)); - - sessions.generation_sessions.on_connection_timeout(&Default::default()); - assert_eq!(sessions.generation_sessions.sessions.read().len(), 0); - assert_eq!(*sessions.generation_sessions.container_state.lock(), ClusterSessionsContainerState::Idle); - } -} diff --git a/secret-store/src/key_server_cluster/cluster_sessions_creator.rs b/secret-store/src/key_server_cluster/cluster_sessions_creator.rs deleted file mode 100644 index b991afb5914..00000000000 --- a/secret-store/src/key_server_cluster/cluster_sessions_creator.rs +++ /dev/null @@ -1,559 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::collections::BTreeMap; -use parking_lot::RwLock; -use crypto::publickey::Public; -use key_server_cluster::{Error, NodeId, SessionId, Requester, AclStorage, KeyStorage, DocumentKeyShare, SessionMeta}; -use key_server_cluster::cluster::{Cluster, ClusterConfiguration}; -use key_server_cluster::connection_trigger::ServersSetChangeSessionCreatorConnector; -use key_server_cluster::cluster_sessions::{WaitableSession, ClusterSession, SessionIdWithSubSession, - AdminSession, AdminSessionCreationData}; -use key_server_cluster::message::{self, Message, DecryptionMessage, SchnorrSigningMessage, ConsensusMessageOfShareAdd, - ShareAddMessage, ServersSetChangeMessage, ConsensusMessage, ConsensusMessageWithServersSet, EcdsaSigningMessage}; -use key_server_cluster::generation_session::{SessionImpl as GenerationSessionImpl, SessionParams as GenerationSessionParams}; -use key_server_cluster::decryption_session::{SessionImpl as DecryptionSessionImpl, - SessionParams as DecryptionSessionParams}; -use key_server_cluster::encryption_session::{SessionImpl as EncryptionSessionImpl, SessionParams as EncryptionSessionParams}; -use key_server_cluster::signing_session_ecdsa::{SessionImpl as EcdsaSigningSessionImpl, - SessionParams as EcdsaSigningSessionParams}; -use key_server_cluster::signing_session_schnorr::{SessionImpl as SchnorrSigningSessionImpl, - SessionParams as SchnorrSigningSessionParams}; -use key_server_cluster::share_add_session::{SessionImpl as ShareAddSessionImpl, - SessionParams as ShareAddSessionParams, IsolatedSessionTransport as ShareAddTransport}; -use key_server_cluster::servers_set_change_session::{SessionImpl as ServersSetChangeSessionImpl, - SessionParams as ServersSetChangeSessionParams}; -use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSessionImpl, - SessionParams as KeyVersionNegotiationSessionParams, IsolatedSessionTransport as VersionNegotiationTransport, - FastestResultComputer as FastestResultKeyVersionsResultComputer}; -use key_server_cluster::admin_sessions::ShareChangeSessionMeta; - -/// Generic cluster session creator. -pub trait ClusterSessionCreator { - /// Get creation data from message. - fn creation_data_from_message(_message: &Message) -> Result, Error> { - Ok(None) - } - - /// Prepare error message. - fn make_error_message(sid: S::Id, nonce: u64, err: Error) -> Message; - - /// Create cluster session. - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: S::Id, - creation_data: Option, - ) -> Result, Error>; -} - -/// Message with session id. -pub trait IntoSessionId { - /// Get session id. - fn into_session_id(&self) -> Result; -} - -pub struct SessionCreatorCore { - /// Self node id. - self_node_id: NodeId, - /// Reference to key storage - key_storage: Arc, - /// Reference to ACL storage - acl_storage: Arc, - /// Always-increasing sessions counter. Is used as session nonce to prevent replay attacks: - /// 1) during handshake, KeyServers generate new random key to encrypt messages - /// => there's no way to use messages from previous connections for replay attacks - /// 2) when session (of any type) is started, master node increases its own session counter and broadcasts it - /// 3) when slave KeyServer receives session initialization message, it checks that new nonce is larger than previous (from the same master) - /// => there's no way to use messages from previous sessions for replay attacks - /// 4) KeyServer checks that each session message contains the same nonce that initialization message - /// Given that: (A) handshake is secure and (B) session itself is initially replay-protected - /// => this guarantees that sessions are replay-protected. - session_counter: AtomicUsize, - /// Maximal session nonce, received from given connection. - max_nonce: RwLock>, -} - -impl SessionCreatorCore { - /// Create new session creator core. - pub fn new(config: &ClusterConfiguration) -> Self { - SessionCreatorCore { - self_node_id: config.self_key_pair.public().clone(), - acl_storage: config.acl_storage.clone(), - key_storage: config.key_storage.clone(), - session_counter: AtomicUsize::new(0), - max_nonce: RwLock::new(BTreeMap::new()), - } - } - - /// When node has teimtouted. - pub fn on_connection_timeout(&self, node_id: &NodeId) { - self.max_nonce.write().remove(node_id); - } - - /// Check or generate new session nonce. - fn check_session_nonce(&self, master: &NodeId, nonce: Option) -> Result { - // if we're master node of the session, then nonce should be generated - // if we're slave node of the session, then nonce should be passed from outside - match nonce { - Some(nonce) => match nonce > *self.max_nonce.write().entry(master.clone()).or_insert(0) { - true => Ok(nonce), - false => Err(Error::ReplayProtection), - }, - None => Ok(self.session_counter.fetch_add(1, Ordering::Relaxed) as u64 + 1), - } - } - - /// Read key share && remove disconnected nodes. - fn read_key_share(&self, key_id: &SessionId) -> Result, Error> { - self.key_storage.get(key_id) - } -} - -/// Generation session creator. -pub struct GenerationSessionCreator { - /// True if generation sessions must fail. - pub make_faulty_generation_sessions: AtomicBool, - /// Creator core. - pub core: Arc, -} - -impl GenerationSessionCreator { - #[cfg(test)] - pub fn make_faulty_generation_sessions(&self) { - self.make_faulty_generation_sessions.store(true, Ordering::Relaxed); - } -} - -impl ClusterSessionCreator for GenerationSessionCreator { - fn make_error_message(sid: SessionId, nonce: u64, err: Error) -> Message { - message::Message::Generation(message::GenerationMessage::SessionError(message::SessionError { - session: sid.into(), - session_nonce: nonce, - error: err.into(), - })) - } - - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: SessionId, - _creation_data: Option<()>, - ) -> Result, Error> { - // check that there's no finished encryption session with the same id - if self.core.key_storage.contains(&id) { - return Err(Error::ServerKeyAlreadyGenerated); - } - - let nonce = self.core.check_session_nonce(&master, nonce)?; - let (session, oneshot) = GenerationSessionImpl::new(GenerationSessionParams { - id: id.clone(), - self_node_id: self.core.self_node_id.clone(), - key_storage: Some(self.core.key_storage.clone()), - cluster: cluster, - nonce: Some(nonce), - }); - - if self.make_faulty_generation_sessions.load(Ordering::Relaxed) { - session.simulate_faulty_behaviour(); - } - - Ok(WaitableSession::new(session, oneshot)) - } -} - -/// Encryption session creator. -pub struct EncryptionSessionCreator { - /// Creator core. - pub core: Arc, -} - -impl ClusterSessionCreator for EncryptionSessionCreator { - fn make_error_message(sid: SessionId, nonce: u64, err: Error) -> Message { - message::Message::Encryption(message::EncryptionMessage::EncryptionSessionError(message::EncryptionSessionError { - session: sid.into(), - session_nonce: nonce, - error: err.into(), - })) - } - - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: SessionId, - _creation_data: Option<()>, - ) -> Result, Error> { - let encrypted_data = self.core.read_key_share(&id)?; - let nonce = self.core.check_session_nonce(&master, nonce)?; - let (session, oneshot) = EncryptionSessionImpl::new(EncryptionSessionParams { - id: id, - self_node_id: self.core.self_node_id.clone(), - encrypted_data: encrypted_data, - key_storage: self.core.key_storage.clone(), - cluster: cluster, - nonce: nonce, - })?; - - Ok(WaitableSession::new(session, oneshot)) - } -} - -/// Decryption session creator. -pub struct DecryptionSessionCreator { - /// Creator core. - pub core: Arc, -} - -impl ClusterSessionCreator for DecryptionSessionCreator { - fn creation_data_from_message(message: &Message) -> Result, Error> { - match *message { - Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(ref message)) => match &message.message { - &ConsensusMessage::InitializeConsensusSession(ref message) => Ok(Some(message.requester.clone().into())), - _ => Err(Error::InvalidMessage), - }, - Message::Decryption(DecryptionMessage::DecryptionSessionDelegation(ref message)) => Ok(Some(message.requester.clone().into())), - _ => Err(Error::InvalidMessage), - } - } - - fn make_error_message(sid: SessionIdWithSubSession, nonce: u64, err: Error) -> Message { - message::Message::Decryption(message::DecryptionMessage::DecryptionSessionError(message::DecryptionSessionError { - session: sid.id.into(), - sub_session: sid.access_key.into(), - session_nonce: nonce, - error: err.into(), - })) - } - - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: SessionIdWithSubSession, - requester: Option, - ) -> Result, Error> { - let encrypted_data = self.core.read_key_share(&id.id)?; - let nonce = self.core.check_session_nonce(&master, nonce)?; - let (session, oneshot) = DecryptionSessionImpl::new(DecryptionSessionParams { - meta: SessionMeta { - id: id.id, - self_node_id: self.core.self_node_id.clone(), - master_node_id: master, - threshold: encrypted_data.as_ref().map(|ks| ks.threshold).unwrap_or_default(), - configured_nodes_count: cluster.configured_nodes_count(), - connected_nodes_count: cluster.connected_nodes_count(), - }, - access_key: id.access_key, - key_share: encrypted_data, - acl_storage: self.core.acl_storage.clone(), - cluster: cluster, - nonce: nonce, - }, requester)?; - - Ok(WaitableSession::new(session, oneshot)) - } -} - -/// Schnorr signing session creator. -pub struct SchnorrSigningSessionCreator { - /// Creator core. - pub core: Arc, -} - -impl ClusterSessionCreator for SchnorrSigningSessionCreator { - fn creation_data_from_message(message: &Message) -> Result, Error> { - match *message { - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref message)) => match &message.message { - &ConsensusMessage::InitializeConsensusSession(ref message) => Ok(Some(message.requester.clone().into())), - _ => Err(Error::InvalidMessage), - }, - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref message)) => Ok(Some(message.requester.clone().into())), - _ => Err(Error::InvalidMessage), - } - } - - fn make_error_message(sid: SessionIdWithSubSession, nonce: u64, err: Error) -> Message { - message::Message::SchnorrSigning(message::SchnorrSigningMessage::SchnorrSigningSessionError(message::SchnorrSigningSessionError { - session: sid.id.into(), - sub_session: sid.access_key.into(), - session_nonce: nonce, - error: err.into(), - })) - } - - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: SessionIdWithSubSession, - requester: Option, - ) -> Result, Error> { - let encrypted_data = self.core.read_key_share(&id.id)?; - let nonce = self.core.check_session_nonce(&master, nonce)?; - let (session, oneshot) = SchnorrSigningSessionImpl::new(SchnorrSigningSessionParams { - meta: SessionMeta { - id: id.id, - self_node_id: self.core.self_node_id.clone(), - master_node_id: master, - threshold: encrypted_data.as_ref().map(|ks| ks.threshold).unwrap_or_default(), - configured_nodes_count: cluster.configured_nodes_count(), - connected_nodes_count: cluster.connected_nodes_count(), - }, - access_key: id.access_key, - key_share: encrypted_data, - acl_storage: self.core.acl_storage.clone(), - cluster: cluster, - nonce: nonce, - }, requester)?; - Ok(WaitableSession::new(session, oneshot)) - } -} - -/// ECDSA signing session creator. -pub struct EcdsaSigningSessionCreator { - /// Creator core. - pub core: Arc, -} - -impl ClusterSessionCreator for EcdsaSigningSessionCreator { - fn creation_data_from_message(message: &Message) -> Result, Error> { - match *message { - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref message)) => match &message.message { - &ConsensusMessage::InitializeConsensusSession(ref message) => Ok(Some(message.requester.clone().into())), - _ => Err(Error::InvalidMessage), - }, - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref message)) => Ok(Some(message.requester.clone().into())), - _ => Err(Error::InvalidMessage), - } - } - - fn make_error_message(sid: SessionIdWithSubSession, nonce: u64, err: Error) -> Message { - message::Message::EcdsaSigning(message::EcdsaSigningMessage::EcdsaSigningSessionError(message::EcdsaSigningSessionError { - session: sid.id.into(), - sub_session: sid.access_key.into(), - session_nonce: nonce, - error: err.into(), - })) - } - - fn create(&self, cluster: Arc, master: NodeId, nonce: Option, id: SessionIdWithSubSession, requester: Option) -> Result, Error> { - let encrypted_data = self.core.read_key_share(&id.id)?; - let nonce = self.core.check_session_nonce(&master, nonce)?; - let (session, oneshot) = EcdsaSigningSessionImpl::new(EcdsaSigningSessionParams { - meta: SessionMeta { - id: id.id, - self_node_id: self.core.self_node_id.clone(), - master_node_id: master, - threshold: encrypted_data.as_ref().map(|ks| ks.threshold).unwrap_or_default(), - configured_nodes_count: cluster.configured_nodes_count(), - connected_nodes_count: cluster.connected_nodes_count(), - }, - access_key: id.access_key, - key_share: encrypted_data, - acl_storage: self.core.acl_storage.clone(), - cluster: cluster, - nonce: nonce, - }, requester)?; - - Ok(WaitableSession::new(session, oneshot)) - } -} - -/// Key version negotiation session creator. -pub struct KeyVersionNegotiationSessionCreator { - /// Creator core. - pub core: Arc, -} - -impl ClusterSessionCreator> for KeyVersionNegotiationSessionCreator { - fn make_error_message(sid: SessionIdWithSubSession, nonce: u64, err: Error) -> Message { - message::Message::KeyVersionNegotiation(message::KeyVersionNegotiationMessage::KeyVersionsError(message::KeyVersionsError { - session: sid.id.into(), - sub_session: sid.access_key.into(), - session_nonce: nonce, - error: err.into(), - // we don't care about continue action here. it only matters when we're completing the session with confirmed - // fatal error from result computer - continue_with: None, - })) - } - - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: SessionIdWithSubSession, - _creation_data: Option<()>, - ) -> Result>, Error> { - let configured_nodes_count = cluster.configured_nodes_count(); - let connected_nodes_count = cluster.connected_nodes_count(); - let encrypted_data = self.core.read_key_share(&id.id)?; - let nonce = self.core.check_session_nonce(&master, nonce)?; - let computer = Arc::new(FastestResultKeyVersionsResultComputer::new(self.core.self_node_id.clone(), encrypted_data.as_ref(), - configured_nodes_count, configured_nodes_count)); - let (session, oneshot) = KeyVersionNegotiationSessionImpl::new(KeyVersionNegotiationSessionParams { - meta: ShareChangeSessionMeta { - id: id.id.clone(), - self_node_id: self.core.self_node_id.clone(), - master_node_id: master, - configured_nodes_count: configured_nodes_count, - connected_nodes_count: connected_nodes_count, - }, - sub_session: id.access_key.clone(), - key_share: encrypted_data, - result_computer: computer, - transport: VersionNegotiationTransport { - cluster: cluster, - key_id: id.id, - sub_session: id.access_key.clone(), - nonce: nonce, - }, - nonce: nonce, - }); - Ok(WaitableSession::new(session, oneshot)) - } -} - -/// Administrative session creator. -pub struct AdminSessionCreator { - /// Creator core. - pub core: Arc, - /// Administrator public. - pub admin_public: Option, - /// Servers set change sessions creator connector. - pub servers_set_change_session_creator_connector: Arc, -} - -impl ClusterSessionCreator for AdminSessionCreator { - fn creation_data_from_message(message: &Message) -> Result, Error> { - match *message { - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(ref message)) => match &message.message { - &ConsensusMessageWithServersSet::InitializeConsensusSession(ref message) => Ok(Some(AdminSessionCreationData::ServersSetChange( - message.migration_id.clone().map(Into::into), - message.new_nodes_set.clone().into_iter().map(Into::into).collect() - ))), - _ => Err(Error::InvalidMessage), - }, - Message::ShareAdd(ShareAddMessage::ShareAddConsensusMessage(ref message)) => match &message.message { - &ConsensusMessageOfShareAdd::InitializeConsensusSession(ref message) => Ok(Some(AdminSessionCreationData::ShareAdd(message.version.clone().into()))), - _ => Err(Error::InvalidMessage), - }, - _ => Err(Error::InvalidMessage), - } - } - - fn make_error_message(sid: SessionId, nonce: u64, err: Error) -> Message { - message::Message::ServersSetChange(message::ServersSetChangeMessage::ServersSetChangeError(message::ServersSetChangeError { - session: sid.into(), - session_nonce: nonce, - error: err.into(), - })) - } - - fn create( - &self, - cluster: Arc, - master: NodeId, - nonce: Option, - id: SessionId, - creation_data: Option, - ) -> Result, Error> { - let nonce = self.core.check_session_nonce(&master, nonce)?; - match creation_data { - Some(AdminSessionCreationData::ShareAdd(version)) => { - let (session, oneshot) = ShareAddSessionImpl::new(ShareAddSessionParams { - meta: ShareChangeSessionMeta { - id: id.clone(), - self_node_id: self.core.self_node_id.clone(), - master_node_id: master, - configured_nodes_count: cluster.configured_nodes_count(), - connected_nodes_count: cluster.connected_nodes_count(), - }, - transport: ShareAddTransport::new(id.clone(), Some(version), nonce, cluster), - key_storage: self.core.key_storage.clone(), - nonce: nonce, - admin_public: Some(self.admin_public.clone().ok_or(Error::AccessDenied)?), - })?; - Ok(WaitableSession::new(AdminSession::ShareAdd(session), oneshot)) - }, - Some(AdminSessionCreationData::ServersSetChange(migration_id, new_nodes_set)) => { - let admin_public = self.servers_set_change_session_creator_connector.admin_public(migration_id.as_ref(), new_nodes_set) - .map_err(|_| Error::AccessDenied)?; - - let (session, oneshot) = ServersSetChangeSessionImpl::new(ServersSetChangeSessionParams { - meta: ShareChangeSessionMeta { - id: id.clone(), - self_node_id: self.core.self_node_id.clone(), - master_node_id: master, - configured_nodes_count: cluster.configured_nodes_count(), - connected_nodes_count: cluster.connected_nodes_count(), - }, - cluster: cluster.clone(), - key_storage: self.core.key_storage.clone(), - nonce: nonce, - all_nodes_set: cluster.nodes(), - admin_public: admin_public, - migration_id: migration_id, - })?; - Ok(WaitableSession::new(AdminSession::ServersSetChange(session), oneshot)) - }, - None => unreachable!("expected to call with non-empty creation data; qed"), - } - } -} - -impl IntoSessionId for Message { - fn into_session_id(&self) -> Result { - match *self { - Message::Generation(ref message) => Ok(message.session_id().clone()), - Message::Encryption(ref message) => Ok(message.session_id().clone()), - Message::Decryption(_) => Err(Error::InvalidMessage), - Message::SchnorrSigning(_) => Err(Error::InvalidMessage), - Message::EcdsaSigning(_) => Err(Error::InvalidMessage), - Message::ServersSetChange(ref message) => Ok(message.session_id().clone()), - Message::ShareAdd(ref message) => Ok(message.session_id().clone()), - Message::KeyVersionNegotiation(_) => Err(Error::InvalidMessage), - Message::Cluster(_) => Err(Error::InvalidMessage), - } - } -} - -impl IntoSessionId for Message { - fn into_session_id(&self) -> Result { - match *self { - Message::Generation(_) => Err(Error::InvalidMessage), - Message::Encryption(_) => Err(Error::InvalidMessage), - Message::Decryption(ref message) => Ok(SessionIdWithSubSession::new(message.session_id().clone(), message.sub_session_id().clone())), - Message::SchnorrSigning(ref message) => Ok(SessionIdWithSubSession::new(message.session_id().clone(), message.sub_session_id().clone())), - Message::EcdsaSigning(ref message) => Ok(SessionIdWithSubSession::new(message.session_id().clone(), message.sub_session_id().clone())), - Message::ServersSetChange(_) => Err(Error::InvalidMessage), - Message::ShareAdd(_) => Err(Error::InvalidMessage), - Message::KeyVersionNegotiation(ref message) => Ok(SessionIdWithSubSession::new(message.session_id().clone(), message.sub_session_id().clone())), - Message::Cluster(_) => Err(Error::InvalidMessage), - } - } -} diff --git a/secret-store/src/key_server_cluster/connection_trigger.rs b/secret-store/src/key_server_cluster/connection_trigger.rs deleted file mode 100644 index db6ddab1e09..00000000000 --- a/secret-store/src/key_server_cluster/connection_trigger.rs +++ /dev/null @@ -1,392 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use std::collections::btree_map::Entry; -use std::net::SocketAddr; -use std::sync::Arc; -use ethereum_types::H256; -use crypto::publickey::Public; -use key_server_cluster::{KeyServerSet, KeyServerSetSnapshot}; -use key_server_cluster::cluster::{ClusterConfiguration, ServersSetChangeParams}; -use key_server_cluster::cluster_sessions::AdminSession; -use key_server_cluster::cluster_connections::{Connection}; -use key_server_cluster::cluster_connections_net::{NetConnectionsContainer}; -use types::{Error, NodeId}; -use blockchain::SigningKeyPair; - -#[derive(Debug, Clone, Copy, PartialEq)] -/// Describes which maintain() call is required. -pub enum Maintain { - /// We need to maintain() both connections && session. - SessionAndConnections, - /// Only call maintain_session. - Session, - /// Only call maintain_connections. - Connections, -} - -/// Connection trigger, which executes necessary actions when set of key servers changes. -pub trait ConnectionTrigger: Send + Sync { - /// On maintain interval. - fn on_maintain(&mut self) -> Option; - /// When connection is established. - fn on_connection_established(&mut self, node: &NodeId) -> Option; - /// When connection is closed. - fn on_connection_closed(&mut self, node: &NodeId) -> Option; - /// Maintain active sessions. Returns Some if servers set session creation required. - fn maintain_session(&mut self) -> Option; - /// Maintain active connections. - fn maintain_connections(&mut self, connections: &mut NetConnectionsContainer); - /// Return connector for the servers set change session creator. - fn servers_set_change_creator_connector(&self) -> Arc; -} - -/// Servers set change session creator connector. -pub trait ServersSetChangeSessionCreatorConnector: Send + Sync { - /// Get actual administrator public key. For manual-migration configuration it is the pre-configured - /// administrator key. For auto-migration configurations it is the key of actual MigrationSession master node. - fn admin_public(&self, migration_id: Option<&H256>, new_server_set: BTreeSet) -> Result; - /// Set active servers set change session. - fn set_key_servers_set_change_session(&self, session: Arc); -} - -/// Simple connection trigger, which only keeps connections to current_set. -pub struct SimpleConnectionTrigger { - /// Key server set cluster. - key_server_set: Arc, - /// Trigger connections. - connections: TriggerConnections, - /// Servers set change session creator connector. - connector: Arc, -} - -/// Simple Servers set change session creator connector, which will just return -/// pre-configured administartor public when asked. -pub struct SimpleServersSetChangeSessionCreatorConnector { - /// Secret store administrator public key. - pub admin_public: Option, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -/// Action with trigger connections. -pub enum ConnectionsAction { - /// Connect to nodes from old set only. - ConnectToCurrentSet, - /// Connect to nodes from migration set. - ConnectToMigrationSet, -} - -/// Trigger connections. -pub struct TriggerConnections { - /// This node key pair. - pub self_key_pair: Arc, -} - -impl SimpleConnectionTrigger { - /// Create new simple from cluster configuration. - pub fn with_config(config: &ClusterConfiguration) -> Self { - Self::new(config.key_server_set.clone(), config.self_key_pair.clone(), config.admin_public) - } - - /// Create new simple connection trigger. - pub fn new(key_server_set: Arc, self_key_pair: Arc, admin_public: Option) -> Self { - SimpleConnectionTrigger { - key_server_set: key_server_set, - connections: TriggerConnections { - self_key_pair: self_key_pair, - }, - connector: Arc::new(SimpleServersSetChangeSessionCreatorConnector { - admin_public: admin_public, - }), - } - } -} - -impl ConnectionTrigger for SimpleConnectionTrigger { - fn on_maintain(&mut self) -> Option { - Some(Maintain::Connections) - } - - fn on_connection_established(&mut self, _node: &NodeId) -> Option { - None - } - - fn on_connection_closed(&mut self, _node: &NodeId) -> Option { - // we do not want to reconnect after every connection close - // because it could be a part of something bigger - None - } - - fn maintain_session(&mut self) -> Option { - None - } - - fn maintain_connections(&mut self, connections: &mut NetConnectionsContainer) { - self.connections.maintain(ConnectionsAction::ConnectToCurrentSet, connections, &self.key_server_set.snapshot()) - } - - fn servers_set_change_creator_connector(&self) -> Arc { - self.connector.clone() - } -} - -impl ServersSetChangeSessionCreatorConnector for SimpleServersSetChangeSessionCreatorConnector { - fn admin_public(&self, _migration_id: Option<&H256>, _new_server_set: BTreeSet) -> Result { - self.admin_public.clone().ok_or(Error::AccessDenied) - } - - fn set_key_servers_set_change_session(&self, _session: Arc) { - } -} - -impl TriggerConnections { - pub fn maintain(&self, action: ConnectionsAction, data: &mut NetConnectionsContainer, server_set: &KeyServerSetSnapshot) { - match action { - ConnectionsAction::ConnectToCurrentSet => { - adjust_connections(self.self_key_pair.public(), data, &server_set.current_set); - }, - ConnectionsAction::ConnectToMigrationSet => { - let migration_set = server_set.migration.as_ref().map(|s| s.set.clone()).unwrap_or_default(); - adjust_connections(self.self_key_pair.public(), data, &migration_set); - }, - } - } -} - -fn adjust_connections( - self_node_id: &NodeId, - data: &mut NetConnectionsContainer, - required_set: &BTreeMap -) { - if !required_set.contains_key(self_node_id) { - if !data.is_isolated { - trace!(target: "secretstore_net", "{}: isolated from cluser", self_node_id); - } - - data.is_isolated = true; - data.connections.clear(); - data.nodes.clear(); - return; - } - - data.is_isolated = false; - for node_to_disconnect in select_nodes_to_disconnect(&data.nodes, required_set) { - if let Entry::Occupied(entry) = data.connections.entry(node_to_disconnect.clone()) { - trace!(target: "secretstore_net", "{}: adjusting connections - removing connection to {} at {}", - self_node_id, entry.get().node_id(), entry.get().node_address()); - entry.remove(); - } - - data.nodes.remove(&node_to_disconnect); - } - - for (node_to_connect, node_addr) in required_set { - if node_to_connect != self_node_id { - data.nodes.insert(node_to_connect.clone(), node_addr.clone()); - } - } -} - -fn select_nodes_to_disconnect(current_set: &BTreeMap, new_set: &BTreeMap) -> Vec { - current_set.iter() - .filter(|&(node_id, node_addr)| match new_set.get(node_id) { - Some(new_node_addr) => node_addr != new_node_addr, - None => true, - }) - .map(|(node_id, _)| node_id.clone()) - .collect() -} - -#[cfg(test)] -mod tests { - use std::collections::BTreeSet; - use std::sync::Arc; - use crypto::publickey::{Random, Generator}; - use key_server_cluster::{MapKeyServerSet, PlainNodeKeyPair, KeyServerSetSnapshot, KeyServerSetMigration}; - use key_server_cluster::cluster_connections_net::NetConnectionsContainer; - use super::{Maintain, TriggerConnections, ConnectionsAction, ConnectionTrigger, SimpleConnectionTrigger, - select_nodes_to_disconnect, adjust_connections}; - - fn default_connection_data() -> NetConnectionsContainer { - NetConnectionsContainer { - is_isolated: false, - nodes: Default::default(), - connections: Default::default(), - } - } - - fn create_connections() -> TriggerConnections { - TriggerConnections { - self_key_pair: Arc::new(PlainNodeKeyPair::new(Random.generate().unwrap())), - } - } - - #[test] - fn do_not_disconnect_if_set_is_not_changed() { - let node_id = Random.generate().unwrap().public().clone(); - assert_eq!(select_nodes_to_disconnect( - &vec![(node_id, "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - &vec![(node_id, "127.0.0.1:8081".parse().unwrap())].into_iter().collect()), - vec![]); - } - - #[test] - fn disconnect_if_address_has_changed() { - let node_id = Random.generate().unwrap().public().clone(); - assert_eq!(select_nodes_to_disconnect( - &vec![(node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - &vec![(node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect()), - vec![node_id.clone()]); - } - - #[test] - fn disconnect_if_node_has_removed() { - let node_id = Random.generate().unwrap().public().clone(); - assert_eq!(select_nodes_to_disconnect( - &vec![(node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - &vec![].into_iter().collect()), - vec![node_id.clone()]); - } - - #[test] - fn does_not_disconnect_if_node_has_added() { - let node_id = Random.generate().unwrap().public().clone(); - assert_eq!(select_nodes_to_disconnect( - &vec![(node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - &vec![(node_id.clone(), "127.0.0.1:8081".parse().unwrap()), - (Random.generate().unwrap().public().clone(), "127.0.0.1:8082".parse().unwrap())] - .into_iter().collect()), - vec![]); - } - - #[test] - fn adjust_connections_disconnects_from_all_nodes_if_not_a_part_of_key_server() { - let self_node_id = Random.generate().unwrap().public().clone(); - let other_node_id = Random.generate().unwrap().public().clone(); - let mut connection_data = default_connection_data(); - connection_data.nodes.insert(other_node_id.clone(), "127.0.0.1:8081".parse().unwrap()); - - let required_set = connection_data.nodes.clone(); - adjust_connections(&self_node_id, &mut connection_data, &required_set); - assert!(connection_data.nodes.is_empty()); - assert!(connection_data.is_isolated); - } - - #[test] - fn adjust_connections_connects_to_new_nodes() { - let self_node_id = Random.generate().unwrap().public().clone(); - let other_node_id = Random.generate().unwrap().public().clone(); - let mut connection_data = default_connection_data(); - - let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()), - (other_node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect(); - adjust_connections(&self_node_id, &mut connection_data, &required_set); - assert!(connection_data.nodes.contains_key(&other_node_id)); - assert!(!connection_data.is_isolated); - } - - #[test] - fn adjust_connections_reconnects_from_changed_nodes() { - let self_node_id = Random.generate().unwrap().public().clone(); - let other_node_id = Random.generate().unwrap().public().clone(); - let mut connection_data = default_connection_data(); - connection_data.nodes.insert(other_node_id.clone(), "127.0.0.1:8082".parse().unwrap()); - - let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()), - (other_node_id.clone(), "127.0.0.1:8083".parse().unwrap())].into_iter().collect(); - adjust_connections(&self_node_id, &mut connection_data, &required_set); - assert_eq!(connection_data.nodes.get(&other_node_id), Some(&"127.0.0.1:8083".parse().unwrap())); - assert!(!connection_data.is_isolated); - } - - #[test] - fn adjust_connections_disconnects_from_removed_nodes() { - let self_node_id = Random.generate().unwrap().public().clone(); - let other_node_id = Random.generate().unwrap().public().clone(); - let mut connection_data = default_connection_data(); - connection_data.nodes.insert(other_node_id.clone(), "127.0.0.1:8082".parse().unwrap()); - - let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(); - adjust_connections(&self_node_id, &mut connection_data, &required_set); - assert!(connection_data.nodes.is_empty()); - assert!(!connection_data.is_isolated); - } - - #[test] - fn adjust_connections_does_not_connects_to_self() { - let self_node_id = Random.generate().unwrap().public().clone(); - let mut connection_data = default_connection_data(); - - let required_set = vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(); - adjust_connections(&self_node_id, &mut connection_data, &required_set); - assert!(connection_data.nodes.is_empty()); - assert!(!connection_data.is_isolated); - } - - #[test] - fn maintain_connects_to_current_set_works() { - let connections = create_connections(); - let self_node_id = connections.self_key_pair.public().clone(); - let current_node_id = Random.generate().unwrap().public().clone(); - let migration_node_id = Random.generate().unwrap().public().clone(); - let new_node_id = Random.generate().unwrap().public().clone(); - - let mut connections_data = default_connection_data(); - connections.maintain(ConnectionsAction::ConnectToCurrentSet, &mut connections_data, &KeyServerSetSnapshot { - current_set: vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()), - (current_node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect(), - new_set: vec![(new_node_id.clone(), "127.0.0.1:8083".parse().unwrap())].into_iter().collect(), - migration: Some(KeyServerSetMigration { - set: vec![(migration_node_id.clone(), "127.0.0.1:8084".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }); - - assert_eq!(vec![current_node_id], connections_data.nodes.keys().cloned().collect::>()); - } - - #[test] - fn maintain_connects_to_migration_set_works() { - let connections = create_connections(); - let self_node_id = connections.self_key_pair.public().clone(); - let current_node_id = Random.generate().unwrap().public().clone(); - let migration_node_id = Random.generate().unwrap().public().clone(); - let new_node_id = Random.generate().unwrap().public().clone(); - - let mut connections_data = default_connection_data(); - connections.maintain(ConnectionsAction::ConnectToMigrationSet, &mut connections_data, &KeyServerSetSnapshot { - current_set: vec![(current_node_id.clone(), "127.0.0.1:8082".parse().unwrap())].into_iter().collect(), - new_set: vec![(new_node_id.clone(), "127.0.0.1:8083".parse().unwrap())].into_iter().collect(), - migration: Some(KeyServerSetMigration { - set: vec![(self_node_id.clone(), "127.0.0.1:8081".parse().unwrap()), - (migration_node_id.clone(), "127.0.0.1:8084".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }); - - assert_eq!(vec![migration_node_id].into_iter().collect::>(), - connections_data.nodes.keys().cloned().collect::>()); - } - - #[test] - fn simple_connections_trigger_only_maintains_connections() { - let key_server_set = Arc::new(MapKeyServerSet::new(false, Default::default())); - let self_key_pair = Arc::new(PlainNodeKeyPair::new(Random.generate().unwrap())); - let mut trigger = SimpleConnectionTrigger::new(key_server_set, self_key_pair, None); - assert_eq!(trigger.on_maintain(), Some(Maintain::Connections)); - } -} diff --git a/secret-store/src/key_server_cluster/connection_trigger_with_migration.rs b/secret-store/src/key_server_cluster/connection_trigger_with_migration.rs deleted file mode 100644 index 4c8bcc482f7..00000000000 --- a/secret-store/src/key_server_cluster/connection_trigger_with_migration.rs +++ /dev/null @@ -1,759 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use std::net::SocketAddr; -use std::sync::Arc; -use ethereum_types::H256; -use crypto::publickey::Public; -use parking_lot::Mutex; -use key_server_cluster::{KeyServerSet, KeyServerSetSnapshot, KeyServerSetMigration, is_migration_required}; -use key_server_cluster::cluster::{ClusterConfiguration, ServersSetChangeParams}; -use key_server_cluster::cluster_connections_net::NetConnectionsContainer; -use key_server_cluster::cluster_sessions::{AdminSession, ClusterSession}; -use key_server_cluster::jobs::servers_set_change_access_job::ordered_nodes_hash; -use key_server_cluster::connection_trigger::{Maintain, ConnectionsAction, ConnectionTrigger, - ServersSetChangeSessionCreatorConnector, TriggerConnections}; -use types::{Error, NodeId}; -use blockchain::SigningKeyPair; - -/// Key servers set change trigger with automated migration procedure. -pub struct ConnectionTriggerWithMigration { - /// This node key pair. - self_key_pair: Arc, - /// Key server set. - key_server_set: Arc, - /// Last server set state. - snapshot: KeyServerSetSnapshot, - /// Required connections action. - connections_action: Option, - /// Required session action. - session_action: Option, - /// Currenty connected nodes. - connected: BTreeSet, - /// Trigger migration connections. - connections: TriggerConnections, - /// Trigger migration session. - session: TriggerSession, -} - -#[derive(Default)] -/// Key servers set change session creator connector with migration support. -pub struct ServersSetChangeSessionCreatorConnectorWithMigration { - /// This node id. - self_node_id: NodeId, - /// Active migration state to check when servers set change session is started. - migration: Mutex>, - /// Active servers set change session. - session: Mutex>>, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -/// Migration session action. -enum SessionAction { - /// Start migration (confirm migration transaction). - StartMigration(H256), - /// Start migration session. - Start, - /// Confirm migration and forget migration session. - ConfirmAndDrop(H256), - /// Forget migration session. - Drop, - /// Forget migration session and retry. - DropAndRetry, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -/// Migration session state. -enum SessionState { - /// No active session. - Idle, - /// Session is running with given migration id. - Active(Option), - /// Session is completed successfully. - Finished(Option), - /// Session is completed with an error. - Failed(Option), -} - -#[derive(Debug, Clone, Copy, PartialEq)] -/// Migration state. -pub enum MigrationState { - /// No migration required. - Idle, - /// Migration is required. - Required, - /// Migration has started. - Started, -} - -/// Migration session. -struct TriggerSession { - /// Servers set change session creator connector. - connector: Arc, - /// This node key pair. - self_key_pair: Arc, - /// Key server set. - key_server_set: Arc, -} - -impl ConnectionTriggerWithMigration { - /// Create new simple from cluster configuration. - pub fn with_config(config: &ClusterConfiguration) -> Self { - Self::new(config.key_server_set.clone(), config.self_key_pair.clone()) - } - - /// Create new trigge with migration. - pub fn new(key_server_set: Arc, self_key_pair: Arc) -> Self { - let snapshot = key_server_set.snapshot(); - let migration = snapshot.migration.clone(); - - ConnectionTriggerWithMigration { - self_key_pair: self_key_pair.clone(), - key_server_set: key_server_set.clone(), - snapshot: snapshot, - connected: BTreeSet::new(), - connections: TriggerConnections { - self_key_pair: self_key_pair.clone(), - }, - session: TriggerSession { - connector: Arc::new(ServersSetChangeSessionCreatorConnectorWithMigration { - self_node_id: self_key_pair.public().clone(), - migration: Mutex::new(migration), - session: Mutex::new(None), - }), - self_key_pair: self_key_pair, - key_server_set: key_server_set, - }, - connections_action: None, - session_action: None, - } - } - - /// Actually do mainteinance. - fn do_maintain(&mut self) -> Option { - loop { - let session_state = session_state(self.session.connector.session.lock().clone()); - let migration_state = migration_state(self.self_key_pair.public(), &self.snapshot); - - let session_action = maintain_session(self.self_key_pair.public(), &self.connected, &self.snapshot, migration_state, session_state); - let session_maintain_required = session_action.map(|session_action| - self.session.process(session_action)).unwrap_or_default(); - self.session_action = session_action; - - let connections_action = maintain_connections(migration_state, session_state); - let connections_maintain_required = connections_action.map(|_| true).unwrap_or_default(); - self.connections_action = connections_action; - - if session_state != SessionState::Idle || migration_state != MigrationState::Idle { - trace!(target: "secretstore_net", "{}: non-idle auto-migration state: {:?} -> {:?}", - self.self_key_pair.public(), (migration_state, session_state), (self.connections_action, self.session_action)); - } - - if session_action != Some(SessionAction::DropAndRetry) { - return match (session_maintain_required, connections_maintain_required) { - (true, true) => Some(Maintain::SessionAndConnections), - (true, false) => Some(Maintain::Session), - (false, true) => Some(Maintain::Connections), - (false, false) => None, - }; - } - } - } -} - -impl ConnectionTrigger for ConnectionTriggerWithMigration { - fn on_maintain(&mut self) -> Option { - self.snapshot = self.key_server_set.snapshot(); - *self.session.connector.migration.lock() = self.snapshot.migration.clone(); - - self.do_maintain() - } - - fn on_connection_established(&mut self, node: &NodeId) -> Option { - self.connected.insert(node.clone()); - self.do_maintain() - } - - fn on_connection_closed(&mut self, node: &NodeId) -> Option { - self.connected.remove(node); - self.do_maintain() - } - - fn maintain_session(&mut self) -> Option { - self.session_action.and_then(|action| self.session.maintain(action, &self.snapshot)) - } - - fn maintain_connections(&mut self, connections: &mut NetConnectionsContainer) { - if let Some(action) = self.connections_action { - self.connections.maintain(action, connections, &self.snapshot); - } - } - - fn servers_set_change_creator_connector(&self) -> Arc { - self.session.connector.clone() - } -} - -impl ServersSetChangeSessionCreatorConnector for ServersSetChangeSessionCreatorConnectorWithMigration { - fn admin_public(&self, migration_id: Option<&H256>, new_server_set: BTreeSet) -> Result { - // the idea is that all nodes are agreed upon a block number and a new set of nodes in this block - // then master node is selected of all nodes set && this master signs the old set && new set - // (signatures are inputs to ServerSetChangeSession) - self.migration.lock().as_ref() - .map(|migration| { - let is_migration_id_same = migration_id.map(|mid| mid == &migration.id).unwrap_or_default(); - let is_migration_set_same = new_server_set == migration.set.keys().cloned().collect(); - if is_migration_id_same && is_migration_set_same { - Ok(migration.master.clone()) - } else { - warn!(target: "secretstore_net", "{}: failed to accept auto-migration session: same_migration_id={}, same_migration_set={}", - self.self_node_id, is_migration_id_same, is_migration_set_same); - - Err(Error::AccessDenied) - } - }) - .unwrap_or_else(|| { - warn!(target: "secretstore_net", "{}: failed to accept non-scheduled auto-migration session", self.self_node_id); - Err(Error::AccessDenied) - }) - } - - fn set_key_servers_set_change_session(&self, session: Arc) { - *self.session.lock() = Some(session); - } -} - -impl TriggerSession { - /// Process session action. - pub fn process(&mut self, action: SessionAction) -> bool { - match action { - SessionAction::ConfirmAndDrop(migration_id) => { - *self.connector.session.lock() = None; - self.key_server_set.confirm_migration(migration_id); - false - }, - SessionAction::Drop | SessionAction::DropAndRetry => { - *self.connector.session.lock() = None; - false - }, - SessionAction::StartMigration(migration_id) => { - self.key_server_set.start_migration(migration_id); - false - }, - SessionAction::Start => true, - } - } - - /// Maintain session. - pub fn maintain( - &mut self, - action: SessionAction, - server_set: &KeyServerSetSnapshot - ) -> Option { - if action != SessionAction::Start { // all other actions are processed in maintain - return None; - } - let migration = server_set.migration.as_ref() - .expect("action is Start only when migration is started (see maintain_session); qed"); - - // we assume that authorities that are removed from the servers set are either offline, or malicious - // => they're not involved in ServersSetChangeSession - // => both sets are the same - let old_set: BTreeSet<_> = migration.set.keys().cloned().collect(); - let new_set = old_set.clone(); - - let signatures = self.self_key_pair.sign(&ordered_nodes_hash(&old_set)) - .and_then(|old_set_signature| self.self_key_pair.sign(&ordered_nodes_hash(&new_set)) - .map(|new_set_signature| (old_set_signature, new_set_signature))); - - match signatures { - Ok((old_set_signature, new_set_signature)) => Some(ServersSetChangeParams { - session_id: None, - migration_id: Some(migration.id), - new_nodes_set: new_set, - old_set_signature, - new_set_signature, - }), - Err(err) => { - trace!( - target: "secretstore_net", - "{}: failed to sign servers set for auto-migrate session with: {}", - self.self_key_pair.public(), err); - None - }, - } - } -} - -fn migration_state(self_node_id: &NodeId, snapshot: &KeyServerSetSnapshot) -> MigrationState { - // if this node is not on current && old set => we do not participate in migration - if !snapshot.current_set.contains_key(self_node_id) && - !snapshot.migration.as_ref().map(|s| s.set.contains_key(self_node_id)).unwrap_or_default() { - return MigrationState::Idle; - } - - // if migration has already started no other states possible - if snapshot.migration.is_some() { - return MigrationState::Started; - } - - // we only require migration if set actually changes - // when only address changes, we could simply adjust connections - if !is_migration_required(&snapshot.current_set, &snapshot.new_set) { - return MigrationState::Idle; - } - - return MigrationState::Required; -} - -fn session_state(session: Option>) -> SessionState { - session - .and_then(|s| match s.as_servers_set_change() { - Some(s) if !s.is_finished() => Some(SessionState::Active(s.migration_id().cloned())), - Some(s) => match s.result() { - Some(Ok(_)) => Some(SessionState::Finished(s.migration_id().cloned())), - Some(Err(_)) => Some(SessionState::Failed(s.migration_id().cloned())), - None => unreachable!("s.is_finished() == true; when session is finished, result is available; qed"), - }, - None => None, - }) - .unwrap_or(SessionState::Idle) -} - -fn maintain_session(self_node_id: &NodeId, connected: &BTreeSet, snapshot: &KeyServerSetSnapshot, migration_state: MigrationState, session_state: SessionState) -> Option { - let migration_data_proof = "migration_state is Started; migration data available when started; qed"; - - match (migration_state, session_state) { - // === NORMAL combinations === - - // having no session when it is not required => ok - (MigrationState::Idle, SessionState::Idle) => None, - // migration is required && no active session => start migration - (MigrationState::Required, SessionState::Idle) => { - match select_master_node(snapshot) == self_node_id { - true => Some(SessionAction::StartMigration(H256::random())), - // we are not on master node - false => None, - } - }, - // migration is active && there's no active session => start it - (MigrationState::Started, SessionState::Idle) => { - match is_connected_to_all_nodes(self_node_id, &snapshot.migration.as_ref().expect(migration_data_proof).set, connected) && - select_master_node(snapshot) == self_node_id { - true => Some(SessionAction::Start), - // we are not connected to all required nodes yet or we are not on master node => wait for it - false => None, - } - }, - // migration is active && session is not yet started/finished => ok - (MigrationState::Started, SessionState::Active(ref session_migration_id)) - if snapshot.migration.as_ref().expect(migration_data_proof).id == session_migration_id.unwrap_or_default() => - None, - // migration has finished => confirm migration - (MigrationState::Started, SessionState::Finished(ref session_migration_id)) - if snapshot.migration.as_ref().expect(migration_data_proof).id == session_migration_id.unwrap_or_default() => - match snapshot.migration.as_ref().expect(migration_data_proof).set.contains_key(self_node_id) { - true => Some(SessionAction::ConfirmAndDrop( - snapshot.migration.as_ref().expect(migration_data_proof).id.clone() - )), - // we are not on migration set => we do not need to confirm - false => Some(SessionAction::Drop), - }, - // migration has failed => it should be dropped && restarted later - (MigrationState::Started, SessionState::Failed(ref session_migration_id)) - if snapshot.migration.as_ref().expect(migration_data_proof).id == session_migration_id.unwrap_or_default() => - Some(SessionAction::Drop), - - // ABNORMAL combinations, which are still possible when contract misbehaves === - - // having active session when it is not required => drop it && wait for other tasks - (MigrationState::Idle, SessionState::Active(_)) | - // no migration required && there's finished session => drop it && wait for other tasks - (MigrationState::Idle, SessionState::Finished(_)) | - // no migration required && there's failed session => drop it && wait for other tasks - (MigrationState::Idle, SessionState::Failed(_)) | - // migration is required && session is active => drop it && wait for other tasks - (MigrationState::Required, SessionState::Active(_)) | - // migration is required && session has failed => we need to forget this obsolete session and retry - (MigrationState::Required, SessionState::Finished(_)) | - // session for other migration is active => we need to forget this obsolete session and retry - // (the case for same id is checked above) - (MigrationState::Started, SessionState::Active(_)) | - // session for other migration has finished => we need to forget this obsolete session and retry - // (the case for same id is checked above) - (MigrationState::Started, SessionState::Finished(_)) | - // session for other migration has failed => we need to forget this obsolete session and retry - // (the case for same id is checked above) - (MigrationState::Started, SessionState::Failed(_)) | - // migration is required && session has failed => we need to forget this obsolete session and retry - (MigrationState::Required, SessionState::Failed(_)) => { - // some of the cases above could happen because of lags (could actually be a non-abnormal behavior) - // => we ony trace here - trace!(target: "secretstore_net", "{}: suspicious auto-migration state: {:?}", - self_node_id, (migration_state, session_state)); - Some(SessionAction::DropAndRetry) - }, - } -} - -fn maintain_connections(migration_state: MigrationState, session_state: SessionState) -> Option { - match (migration_state, session_state) { - // session is active => we do not alter connections when session is active - (_, SessionState::Active(_)) => None, - // when no migration required => we just keep us connected to old nodes set - (MigrationState::Idle, _) => Some(ConnectionsAction::ConnectToCurrentSet), - // when migration is either scheduled, or in progress => connect to both old and migration set. - // this could lead to situation when node is not 'officially' a part of KeyServer (i.e. it is not in current_set) - // but it participates in new key generation session - // it is ok, since 'officialy' here means that this node is a owner of all old shares - (MigrationState::Required, _) | - (MigrationState::Started, _) => Some(ConnectionsAction::ConnectToMigrationSet), - } -} - -fn is_connected_to_all_nodes(self_node_id: &NodeId, nodes: &BTreeMap, connected: &BTreeSet) -> bool { - nodes.keys() - .filter(|n| *n != self_node_id) - .all(|n| connected.contains(n)) -} - -fn select_master_node(snapshot: &KeyServerSetSnapshot) -> &NodeId { - // we want to minimize a number of UnknownSession messages => - // try to select a node which was in SS && will be in SS - match snapshot.migration.as_ref() { - Some(migration) => &migration.master, - None => snapshot.current_set.keys() - .filter(|n| snapshot.new_set.contains_key(n)) - .nth(0) - .or_else(|| snapshot.new_set.keys().nth(0)) - .unwrap_or_else(|| snapshot.current_set.keys().nth(0) - .expect("select_master_node is only called when migration is Required or Started;\ - when Started: migration.is_some() && we return migration.master; qed;\ - when Required: current_set != new_set; this means that at least one set is non-empty; we try to take node from each set; qed")) - } -} - -#[cfg(test)] -mod tests { - use key_server_cluster::{KeyServerSetSnapshot, KeyServerSetMigration}; - use key_server_cluster::connection_trigger::ConnectionsAction; - use super::{MigrationState, SessionState, SessionAction, migration_state, maintain_session, - maintain_connections, select_master_node}; - use ethereum_types::{H256, H512}; - - #[test] - fn migration_state_is_idle_when_required_but_this_node_is_not_on_the_list() { - assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(2), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(3), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - migration: None, - }), MigrationState::Idle); - } - - #[test] - fn migration_state_is_idle_when_sets_are_equal() { - assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - migration: None, - }), MigrationState::Idle); - } - - #[test] - fn migration_state_is_idle_when_only_address_changes() { - assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - migration: None, - }), MigrationState::Idle); - } - - #[test] - fn migration_state_is_required_when_node_is_added() { - assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - migration: None, - }), MigrationState::Required); - } - - #[test] - fn migration_state_is_required_when_node_is_removed() { - assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8081".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), - migration: None, - }), MigrationState::Required); - } - - #[test] - fn migration_state_is_started_when_migration_is_some() { - assert_eq!(migration_state(&H512::from_low_u64_be(1), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8080".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - id: Default::default(), - set: Default::default(), - master: Default::default(), - is_confirmed: Default::default(), - }), - }), MigrationState::Started); - } - - #[test] - fn existing_master_is_selected_when_migration_has_started() { - assert_eq!(select_master_node(&KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8180".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(3), - ..Default::default() - }), - }), &H512::from_low_u64_be(3)); - } - - #[test] - fn persistent_master_is_selected_when_migration_has_not_started_yet() { - assert_eq!(select_master_node(&KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8180".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8180".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(4), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - migration: None, - }), &H512::from_low_u64_be(2)); - } - - #[test] - fn new_master_is_selected_in_worst_case() { - assert_eq!(select_master_node(&KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8180".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8180".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(3), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(4), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - migration: None, - }), &H512::from_low_u64_be(3)); - } - - #[test] - fn maintain_connections_returns_none_when_session_is_active() { - assert_eq!(maintain_connections(MigrationState::Required, - SessionState::Active(Default::default())), None); - } - - #[test] - fn maintain_connections_connects_to_current_set_when_no_migration() { - assert_eq!(maintain_connections(MigrationState::Idle, - SessionState::Idle), Some(ConnectionsAction::ConnectToCurrentSet)); - } - - #[test] - fn maintain_connections_connects_to_current_and_old_set_when_migration_is_required() { - assert_eq!(maintain_connections(MigrationState::Required, - SessionState::Idle), Some(ConnectionsAction::ConnectToMigrationSet)); - } - - #[test] - fn maintain_connections_connects_to_current_and_old_set_when_migration_is_started() { - assert_eq!(maintain_connections(MigrationState::Started, - SessionState::Idle), Some(ConnectionsAction::ConnectToMigrationSet)); - } - - #[test] - fn maintain_sessions_does_nothing_if_no_session_and_no_migration() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &Default::default(), &Default::default(), - MigrationState::Idle, SessionState::Idle), None); - } - - #[test] - fn maintain_session_does_nothing_when_migration_required_on_slave_node_and_no_session() { - assert_eq!(maintain_session(&H512::from_low_u64_be(2), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - migration: None, - }, MigrationState::Required, SessionState::Idle), None); - } - - #[test] - fn maintain_session_does_nothing_when_migration_started_on_slave_node_and_no_session() { - assert_eq!(maintain_session(&H512::from_low_u64_be(2), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(1), - set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Idle), None); - } - - #[test] - fn maintain_session_does_nothing_when_migration_started_on_master_node_and_no_session_and_not_connected_to_migration_nodes() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &Default::default(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(1), - set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Idle), None); - } - - #[test] - fn maintain_session_starts_session_when_migration_started_on_master_node_and_no_session() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(1), - set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Idle), Some(SessionAction::Start)); - } - - #[test] - fn maintain_session_does_nothing_when_both_migration_and_session_are_started() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(1), - set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Active(Default::default())), None); - } - - #[test] - fn maintain_session_confirms_migration_when_active_and_session_has_finished_on_new_node() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(1), - set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Finished(Default::default())), Some(SessionAction::ConfirmAndDrop(Default::default()))); - } - - #[test] - fn maintain_session_drops_session_when_active_and_session_has_finished_on_removed_node() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(2), - set: vec![(H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Finished(Default::default())), Some(SessionAction::Drop)); - } - - #[test] - fn maintain_session_drops_session_when_active_and_session_has_failed() { - assert_eq!(maintain_session(&H512::from_low_u64_be(1), &vec![H512::from_low_u64_be(2)].into_iter().collect(), &KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - new_set: Default::default(), - migration: Some(KeyServerSetMigration { - master: H512::from_low_u64_be(1), - set: vec![(H512::from_low_u64_be(1), "127.0.0.1:8181".parse().unwrap()), - (H512::from_low_u64_be(2), "127.0.0.1:8181".parse().unwrap())].into_iter().collect(), - ..Default::default() - }), - }, MigrationState::Started, SessionState::Failed(Default::default())), Some(SessionAction::Drop)); - } - - #[test] - fn maintain_session_detects_abnormal_when_no_migration_and_active_session() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &Default::default(), - MigrationState::Idle, SessionState::Active(Default::default())), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_no_migration_and_finished_session() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &Default::default(), - MigrationState::Idle, SessionState::Finished(Default::default())), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_no_migration_and_failed_session() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &Default::default(), - MigrationState::Idle, SessionState::Failed(Default::default())), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_required_migration_and_active_session() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &Default::default(), - MigrationState::Required, SessionState::Active(Default::default())), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_required_migration_and_finished_session() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &Default::default(), - MigrationState::Required, SessionState::Finished(Default::default())), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_required_migration_and_failed_session() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &Default::default(), - MigrationState::Required, SessionState::Failed(Default::default())), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_active_migration_and_active_session_with_different_id() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &KeyServerSetSnapshot { - migration: Some(KeyServerSetMigration { - id: H256::zero(), - ..Default::default() - }), - ..Default::default() - }, MigrationState::Started, SessionState::Active(Some(H256::from_low_u64_be(1)))), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_active_migration_and_finished_session_with_different_id() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &KeyServerSetSnapshot { - migration: Some(KeyServerSetMigration { - id: H256::zero(), - ..Default::default() - }), - ..Default::default() - }, MigrationState::Started, SessionState::Finished(Some(H256::from_low_u64_be(1)))), Some(SessionAction::DropAndRetry)); - } - - #[test] - fn maintain_session_detects_abnormal_when_active_migration_and_failed_session_with_different_id() { - assert_eq!(maintain_session(&Default::default(), &Default::default(), &KeyServerSetSnapshot { - migration: Some(KeyServerSetMigration { - id: H256::zero(), - ..Default::default() - }), - ..Default::default() - }, MigrationState::Started, SessionState::Failed(Some(H256::from_low_u64_be(1)))), Some(SessionAction::DropAndRetry)); - } -} diff --git a/secret-store/src/key_server_cluster/io/deadline.rs b/secret-store/src/key_server_cluster/io/deadline.rs deleted file mode 100644 index cb960a514c1..00000000000 --- a/secret-store/src/key_server_cluster/io/deadline.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io; -use std::time::Duration; -use futures::{Future, Poll}; -use tokio::timer::timeout::{Timeout, Error as TimeoutError}; - -type DeadlineBox = Box::Item>, - Error = TimeoutError<::Error> -> + Send>; - -/// Complete a passed future or fail if it is not completed within timeout. -pub fn deadline(duration: Duration, future: F) -> Result, io::Error> - where F: Future + Send + 'static, T: Send + 'static -{ - let timeout = Box::new(Timeout::new(future, duration) - .then(|res| { - match res { - Ok(fut) => Ok(DeadlineStatus::Meet(fut)), - Err(err) => { - if err.is_elapsed() { - Ok(DeadlineStatus::Timeout) - } else { - Err(err) - } - }, - } - }) - ); - let deadline = Deadline { - future: timeout, - }; - Ok(deadline) -} - -/// Deadline future completion status. -#[derive(Debug, PartialEq)] -pub enum DeadlineStatus { - /// Completed a future. - Meet(T), - /// Faled with timeout. - Timeout, -} - -/// Future, which waits for passed future completion within given period, or fails with timeout. -pub struct Deadline where F: Future { - future: DeadlineBox, -} - -impl Future for Deadline where F: Future { - type Item = DeadlineStatus; - type Error = TimeoutError; - - fn poll(&mut self) -> Poll { - self.future.poll() - } -} - -#[cfg(test)] -mod tests { - use std::time::Duration; - use futures::{Future, done}; - use tokio::reactor::Reactor; - use super::{deadline, DeadlineStatus}; - - #[test] - fn deadline_result_works() { - let mut reactor = Reactor::new().unwrap(); - let deadline = deadline(Duration::from_millis(1000), done(Ok(()))).unwrap(); - reactor.turn(Some(Duration::from_millis(3))).unwrap(); - assert_eq!(deadline.wait().unwrap(), DeadlineStatus::Meet(())); - } -} diff --git a/secret-store/src/key_server_cluster/io/handshake.rs b/secret-store/src/key_server_cluster/io/handshake.rs deleted file mode 100644 index 2e7cbd14e42..00000000000 --- a/secret-store/src/key_server_cluster/io/handshake.rs +++ /dev/null @@ -1,383 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -///! Given: two nodes each holding its own `self_key_pair`. -///! -///! Handshake process: -///! 1) both nodes are generating random `KeyPair` (`session_key_pair`), which will be used for channel encryption -///! 2) both nodes are generating random H256 (`confirmation_plain`) -///! 3) both nodes are signing `confirmation_plain` using `session_key_pair` to receive `confirmation_signed_session` -///! 4) nodes exchange with `NodePublicKey` messages, containing: `self_key_pair.public`, `confirmation_plain`, `confirmation_signed_session` -///! 5) both nodes are checking that they're configured to communicate to server with received `message.self_key_pair.public`. Connection is closed otherwise -///! 6) both nodes are recovering peer' `session_key_pair.public` from `message.confirmation_plain` and `message.confirmation_signed_session` -///! 7) both nodes are computing shared session key pair using self' `session_key_pair.secret` && peer' `session_key_pair.public`. All following messages are encrypted using this key_pair. -///! 8) both nodes are signing `message.confirmation_plain` with their own `self_key_pair.private` to receive `confirmation_signed` -///! 9) nodes exchange with `NodePrivateKeySignature` messages, containing `confirmation_signed` -///! 10) both nodes are checking that `confirmation_signed` is actually signed with the owner of peer' `self_key_pair.secret` -///! -///! Result of handshake is: -///! 1) belief, that we are connected to the KS from our KS-set -///! 2) session key pair, which is used to enrypt all connection messages - -use std::io; -use std::sync::Arc; -use std::collections::BTreeSet; -use futures::{Future, Poll, Async}; -use tokio_io::{AsyncRead, AsyncWrite}; -use crypto::publickey::ecdh::agree; -use crypto::publickey::{Random, Generator, KeyPair, Public, Signature, verify_public, sign, recover}; -use ethereum_types::H256; -use blockchain::SigningKeyPair; -use key_server_cluster::{NodeId, Error}; -use key_server_cluster::message::{Message, ClusterMessage, NodePublicKey, NodePrivateKeySignature}; -use key_server_cluster::io::{write_message, write_encrypted_message, WriteMessage, ReadMessage, - read_message, read_encrypted_message, fix_shared_key}; - -/// Start handshake procedure with another node from the cluster. -pub fn handshake(a: A, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { - let init_data = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into) - .and_then(|cp| Random.generate().map(|kp| (cp, kp)).map_err(Into::into)); - handshake_with_init_data(a, init_data, self_key_pair, trusted_nodes) -} - -/// Start handshake procedure with another node from the cluster and given plain confirmation + session key pair. -pub fn handshake_with_init_data(a: A, init_data: Result<(H256, KeyPair), Error>, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Handshake where A: AsyncWrite + AsyncRead { - let handshake_input_data = init_data - .and_then(|(cp, kp)| sign(kp.secret(), &cp).map(|sp| (cp, kp, sp)).map_err(Into::into)) - .and_then(|(cp, kp, sp)| Handshake::::make_public_key_message(self_key_pair.public().clone(), cp.clone(), sp).map(|msg| (cp, kp, msg))); - - let (error, cp, kp, state) = match handshake_input_data { - Ok((cp, kp, msg)) => (None, cp, Some(kp), HandshakeState::SendPublicKey(write_message(a, msg))), - Err(err) => (Some((a, Err(err))), Default::default(), None, HandshakeState::Finished), - }; - - Handshake { - is_active: true, - error: error, - state: state, - self_key_pair: self_key_pair, - self_session_key_pair: kp, - self_confirmation_plain: cp, - trusted_nodes: Some(trusted_nodes), - peer_node_id: None, - peer_session_public: None, - peer_confirmation_plain: None, - shared_key: None, - } -} - -/// Wait for handshake procedure to be started by another node from the cluster. -pub fn accept_handshake(a: A, self_key_pair: Arc) -> Handshake where A: AsyncWrite + AsyncRead { - let self_confirmation_plain = Random.generate().map(|kp| *kp.secret().clone()).map_err(Into::into); - let handshake_input_data = self_confirmation_plain - .and_then(|cp| Random.generate().map(|kp| (cp, kp)).map_err(Into::into)); - - let (error, cp, kp, state) = match handshake_input_data { - Ok((cp, kp)) => (None, cp, Some(kp), HandshakeState::ReceivePublicKey(read_message(a))), - Err(err) => (Some((a, Err(err))), Default::default(), None, HandshakeState::Finished), - }; - - Handshake { - is_active: false, - error: error, - state: state, - self_key_pair: self_key_pair, - self_session_key_pair: kp, - self_confirmation_plain: cp, - trusted_nodes: None, - peer_node_id: None, - peer_session_public: None, - peer_confirmation_plain: None, - shared_key: None, - } -} - -/// Result of handshake procedure. -#[derive(Debug, PartialEq)] -pub struct HandshakeResult { - /// Node id. - pub node_id: NodeId, - /// Shared key. - pub shared_key: KeyPair, -} - -/// Future handshake procedure. -pub struct Handshake { - is_active: bool, - error: Option<(A, Result)>, - state: HandshakeState, - self_key_pair: Arc, - self_session_key_pair: Option, - self_confirmation_plain: H256, - trusted_nodes: Option>, - peer_node_id: Option, - peer_session_public: Option, - peer_confirmation_plain: Option, - shared_key: Option, -} - -/// Active handshake state. -enum HandshakeState { - SendPublicKey(WriteMessage), - ReceivePublicKey(ReadMessage), - SendPrivateKeySignature(WriteMessage), - ReceivePrivateKeySignature(ReadMessage), - Finished, -} - -impl Handshake where A: AsyncRead + AsyncWrite { - #[cfg(test)] - pub fn set_self_confirmation_plain(&mut self, self_confirmation_plain: H256) { - self.self_confirmation_plain = self_confirmation_plain; - } - - #[cfg(test)] - pub fn set_self_session_key_pair(&mut self, self_session_key_pair: KeyPair) { - self.self_session_key_pair = Some(self_session_key_pair); - } - - pub fn make_public_key_message(self_node_id: NodeId, confirmation_plain: H256, confirmation_signed_session: Signature) -> Result { - Ok(Message::Cluster(ClusterMessage::NodePublicKey(NodePublicKey { - node_id: self_node_id.into(), - confirmation_plain: confirmation_plain.into(), - confirmation_signed_session: confirmation_signed_session.into(), - }))) - } - - fn make_private_key_signature_message(self_key_pair: &dyn SigningKeyPair, confirmation_plain: &H256) -> Result { - Ok(Message::Cluster(ClusterMessage::NodePrivateKeySignature(NodePrivateKeySignature { - confirmation_signed: self_key_pair.sign(confirmation_plain)?.into(), - }))) - } - - fn compute_shared_key(self_session_key_pair: &KeyPair, peer_session_public: &Public) -> Result { - agree(self_session_key_pair.secret(), peer_session_public) - .map_err(Into::into) - .and_then(|s| fix_shared_key(&s)) - } -} - -impl Future for Handshake where A: AsyncRead + AsyncWrite { - type Item = (A, Result); - type Error = io::Error; - - fn poll(&mut self) -> Poll { - if let Some(error_result) = self.error.take() { - return Ok(error_result.into()); - } - - let (next, result) = match self.state { - HandshakeState::SendPublicKey(ref mut future) => { - let (stream, _) = try_ready!(future.poll()); - - if self.is_active { - (HandshakeState::ReceivePublicKey( - read_message(stream) - ), Async::NotReady) - } else { - let shared_key = Self::compute_shared_key( - self.self_session_key_pair.as_ref().expect( - "self_session_key_pair is not filled only when initialization has failed; if initialization has failed, self.error.is_some(); qed"), - self.peer_session_public.as_ref().expect( - "we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; peer_session_public is filled in ReceivePublicKey; qed"), - ); - - self.shared_key = match shared_key { - Ok(shared_key) => Some(shared_key), - Err(err) => return Ok((stream, Err(err)).into()), - }; - - let peer_confirmation_plain = self.peer_confirmation_plain.as_ref() - .expect("we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; peer_confirmation_plain is filled in ReceivePublicKey; qed"); - let message = match Handshake::::make_private_key_signature_message(&*self.self_key_pair, peer_confirmation_plain) { - Ok(message) => message, - Err(err) => return Ok((stream, Err(err)).into()), - }; - - (HandshakeState::SendPrivateKeySignature(write_encrypted_message(stream, - self.shared_key.as_ref().expect("filled couple of lines above; qed"), - message)), Async::NotReady) - } - }, - HandshakeState::ReceivePublicKey(ref mut future) => { - let (stream, message) = try_ready!(future.poll()); - - let message = match message { - Ok(message) => match message { - Message::Cluster(ClusterMessage::NodePublicKey(message)) => message, - _ => return Ok((stream, Err(Error::InvalidMessage)).into()), - }, - Err(err) => return Ok((stream, Err(err.into())).into()), - }; - - if !self.trusted_nodes.as_ref().map(|tn| tn.contains(&*message.node_id)).unwrap_or(true) { - return Ok((stream, Err(Error::InvalidNodeId)).into()); - } - - self.peer_node_id = Some(message.node_id.into()); - self.peer_session_public = Some(match recover(&message.confirmation_signed_session, &message.confirmation_plain) { - Ok(peer_session_public) => peer_session_public, - Err(err) => return Ok((stream, Err(err.into())).into()), - }); - self.peer_confirmation_plain = Some(message.confirmation_plain.into()); - if self.is_active { - let shared_key = Self::compute_shared_key( - self.self_session_key_pair.as_ref().expect( - "self_session_key_pair is not filled only when initialization has failed; if initialization has failed, self.error.is_some(); qed"), - self.peer_session_public.as_ref().expect( - "we are in passive mode; in passive mode SendPublicKey follows ReceivePublicKey; peer_session_public is filled in ReceivePublicKey; qed"), - ); - - self.shared_key = match shared_key { - Ok(shared_key) => Some(shared_key), - Err(err) => return Ok((stream, Err(err)).into()), - }; - - let peer_confirmation_plain = self.peer_confirmation_plain.as_ref() - .expect("filled couple of lines above; qed"); - let message = match Handshake::::make_private_key_signature_message(&*self.self_key_pair, peer_confirmation_plain) { - Ok(message) => message, - Err(err) => return Ok((stream, Err(err)).into()), - }; - - (HandshakeState::SendPrivateKeySignature(write_encrypted_message(stream, - self.shared_key.as_ref().expect("filled couple of lines above; qed"), - message)), Async::NotReady) - } else { - let self_session_key_pair = self.self_session_key_pair.as_ref() - .expect("self_session_key_pair is not filled only when initialization has failed; if initialization has failed, self.error.is_some(); qed"); - let confirmation_signed_session = match sign(self_session_key_pair.secret(), &self.self_confirmation_plain).map_err(Into::into) { - Ok(confirmation_signed_session) => confirmation_signed_session, - Err(err) => return Ok((stream, Err(err)).into()), - }; - - let message = match Handshake::::make_public_key_message(self.self_key_pair.public().clone(), self.self_confirmation_plain.clone(), confirmation_signed_session) { - Ok(message) => message, - Err(err) => return Ok((stream, Err(err)).into()), - }; - (HandshakeState::SendPublicKey(write_message(stream, message)), Async::NotReady) - } - }, - HandshakeState::SendPrivateKeySignature(ref mut future) => { - let (stream, _) = try_ready!(future.poll()); - - (HandshakeState::ReceivePrivateKeySignature( - read_encrypted_message(stream, - self.shared_key.as_ref().expect("shared_key is filled in Send/ReceivePublicKey; SendPrivateKeySignature follows Send/ReceivePublicKey; qed").clone() - ) - ), Async::NotReady) - }, - HandshakeState::ReceivePrivateKeySignature(ref mut future) => { - let (stream, message) = try_ready!(future.poll()); - - let message = match message { - Ok(message) => match message { - Message::Cluster(ClusterMessage::NodePrivateKeySignature(message)) => message, - _ => return Ok((stream, Err(Error::InvalidMessage)).into()), - }, - Err(err) => return Ok((stream, Err(err.into())).into()), - }; - - let peer_public = self.peer_node_id.as_ref().expect("peer_node_id is filled in ReceivePublicKey; ReceivePrivateKeySignature follows ReceivePublicKey; qed"); - if !verify_public(peer_public, &*message.confirmation_signed, &self.self_confirmation_plain).unwrap_or(false) { - return Ok((stream, Err(Error::InvalidMessage)).into()); - } - - (HandshakeState::Finished, Async::Ready((stream, Ok(HandshakeResult { - node_id: self.peer_node_id.expect("peer_node_id is filled in ReceivePublicKey; ReceivePrivateKeySignature follows ReceivePublicKey; qed"), - shared_key: self.shared_key.clone().expect("shared_key is filled in Send/ReceivePublicKey; ReceivePrivateKeySignature follows Send/ReceivePublicKey; qed"), - })))) - }, - HandshakeState::Finished => panic!("poll Handshake after it's done"), - }; - - self.state = next; - match result { - // by polling again, we register new future - Async::NotReady => self.poll(), - result => Ok(result) - } - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::collections::BTreeSet; - use futures::Future; - use crypto::publickey::{Random, Generator, sign}; - use ethereum_types::H256; - use key_server_cluster::PlainNodeKeyPair; - use key_server_cluster::io::message::tests::TestIo; - use key_server_cluster::message::{Message, ClusterMessage, NodePublicKey, NodePrivateKeySignature}; - use super::{handshake_with_init_data, accept_handshake, HandshakeResult}; - - fn prepare_test_io() -> (H256, TestIo) { - let mut io = TestIo::new(); - - let self_confirmation_plain = *Random.generate().unwrap().secret().clone(); - let peer_confirmation_plain = *Random.generate().unwrap().secret().clone(); - - let self_confirmation_signed = sign(io.peer_key_pair().secret(), &self_confirmation_plain).unwrap(); - let peer_confirmation_signed = sign(io.peer_session_key_pair().secret(), &peer_confirmation_plain).unwrap(); - - let peer_public = io.peer_key_pair().public().clone(); - io.add_input_message(Message::Cluster(ClusterMessage::NodePublicKey(NodePublicKey { - node_id: peer_public.into(), - confirmation_plain: peer_confirmation_plain.into(), - confirmation_signed_session: peer_confirmation_signed.into(), - }))); - io.add_encrypted_input_message(Message::Cluster(ClusterMessage::NodePrivateKeySignature(NodePrivateKeySignature { - confirmation_signed: self_confirmation_signed.into(), - }))); - - (self_confirmation_plain, io) - } - - #[test] - fn active_handshake_works() { - let (self_confirmation_plain, io) = prepare_test_io(); - let trusted_nodes: BTreeSet<_> = vec![io.peer_key_pair().public().clone()].into_iter().collect(); - let self_session_key_pair = io.self_session_key_pair().clone(); - let self_key_pair = Arc::new(PlainNodeKeyPair::new(io.self_key_pair().clone())); - let shared_key = io.shared_key_pair().clone(); - - let handshake = handshake_with_init_data(io, Ok((self_confirmation_plain, self_session_key_pair)), self_key_pair, trusted_nodes); - let handshake_result = handshake.wait().unwrap(); - assert_eq!(handshake_result.1, Ok(HandshakeResult { - node_id: handshake_result.0.peer_key_pair().public().clone(), - shared_key: shared_key, - })); - } - - #[test] - fn passive_handshake_works() { - let (self_confirmation_plain, io) = prepare_test_io(); - let self_key_pair = Arc::new(PlainNodeKeyPair::new(io.self_key_pair().clone())); - let self_session_key_pair = io.self_session_key_pair().clone(); - let shared_key = io.shared_key_pair().clone(); - - let mut handshake = accept_handshake(io, self_key_pair); - handshake.set_self_confirmation_plain(self_confirmation_plain); - handshake.set_self_session_key_pair(self_session_key_pair); - - let handshake_result = handshake.wait().unwrap(); - assert_eq!(handshake_result.1, Ok(HandshakeResult { - node_id: handshake_result.0.peer_key_pair().public().clone(), - shared_key: shared_key, - })); - } -} diff --git a/secret-store/src/key_server_cluster/io/message.rs b/secret-store/src/key_server_cluster/io/message.rs deleted file mode 100644 index 34ed722fedb..00000000000 --- a/secret-store/src/key_server_cluster/io/message.rs +++ /dev/null @@ -1,429 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io::Cursor; -use std::u16; -use std::ops::Deref; -use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use serde_json; -use crypto::publickey::ecies; -use crypto::publickey::{Secret, KeyPair}; -use crypto::publickey::ec_math_utils::CURVE_ORDER; -use ethereum_types::{H256, U256, BigEndianHash}; -use key_server_cluster::Error; -use key_server_cluster::message::{Message, ClusterMessage, GenerationMessage, EncryptionMessage, DecryptionMessage, - SchnorrSigningMessage, EcdsaSigningMessage, ServersSetChangeMessage, ShareAddMessage, KeyVersionNegotiationMessage}; - -/// Size of serialized header. -pub const MESSAGE_HEADER_SIZE: usize = 18; -/// Current header version. -pub const CURRENT_HEADER_VERSION: u64 = 1; - -/// Message header. -#[derive(Debug, PartialEq)] -pub struct MessageHeader { - /// Message/Header version. - pub version: u64, - /// Message kind. - pub kind: u64, - /// Message payload size (without header). - pub size: u16, -} - -/// Serialized message. -#[derive(Debug, Clone, PartialEq)] -pub struct SerializedMessage(Vec); - -impl Deref for SerializedMessage { - type Target = [u8]; - - fn deref(&self) -> &[u8] { - &self.0 - } -} - -impl Into> for SerializedMessage { - fn into(self) -> Vec { - self.0 - } -} - -/// Serialize message. -pub fn serialize_message(message: Message) -> Result { - let (message_kind, payload) = match message { - Message::Cluster(ClusterMessage::NodePublicKey(payload)) => (1, serde_json::to_vec(&payload)), - Message::Cluster(ClusterMessage::NodePrivateKeySignature(payload)) => (2, serde_json::to_vec(&payload)), - Message::Cluster(ClusterMessage::KeepAlive(payload)) => (3, serde_json::to_vec(&payload)), - Message::Cluster(ClusterMessage::KeepAliveResponse(payload)) => (4, serde_json::to_vec(&payload)), - - Message::Generation(GenerationMessage::InitializeSession(payload)) => (50, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::ConfirmInitialization(payload)) => (51, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::CompleteInitialization(payload)) => (52, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::KeysDissemination(payload)) => (53, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::PublicKeyShare(payload)) => (54, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::SessionError(payload)) => (55, serde_json::to_vec(&payload)), - Message::Generation(GenerationMessage::SessionCompleted(payload)) => (56, serde_json::to_vec(&payload)), - - Message::Encryption(EncryptionMessage::InitializeEncryptionSession(payload)) => (100, serde_json::to_vec(&payload)), - Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(payload)) => (101, serde_json::to_vec(&payload)), - Message::Encryption(EncryptionMessage::EncryptionSessionError(payload)) => (102, serde_json::to_vec(&payload)), - - Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(payload)) => (150, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::RequestPartialDecryption(payload)) => (151, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::PartialDecryption(payload)) => (152, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::DecryptionSessionError(payload)) => (153, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(payload)) => (154, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::DecryptionSessionDelegation(payload)) => (155, serde_json::to_vec(&payload)), - Message::Decryption(DecryptionMessage::DecryptionSessionDelegationCompleted(payload)) - => (156, serde_json::to_vec(&payload)), - - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(payload)) - => (200, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningGenerationMessage(payload)) - => (201, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrRequestPartialSignature(payload)) - => (202, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrPartialSignature(payload)) => (203, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(payload)) => (204, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionCompleted(payload)) - => (205, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(payload)) - => (206, serde_json::to_vec(&payload)), - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(payload)) - => (207, serde_json::to_vec(&payload)), - - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(payload)) - => (250, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::UnknownSessionsRequest(payload)) => (251, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::UnknownSessions(payload)) => (252, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(payload)) - => (253, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::InitializeShareChangeSession(payload)) - => (254, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(payload)) - => (255, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeDelegate(payload)) - => (256, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeDelegateResponse(payload)) - => (257, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeShareAddMessage(payload)) - => (258, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeError(payload)) => (261, serde_json::to_vec(&payload)), - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeCompleted(payload)) - => (262, serde_json::to_vec(&payload)), - - Message::ShareAdd(ShareAddMessage::ShareAddConsensusMessage(payload)) => (300, serde_json::to_vec(&payload)), - Message::ShareAdd(ShareAddMessage::KeyShareCommon(payload)) => (301, serde_json::to_vec(&payload)), - Message::ShareAdd(ShareAddMessage::NewKeysDissemination(payload)) => (302, serde_json::to_vec(&payload)), - Message::ShareAdd(ShareAddMessage::ShareAddError(payload)) => (303, serde_json::to_vec(&payload)), - - Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::RequestKeyVersions(payload)) - => (450, serde_json::to_vec(&payload)), - Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersions(payload)) - => (451, serde_json::to_vec(&payload)), - Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersionsError(payload)) - => (452, serde_json::to_vec(&payload)), - - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(payload)) => (500, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(payload)) - => (501, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(payload)) - => (502, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(payload)) - => (503, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(payload)) - => (504, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaRequestPartialSignature(payload)) => (505, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaPartialSignature(payload)) => (506, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(payload)) => (507, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionCompleted(payload)) => (508, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(payload)) => (509, serde_json::to_vec(&payload)), - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(payload)) - => (510, serde_json::to_vec(&payload)), - }; - - let payload = payload.map_err(|err| Error::Serde(err.to_string()))?; - build_serialized_message(MessageHeader { - kind: message_kind, - version: CURRENT_HEADER_VERSION, - size: 0, - }, payload) -} - -/// Deserialize message. -pub fn deserialize_message(header: &MessageHeader, payload: Vec) -> Result { - Ok(match header.kind { - 1 => Message::Cluster(ClusterMessage::NodePublicKey(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 2 => Message::Cluster(ClusterMessage::NodePrivateKeySignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 3 => Message::Cluster(ClusterMessage::KeepAlive(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 4 => Message::Cluster(ClusterMessage::KeepAliveResponse(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 50 => Message::Generation(GenerationMessage::InitializeSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 51 => Message::Generation(GenerationMessage::ConfirmInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 52 => Message::Generation(GenerationMessage::CompleteInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 53 => Message::Generation(GenerationMessage::KeysDissemination(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 54 => Message::Generation(GenerationMessage::PublicKeyShare(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 55 => Message::Generation(GenerationMessage::SessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 56 => Message::Generation(GenerationMessage::SessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 100 => Message::Encryption(EncryptionMessage::InitializeEncryptionSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 101 => Message::Encryption(EncryptionMessage::ConfirmEncryptionInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 102 => Message::Encryption(EncryptionMessage::EncryptionSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 150 => Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 151 => Message::Decryption(DecryptionMessage::RequestPartialDecryption(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 152 => Message::Decryption(DecryptionMessage::PartialDecryption(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 153 => Message::Decryption(DecryptionMessage::DecryptionSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 154 => Message::Decryption(DecryptionMessage::DecryptionSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 155 => Message::Decryption(DecryptionMessage::DecryptionSessionDelegation(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 156 => Message::Decryption(DecryptionMessage::DecryptionSessionDelegationCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 200 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 201 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningGenerationMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 202 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrRequestPartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 203 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrPartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 204 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 205 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 206 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 207 => Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 250 => Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 251 => Message::ServersSetChange(ServersSetChangeMessage::UnknownSessionsRequest(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 252 => Message::ServersSetChange(ServersSetChangeMessage::UnknownSessions(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 253 => Message::ServersSetChange(ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 254 => Message::ServersSetChange(ServersSetChangeMessage::InitializeShareChangeSession(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 255 => Message::ServersSetChange(ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 256 => Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeDelegate(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 257 => Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeDelegateResponse(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 258 => Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeShareAddMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 261 => Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 262 => Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 300 => Message::ShareAdd(ShareAddMessage::ShareAddConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 301 => Message::ShareAdd(ShareAddMessage::KeyShareCommon(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 302 => Message::ShareAdd(ShareAddMessage::NewKeysDissemination(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 303 => Message::ShareAdd(ShareAddMessage::ShareAddError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 450 => Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::RequestKeyVersions(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 451 => Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersions(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 452 => Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersionsError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - 500 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 501 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 502 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 503 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 504 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 505 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaRequestPartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 506 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaPartialSignature(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 507 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 508 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 509 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - 510 => Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(serde_json::from_slice(&payload).map_err(|err| Error::Serde(err.to_string()))?)), - - _ => return Err(Error::Serde(format!("unknown message type {}", header.kind))), - }) -} - -/// Encrypt serialized message. -pub fn encrypt_message(key: &KeyPair, message: SerializedMessage) -> Result { - let mut header: Vec<_> = message.into(); - let payload = header.split_off(MESSAGE_HEADER_SIZE); - let encrypted_payload = ecies::encrypt(key.public(), &[], &payload)?; - - let header = deserialize_header(&header)?; - build_serialized_message(header, encrypted_payload) -} - -/// Decrypt serialized message. -pub fn decrypt_message(key: &KeyPair, payload: Vec) -> Result, Error> { - Ok(ecies::decrypt(key.secret(), &[], &payload)?) -} - -/// Fix shared encryption key. -pub fn fix_shared_key(shared_secret: &Secret) -> Result { - // secret key created in agree function is invalid, as it is not calculated mod EC.field.n - // => let's do it manually - let shared_secret: H256 = (**shared_secret).into(); - let shared_secret: U256 = shared_secret.into_uint(); - let shared_secret: H256 = BigEndianHash::from_uint(&(shared_secret % *CURVE_ORDER)); - let shared_key_pair = KeyPair::from_secret_slice(shared_secret.as_bytes())?; - Ok(shared_key_pair) -} - -/// Serialize message header. -fn serialize_header(header: &MessageHeader) -> Result, Error> { - let mut buffer = Vec::with_capacity(MESSAGE_HEADER_SIZE); - buffer.write_u64::(header.version)?; - buffer.write_u64::(header.kind)?; - buffer.write_u16::(header.size)?; - Ok(buffer) -} - -/// Deserialize message header. -pub fn deserialize_header(data: &[u8]) -> Result { - let mut reader = Cursor::new(data); - let version = reader.read_u64::()?; - if version != CURRENT_HEADER_VERSION { - return Err(Error::InvalidMessageVersion); - } - - Ok(MessageHeader { - version: version, - kind: reader.read_u64::()?, - size: reader.read_u16::()?, - }) -} - -/// Build serialized message from header && payload -fn build_serialized_message(mut header: MessageHeader, payload: Vec) -> Result { - let payload_len = payload.len(); - if payload_len > u16::MAX as usize { - return Err(Error::InvalidMessage); - } - header.size = payload.len() as u16; - - let mut message = serialize_header(&header)?; - message.extend(payload); - Ok(SerializedMessage(message)) -} - -#[cfg(test)] -pub mod tests { - use std::io; - use futures::Poll; - use tokio_io::{AsyncRead, AsyncWrite}; - use crypto::publickey::{Random, Generator, KeyPair}; - use crypto::publickey::ecdh::agree; - use key_server_cluster::Error; - use key_server_cluster::message::Message; - use super::{MESSAGE_HEADER_SIZE, CURRENT_HEADER_VERSION, MessageHeader, fix_shared_key, encrypt_message, - serialize_message, serialize_header, deserialize_header}; - - pub struct TestIo { - self_key_pair: KeyPair, - self_session_key_pair: KeyPair, - peer_key_pair: KeyPair, - peer_session_key_pair: KeyPair, - shared_key_pair: KeyPair, - input_buffer: io::Cursor>, - } - - impl TestIo { - pub fn new() -> Self { - let self_session_key_pair = Random.generate().unwrap(); - let peer_session_key_pair = Random.generate().unwrap(); - let self_key_pair = Random.generate().unwrap(); - let peer_key_pair = Random.generate().unwrap(); - let shared_key_pair = fix_shared_key(&agree(self_session_key_pair.secret(), peer_session_key_pair.public()).unwrap()).unwrap(); - TestIo { - self_key_pair: self_key_pair, - self_session_key_pair: self_session_key_pair, - peer_key_pair: peer_key_pair, - peer_session_key_pair: peer_session_key_pair, - shared_key_pair: shared_key_pair, - input_buffer: io::Cursor::new(Vec::new()), - } - } - - pub fn self_key_pair(&self) -> &KeyPair { - &self.self_key_pair - } - - pub fn self_session_key_pair(&self) -> &KeyPair { - &self.self_session_key_pair - } - - pub fn peer_key_pair(&self) -> &KeyPair { - &self.peer_key_pair - } - - pub fn peer_session_key_pair(&self) -> &KeyPair { - &self.peer_session_key_pair - } - - pub fn shared_key_pair(&self) -> &KeyPair { - &self.shared_key_pair - } - - pub fn add_input_message(&mut self, message: Message) { - let serialized_message = serialize_message(message).unwrap(); - let serialized_message: Vec<_> = serialized_message.into(); - let input_buffer = self.input_buffer.get_mut(); - for b in serialized_message { - input_buffer.push(b); - } - } - - pub fn add_encrypted_input_message(&mut self, message: Message) { - let serialized_message = encrypt_message(&self.shared_key_pair, serialize_message(message).unwrap()).unwrap(); - let serialized_message: Vec<_> = serialized_message.into(); - let input_buffer = self.input_buffer.get_mut(); - for b in serialized_message { - input_buffer.push(b); - } - } - } - - impl AsyncRead for TestIo {} - - impl AsyncWrite for TestIo { - fn shutdown(&mut self) -> Poll<(), io::Error> { - Ok(().into()) - } - } - - impl io::Read for TestIo { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - io::Read::read(&mut self.input_buffer, buf) - } - } - - impl io::Write for TestIo { - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } - } - - #[test] - fn header_serialization_works() { - let header = MessageHeader { - kind: 1, - version: CURRENT_HEADER_VERSION, - size: 3, - }; - - let serialized_header = serialize_header(&header).unwrap(); - assert_eq!(serialized_header.len(), MESSAGE_HEADER_SIZE); - - let deserialized_header = deserialize_header(&serialized_header).unwrap(); - assert_eq!(deserialized_header, header); - } - - #[test] - fn deserializing_header_of_wrong_version_fails() { - let header = MessageHeader { - kind: 1, - version: CURRENT_HEADER_VERSION + 1, - size: 3, - }; - - assert_eq!(deserialize_header(&serialize_header(&header).unwrap()).unwrap_err(), Error::InvalidMessageVersion); - } -} diff --git a/secret-store/src/key_server_cluster/io/mod.rs b/secret-store/src/key_server_cluster/io/mod.rs deleted file mode 100644 index 0b5e71144eb..00000000000 --- a/secret-store/src/key_server_cluster/io/mod.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -mod deadline; -mod handshake; -mod message; -mod read_header; -mod read_payload; -mod read_message; -mod shared_tcp_stream; -mod write_message; - -pub use self::deadline::{deadline, Deadline, DeadlineStatus}; -pub use self::handshake::{handshake, accept_handshake, Handshake, HandshakeResult}; -pub use self::message::{MessageHeader, SerializedMessage, serialize_message, deserialize_message, - encrypt_message, fix_shared_key}; -pub use self::read_header::{read_header, ReadHeader}; -pub use self::read_payload::{read_payload, read_encrypted_payload, ReadPayload}; -pub use self::read_message::{read_message, read_encrypted_message, ReadMessage}; -pub use self::shared_tcp_stream::SharedTcpStream; -pub use self::write_message::{write_message, write_encrypted_message, WriteMessage}; diff --git a/secret-store/src/key_server_cluster/io/read_header.rs b/secret-store/src/key_server_cluster/io/read_header.rs deleted file mode 100644 index c484921b7d7..00000000000 --- a/secret-store/src/key_server_cluster/io/read_header.rs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io; -use futures::{Future, Poll, Async}; -use tokio_io::AsyncRead; -use tokio_io::io::{ReadExact, read_exact}; -use key_server_cluster::Error; -use key_server_cluster::io::message::{MESSAGE_HEADER_SIZE, MessageHeader, deserialize_header}; - -/// Create future for read single message header from the stream. -pub fn read_header(a: A) -> ReadHeader where A: AsyncRead { - ReadHeader { - reader: read_exact(a, [0; MESSAGE_HEADER_SIZE]), - } -} - -/// Future for read single message header from the stream. -pub struct ReadHeader { - reader: ReadExact, -} - -impl Future for ReadHeader where A: AsyncRead { - type Item = (A, Result); - type Error = io::Error; - - fn poll(&mut self) -> Poll { - let (read, data) = try_ready!(self.reader.poll()); - let header = deserialize_header(&data); - Ok(Async::Ready((read, header))) - } -} diff --git a/secret-store/src/key_server_cluster/io/read_message.rs b/secret-store/src/key_server_cluster/io/read_message.rs deleted file mode 100644 index 18402c27e92..00000000000 --- a/secret-store/src/key_server_cluster/io/read_message.rs +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io; -use futures::{Poll, Future, Async}; -use tokio_io::AsyncRead; -use crypto::publickey::KeyPair; -use key_server_cluster::Error; -use key_server_cluster::message::Message; -use key_server_cluster::io::{read_header, ReadHeader, read_payload, read_encrypted_payload, ReadPayload}; - -/// Create future for read single message from the stream. -pub fn read_message(a: A) -> ReadMessage where A: AsyncRead { - ReadMessage { - key: None, - state: ReadMessageState::ReadHeader(read_header(a)), - } -} - -/// Create future for read single encrypted message from the stream. -pub fn read_encrypted_message(a: A, key: KeyPair) -> ReadMessage where A: AsyncRead { - ReadMessage { - key: Some(key), - state: ReadMessageState::ReadHeader(read_header(a)), - } -} - -enum ReadMessageState { - ReadHeader(ReadHeader), - ReadPayload(ReadPayload), - Finished, -} - -/// Future for read single message from the stream. -pub struct ReadMessage { - key: Option, - state: ReadMessageState, -} - -impl Future for ReadMessage where A: AsyncRead { - type Item = (A, Result); - type Error = io::Error; - - fn poll(&mut self) -> Poll { - let (next, result) = match self.state { - ReadMessageState::ReadHeader(ref mut future) => { - let (read, header) = try_ready!(future.poll()); - let header = match header { - Ok(header) => header, - Err(err) => return Ok((read, Err(err)).into()), - }; - - let future = match self.key.take() { - Some(key) => read_encrypted_payload(read, header, key), - None => read_payload(read, header), - }; - let next = ReadMessageState::ReadPayload(future); - (next, Async::NotReady) - }, - ReadMessageState::ReadPayload(ref mut future) => { - let (read, payload) = try_ready!(future.poll()); - (ReadMessageState::Finished, Async::Ready((read, payload))) - }, - ReadMessageState::Finished => panic!("poll ReadMessage after it's done"), - }; - - self.state = next; - match result { - // by polling again, we register new future - Async::NotReady => self.poll(), - result => Ok(result) - } - } -} diff --git a/secret-store/src/key_server_cluster/io/read_payload.rs b/secret-store/src/key_server_cluster/io/read_payload.rs deleted file mode 100644 index fee466c533b..00000000000 --- a/secret-store/src/key_server_cluster/io/read_payload.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io; -use futures::{Poll, Future}; -use tokio_io::AsyncRead; -use tokio_io::io::{read_exact, ReadExact}; -use crypto::publickey::KeyPair; -use key_server_cluster::Error; -use key_server_cluster::message::Message; -use key_server_cluster::io::message::{MessageHeader, deserialize_message, decrypt_message}; - -/// Create future for read single message payload from the stream. -pub fn read_payload(a: A, header: MessageHeader) -> ReadPayload where A: AsyncRead { - ReadPayload { - reader: read_exact(a, vec![0; header.size as usize]), - header: header, - key: None, - } -} - -/// Create future for read single encrypted message payload from the stream. -pub fn read_encrypted_payload(a: A, header: MessageHeader, key: KeyPair) -> ReadPayload where A: AsyncRead { - ReadPayload { - reader: read_exact(a, vec![0; header.size as usize]), - header: header, - key: Some(key), - } -} - -/// Future for read single message payload from the stream. -pub struct ReadPayload { - reader: ReadExact>, - header: MessageHeader, - key: Option, -} - -impl Future for ReadPayload where A: AsyncRead { - type Item = (A, Result); - type Error = io::Error; - - fn poll(&mut self) -> Poll { - let (read, data) = try_ready!(self.reader.poll()); - let payload = if let Some(key) = self.key.take() { - decrypt_message(&key, data) - .and_then(|data| deserialize_message(&self.header, data)) - } else { - deserialize_message(&self.header, data) - }; - Ok((read, payload).into()) - } -} diff --git a/secret-store/src/key_server_cluster/io/shared_tcp_stream.rs b/secret-store/src/key_server_cluster/io/shared_tcp_stream.rs deleted file mode 100644 index a6a533a87da..00000000000 --- a/secret-store/src/key_server_cluster/io/shared_tcp_stream.rs +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::net::Shutdown; -use std::io::{Read, Write, Error}; -use futures::Poll; -use tokio_io::{AsyncRead, AsyncWrite}; -use tokio::net::TcpStream; - -/// Read+Write implementation for Arc. -pub struct SharedTcpStream { - io: Arc, -} - -impl SharedTcpStream { - pub fn new(a: Arc) -> Self { - SharedTcpStream { - io: a, - } - } -} - -impl From for SharedTcpStream { - fn from(a: TcpStream) -> Self { - SharedTcpStream::new(Arc::new(a)) - } -} - -impl AsyncRead for SharedTcpStream {} - -impl AsyncWrite for SharedTcpStream { - fn shutdown(&mut self) -> Poll<(), Error> { - self.io.shutdown(Shutdown::Both).map(Into::into) - } -} - -impl Read for SharedTcpStream { - fn read(&mut self, buf: &mut [u8]) -> Result { - Read::read(&mut (&*self.io as &TcpStream), buf) - } -} - -impl Write for SharedTcpStream { - fn write(&mut self, buf: &[u8]) -> Result { - Write::write(&mut (&*self.io as &TcpStream), buf) - } - - fn flush(&mut self) -> Result<(), Error> { - Write::flush(&mut (&*self.io as &TcpStream)) - } -} - -impl Clone for SharedTcpStream { - fn clone(&self) -> Self { - SharedTcpStream::new(self.io.clone()) - } -} diff --git a/secret-store/src/key_server_cluster/io/write_message.rs b/secret-store/src/key_server_cluster/io/write_message.rs deleted file mode 100644 index 05259eb3a96..00000000000 --- a/secret-store/src/key_server_cluster/io/write_message.rs +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io; -use futures::{Future, Poll}; -use tokio_io::AsyncWrite; -use tokio_io::io::{WriteAll, write_all}; -use crypto::publickey::KeyPair; -use key_server_cluster::message::Message; -use key_server_cluster::io::{serialize_message, encrypt_message}; - -/// Write plain message to the channel. -pub fn write_message(a: A, message: Message) -> WriteMessage where A: AsyncWrite { - let (error, future) = match serialize_message(message) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string())) { - Ok(message) => (None, write_all(a, message.into())), - Err(error) => (Some(error), write_all(a, Vec::new())), - }; - WriteMessage { - error: error, - future: future, - } -} - -/// Write encrypted message to the channel. -pub fn write_encrypted_message(a: A, key: &KeyPair, message: Message) -> WriteMessage where A: AsyncWrite { - let (error, future) = match serialize_message(message) - .and_then(|message| encrypt_message(key, message)) - .map_err(|e| io::Error::new(io::ErrorKind::Other, e.to_string())) { - Ok(message) => (None, write_all(a, message.into())), - Err(error) => (Some(error), write_all(a, Vec::new())), - }; - - WriteMessage { - error: error, - future: future, - } -} - -/// Future message write. -pub struct WriteMessage { - error: Option, - future: WriteAll>, -} - -impl Future for WriteMessage where A: AsyncWrite { - type Item = (A, Vec); - type Error = io::Error; - - fn poll(&mut self) -> Poll { - if let Some(err) = self.error.take() { - return Err(err); - } - - self.future.poll() - } -} diff --git a/secret-store/src/key_server_cluster/jobs/consensus_session.rs b/secret-store/src/key_server_cluster/jobs/consensus_session.rs deleted file mode 100644 index 258348edca4..00000000000 --- a/secret-store/src/key_server_cluster/jobs/consensus_session.rs +++ /dev/null @@ -1,793 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeSet; -use key_server_cluster::{Error, NodeId, SessionMeta, Requester}; -use key_server_cluster::message::ConsensusMessage; -use key_server_cluster::jobs::job_session::{JobSession, JobSessionState, JobTransport, JobExecutor, JobPartialRequestAction}; - -/// Consensus session state. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum ConsensusSessionState { - /// Every node starts in this state. - WaitingForInitialization, - /// Consensus group is establishing. - EstablishingConsensus, - /// Consensus group is established. - /// Master node can start jobs dissemination. - /// Slave node waits for partial job requests. - ConsensusEstablished, - /// Master node waits for partial jobs responses. - WaitingForPartialResults, - /// Consensus session is completed successfully. - /// Master node can call result() to get computation result. - Finished, - /// Consensus session has failed with error. - Failed, -} - -/// Consensus session consists of following states: -/// 1) consensus group is established -/// 2) master node sends partial job requests to every member of consensus group -/// 3) slave nodes are computing partial responses -/// 4) master node computes result from partial responses -pub struct ConsensusSession, - ConsensusTransport: JobTransport, - ComputationExecutor: JobExecutor, - ComputationTransport: JobTransport -> { - /// Current session state. - state: ConsensusSessionState, - /// Session metadata. - meta: SessionMeta, - /// Consensus establish job. - consensus_job: JobSession, - /// Consensus group. - consensus_group: BTreeSet, - /// Computation job. - computation_job: Option>, -} - -/// Consensus session creation parameters. -pub struct ConsensusSessionParams, - ConsensusTransport: JobTransport -> { - /// Session metadata. - pub meta: SessionMeta, - /// ACL storage for access check. - pub consensus_executor: ConsensusExecutor, - /// Transport for consensus establish job. - pub consensus_transport: ConsensusTransport, -} - -impl ConsensusSession - where ConsensusExecutor: JobExecutor>, - ConsensusTransport: JobTransport, - ComputationExecutor: JobExecutor, - ComputationTransport: JobTransport { - /// Create new consensus session. - pub fn new(params: ConsensusSessionParams) -> Result { - let consensus_job = JobSession::new(params.meta.clone(), params.consensus_executor, params.consensus_transport); - debug_assert!(consensus_job.state() == JobSessionState::Inactive); - - Ok(ConsensusSession { - state: ConsensusSessionState::WaitingForInitialization, - meta: params.meta, - consensus_job: consensus_job, - consensus_group: BTreeSet::new(), - computation_job: None, - }) - } - - /// Get consensus job reference. - pub fn consensus_job(&self) -> &JobSession { - &self.consensus_job - } - - /// Get mutable consensus job reference. - pub fn consensus_job_mut(&mut self) -> &mut JobSession { - &mut self.consensus_job - } - - /// Get all nodes, which has not rejected consensus request. - pub fn consensus_non_rejected_nodes(&self) -> BTreeSet { - self.consensus_job.responses().iter() - .filter(|r| *r.1) - .map(|r| r.0) - .chain(self.consensus_job.requests()) - .filter(|n| **n != self.meta.self_node_id) - .cloned() - .collect() - } - - /// Get computation job reference. - pub fn computation_job(&self) -> &JobSession { - self.computation_job.as_ref() - .expect("computation_job must only be called on master nodes") - } - - /// Get consensus session state. - pub fn state(&self) -> ConsensusSessionState { - self.state - } - - /// Get computation result. - pub fn result(&self) -> Result { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - if self.state != ConsensusSessionState::Finished { - return Err(Error::InvalidStateForRequest); - } - - self.computation_job.as_ref() - .expect("we are on master node in finished state; computation_job is set on master node during initialization; qed") - .result() - } - - /// Initialize session on master node. - pub fn initialize(&mut self, nodes: BTreeSet) -> Result<(), Error> { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - let initialization_result = self.consensus_job.initialize(nodes, None, false); - self.state = ConsensusSessionState::EstablishingConsensus; - self.process_result(initialization_result.map(|_| ())) - } - - /// Process consensus request message. - pub fn on_consensus_partial_request(&mut self, sender: &NodeId, request: ConsensusExecutor::PartialJobRequest) -> Result<(), Error> { - let consensus_result = self.consensus_job.on_partial_request(sender, request); - self.process_result(consensus_result.map(|_| ())) - } - - /// Process consensus message response. - pub fn on_consensus_partial_response(&mut self, sender: &NodeId, response: bool) -> Result<(), Error> { - let consensus_result = self.consensus_job.on_partial_response(sender, response); - self.process_result(consensus_result) - } - - /// Select nodes for processing partial requests. - pub fn select_consensus_group(&mut self) -> Result<&BTreeSet, Error> { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - if self.state != ConsensusSessionState::ConsensusEstablished { - return Err(Error::InvalidStateForRequest); - } - - if self.consensus_group.is_empty() { - let consensus_group = self.consensus_job.result()?; - let is_self_in_consensus = consensus_group.contains(&self.meta.self_node_id); - self.consensus_group = consensus_group.into_iter().take(self.meta.threshold + 1).collect(); - - if is_self_in_consensus { - self.consensus_group.remove(&self.meta.master_node_id); - self.consensus_group.insert(self.meta.master_node_id.clone()); - } - } - - Ok(&self.consensus_group) - } - - /// Disseminate jobs from master node. - pub fn disseminate_jobs(&mut self, executor: ComputationExecutor, transport: ComputationTransport, broadcast_self_response: bool) -> Result, Error> { - let consensus_group = self.select_consensus_group()?.clone(); - self.consensus_group.clear(); - - let mut computation_job = JobSession::new(self.meta.clone(), executor, transport); - let computation_result = computation_job.initialize(consensus_group, None, broadcast_self_response); - self.computation_job = Some(computation_job); - self.state = ConsensusSessionState::WaitingForPartialResults; - match computation_result { - Ok(computation_result) => self.process_result(Ok(())).map(|_| computation_result), - Err(error) => Err(self.process_result(Err(error)).unwrap_err()), - } - } - - /// Process job request on slave node. - pub fn on_job_request(&mut self, node: &NodeId, request: ComputationExecutor::PartialJobRequest, executor: ComputationExecutor, transport: ComputationTransport) -> Result, Error> { - if &self.meta.master_node_id != node { - return Err(Error::InvalidMessage); - } - if self.state != ConsensusSessionState::ConsensusEstablished { - return Err(Error::InvalidStateForRequest); - } - - JobSession::new(self.meta.clone(), executor, transport).on_partial_request(node, request) - } - - /// Process job response on slave node. - pub fn on_job_response(&mut self, node: &NodeId, response: ComputationExecutor::PartialJobResponse) -> Result<(), Error> { - if self.state != ConsensusSessionState::WaitingForPartialResults { - return Err(Error::InvalidStateForRequest); - } - - let computation_result = self.computation_job.as_mut() - .expect("WaitingForPartialResults is only set when computation_job is created; qed") - .on_partial_response(node, response); - - self.process_result(computation_result) - } - - /// When session is completed on slave node. - pub fn on_session_completed(&mut self, node: &NodeId) -> Result<(), Error> { - if node != &self.meta.master_node_id { - return Err(Error::InvalidMessage); - } - if self.state != ConsensusSessionState::ConsensusEstablished { - return Err(Error::InvalidStateForRequest); - } - - self.state = ConsensusSessionState::Finished; - - Ok(()) - } - - /// When error is received from node. - pub fn on_node_error(&mut self, node: &NodeId, error: Error) -> Result { - let is_self_master = self.meta.master_node_id == self.meta.self_node_id; - let is_node_master = self.meta.master_node_id == *node; - let (is_restart_needed, timeout_result) = match self.state { - ConsensusSessionState::WaitingForInitialization if is_self_master => { - // it is strange to receive error before session is initialized && slave doesn't know access_key - // => unreachable - self.state = ConsensusSessionState::Failed; - (false, Err(Error::ConsensusUnreachable)) - } - ConsensusSessionState::WaitingForInitialization if is_node_master => { - // error from master node before establishing consensus - // => unreachable - self.state = ConsensusSessionState::Failed; - (false, Err(if !error.is_non_fatal() { - Error::ConsensusUnreachable - } else { - Error::ConsensusTemporaryUnreachable - })) - }, - ConsensusSessionState::EstablishingConsensus => { - debug_assert!(is_self_master); - - // consensus still can be established - // => try to live without this node - (false, self.consensus_job.on_node_error(node, error)) - }, - ConsensusSessionState::ConsensusEstablished => { - // we could try to continue without this node, if enough nodes left - (false, self.consensus_job.on_node_error(node, error)) - }, - ConsensusSessionState::WaitingForPartialResults => { - // check if *current* computation job can continue without this node - let is_computation_node = self.computation_job.as_mut() - .expect("WaitingForPartialResults state is only set when computation_job is created; qed") - .on_node_error(node, error.clone()) - .is_err(); - if !is_computation_node { - // it is not used by current computation job - // => no restart required - (false, Ok(())) - } else { - // it is used by current computation job - // => restart is required if there are still enough nodes - self.consensus_group.clear(); - self.state = ConsensusSessionState::EstablishingConsensus; - - let consensus_result = self.consensus_job.on_node_error(node, error); - let is_consensus_established = self.consensus_job.state() == JobSessionState::Finished; - (is_consensus_established, consensus_result) - } - }, - // in all other cases - just ignore error - ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::Failed | ConsensusSessionState::Finished => (false, Ok(())), - }; - self.process_result(timeout_result)?; - Ok(is_restart_needed) - } - - /// When session is timeouted. - pub fn on_session_timeout(&mut self) -> Result { - match self.state { - // if we are waiting for results from slaves, there is a chance to send request to other nodes subset => fall through - ConsensusSessionState::WaitingForPartialResults => (), - // in some states this error is fatal - ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::EstablishingConsensus | ConsensusSessionState::ConsensusEstablished => { - let _ = self.consensus_job.on_session_timeout(); - - self.consensus_group.clear(); - self.state = ConsensusSessionState::EstablishingConsensus; - return self.process_result(Err(Error::ConsensusTemporaryUnreachable)).map(|_| unreachable!()); - }, - // in all other cases - just ignore error - ConsensusSessionState::Finished | ConsensusSessionState::Failed => return Ok(false), - }; - - let timeouted_nodes = self.computation_job.as_ref() - .expect("WaitingForPartialResults state is only set when computation_job is created; qed") - .requests() - .clone(); - assert!(!timeouted_nodes.is_empty()); // timeout should not ever happen if no requests are active && we are waiting for responses - - self.consensus_group.clear(); - for timeouted_node in timeouted_nodes { - let timeout_result = self.consensus_job.on_node_error(&timeouted_node, Error::NodeDisconnected); - self.state = ConsensusSessionState::EstablishingConsensus; - self.process_result(timeout_result)?; - } - - Ok(self.state == ConsensusSessionState::ConsensusEstablished) - } - - /// Process result of job. - fn process_result(&mut self, result: Result<(), Error>) -> Result<(), Error> { - match self.state { - ConsensusSessionState::WaitingForInitialization | ConsensusSessionState::EstablishingConsensus | ConsensusSessionState::ConsensusEstablished => match self.consensus_job.state() { - JobSessionState::Finished => self.state = ConsensusSessionState::ConsensusEstablished, - JobSessionState::Failed => self.state = ConsensusSessionState::Failed, - _ => (), - }, - ConsensusSessionState::WaitingForPartialResults => match self.computation_job.as_ref() - .expect("WaitingForPartialResults state is only set when computation_job is created; qed") - .state() { - JobSessionState::Finished => self.state = ConsensusSessionState::Finished, - JobSessionState::Failed => self.state = ConsensusSessionState::Failed, - _ => (), - }, - _ => (), - } - - result - } -} - -impl ConsensusSession - where ConsensusExecutor: JobExecutor>, - ConsensusTransport: JobTransport, - ComputationExecutor: JobExecutor, - ComputationTransport: JobTransport { - /// Process basic consensus message. - pub fn on_consensus_message(&mut self, sender: &NodeId, message: &ConsensusMessage) -> Result<(), Error> { - let consensus_result = match message { - - &ConsensusMessage::InitializeConsensusSession(ref message) => - self.consensus_job.on_partial_request(sender, message.requester.clone().into()).map(|_| ()), - &ConsensusMessage::ConfirmConsensusInitialization(ref message) => - self.consensus_job.on_partial_response(sender, message.is_confirmed), - }; - self.process_result(consensus_result) - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use crypto::publickey::{KeyPair, Random, Generator, sign, public_to_address}; - use key_server_cluster::{Error, NodeId, SessionId, Requester, DummyAclStorage}; - use key_server_cluster::message::{ConsensusMessage, InitializeConsensusSession, ConfirmConsensusInitialization}; - use key_server_cluster::jobs::job_session::tests::{make_master_session_meta, make_slave_session_meta, SquaredSumJobExecutor, DummyJobTransport}; - use key_server_cluster::jobs::key_access_job::KeyAccessJob; - use super::{ConsensusSession, ConsensusSessionParams, ConsensusSessionState}; - - type SquaredSumConsensusSession = ConsensusSession, SquaredSumJobExecutor, DummyJobTransport>; - - fn make_master_consensus_session(threshold: usize, requester: Option, acl_storage: Option) -> SquaredSumConsensusSession { - let secret = requester.map(|kp| kp.secret().clone()).unwrap_or(Random.generate().unwrap().secret().clone()); - SquaredSumConsensusSession::new(ConsensusSessionParams { - meta: make_master_session_meta(threshold), - consensus_executor: KeyAccessJob::new_on_master(SessionId::default(), Arc::new(acl_storage.unwrap_or(DummyAclStorage::default())), - sign(&secret, &SessionId::default()).unwrap().into()), - consensus_transport: DummyJobTransport::default(), - }).unwrap() - } - - fn make_slave_consensus_session(threshold: usize, acl_storage: Option) -> SquaredSumConsensusSession { - SquaredSumConsensusSession::new(ConsensusSessionParams { - meta: make_slave_session_meta(threshold), - consensus_executor: KeyAccessJob::new_on_slave(SessionId::default(), Arc::new(acl_storage.unwrap_or(DummyAclStorage::default()))), - consensus_transport: DummyJobTransport::default(), - }).unwrap() - } - - #[test] - fn consensus_session_consensus_is_not_reached_when_initializes_with_non_zero_threshold() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_consensus_is_reached_when_initializes_with_zero_threshold() { - let mut session = make_master_consensus_session(0, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_consensus_is_not_reached_when_initializes_with_zero_threshold_and_master_rejects() { - let requester = Random.generate().unwrap(); - let acl_storage = DummyAclStorage::default(); - acl_storage.prohibit(public_to_address(requester.public()), SessionId::default()); - - let mut session = make_master_consensus_session(0, Some(requester), Some(acl_storage)); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_consensus_is_failed_by_master_node() { - let requester = Random.generate().unwrap(); - let acl_storage = DummyAclStorage::default(); - acl_storage.prohibit(public_to_address(requester.public()), SessionId::default()); - - let mut session = make_master_consensus_session(1, Some(requester), Some(acl_storage)); - assert_eq!(session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap_err(), Error::ConsensusUnreachable); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_session_consensus_is_failed_by_slave_node() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - assert_eq!(session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: false, - })).unwrap_err(), Error::ConsensusUnreachable); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_session_job_dissemination_fails_if_consensus_is_not_reached() { - let mut session = make_master_consensus_session(1, None, None); - assert_eq!(session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn consensus_session_job_dissemination_selects_master_node_if_agreed() { - let mut session = make_master_consensus_session(0, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::Finished); - assert!(session.computation_job().responses().contains_key(&NodeId::from_low_u64_be(1))); - } - - #[test] - fn consensus_session_job_dissemination_does_not_select_master_node_if_rejected() { - let requester = Random.generate().unwrap(); - let acl_storage = DummyAclStorage::default(); - acl_storage.prohibit(public_to_address(requester.public()), SessionId::default()); - - let mut session = make_master_consensus_session(0, Some(requester), Some(acl_storage)); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - assert!(!session.computation_job().responses().contains_key(&NodeId::from_low_u64_be(1))); - } - - #[test] - fn consensus_session_computation_request_is_rejected_when_received_by_master_node() { - let mut session = make_master_consensus_session(0, None, None); - assert_eq!(session.on_job_request(&NodeId::from_low_u64_be(2), 2, SquaredSumJobExecutor, DummyJobTransport::default()).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn consensus_session_computation_request_is_rejected_when_received_before_consensus_is_established() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.on_job_request(&NodeId::from_low_u64_be(1), 2, SquaredSumJobExecutor, DummyJobTransport::default()).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn consensus_session_computation_request_is_ignored_when_wrong() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.state(), ConsensusSessionState::WaitingForInitialization); - session.on_consensus_message(&NodeId::from_low_u64_be(1), &ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requester: Requester::Signature(sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()).into(), - version: Default::default(), - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - assert_eq!(session.on_job_request(&NodeId::from_low_u64_be(1), 20, SquaredSumJobExecutor, DummyJobTransport::default()).unwrap_err(), Error::InvalidMessage); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_computation_request_is_processed_when_correct() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.state(), ConsensusSessionState::WaitingForInitialization); - session.on_consensus_message(&NodeId::from_low_u64_be(1), &ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requester: Requester::Signature(sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()).into(), - version: Default::default(), - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.on_job_request(&NodeId::from_low_u64_be(1), 2, SquaredSumJobExecutor, DummyJobTransport::default()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_computation_response_is_ignored_when_consensus_is_not_reached() { - let mut session = make_master_consensus_session(1, None, None); - assert_eq!(session.on_job_response(&NodeId::from_low_u64_be(2), 4).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn consessus_session_completion_is_ignored_when_received_from_non_master_node() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.on_session_completed(&NodeId::from_low_u64_be(3)).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn consessus_session_completion_is_ignored_when_consensus_is_not_established() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.on_session_completed(&NodeId::from_low_u64_be(1)).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn consessus_session_completion_is_accepted() { - let mut session = make_slave_consensus_session(0, None); - session.on_consensus_message(&NodeId::from_low_u64_be(1), &ConsensusMessage::InitializeConsensusSession(InitializeConsensusSession { - requester: Requester::Signature(sign(Random.generate().unwrap().secret(), &SessionId::default()).unwrap()).into(), - version: Default::default(), - })).unwrap(); - session.on_session_completed(&NodeId::from_low_u64_be(1)).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::Finished); - } - - #[test] - fn consensus_session_fails_if_node_error_received_by_uninitialized_master() { - let mut session = make_master_consensus_session(0, None, None); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Err(Error::ConsensusUnreachable)); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_session_fails_if_node_error_received_by_uninitialized_slave_from_master() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(1), Error::AccessDenied), Err(Error::ConsensusUnreachable)); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_sessions_fails_with_temp_error_if_node_error_received_by_uninitialized_slave_from_master() { - let mut session = make_slave_consensus_session(0, None); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(1), Error::NodeDisconnected).unwrap_err(), Error::ConsensusTemporaryUnreachable); - } - - #[test] - fn consensus_session_continues_if_node_error_received_by_master_during_establish_and_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3)].into_iter().collect()).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Ok(false)); - } - - #[test] - fn consensus_session_fails_if_node_error_received_by_master_during_establish_and_not_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Err(Error::ConsensusUnreachable)); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_session_continues_if_node2_error_received_by_master_after_consensus_established_and_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Ok(false)); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_continues_if_node3_error_received_by_master_after_consensus_established_and_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied), Ok(false)); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - } - - #[test] - fn consensus_session_fails_if_node_error_received_by_master_after_consensus_established_and_not_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Err(Error::ConsensusUnreachable)); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_session_continues_if_node_error_received_from_slave_not_participating_in_computation() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3), NodeId::from_low_u64_be(4)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied), Ok(false)); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(4), Error::AccessDenied), Ok(false)); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - } - - #[test] - fn consensus_session_restarts_if_node_error_received_from_slave_participating_in_computation_and_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3), NodeId::from_low_u64_be(4)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - session.on_consensus_message(&NodeId::from_low_u64_be(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Ok(true)); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied), Ok(false)); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - } - - #[test] - fn consensus_session_fails_if_node_error_received_from_slave_participating_in_computation_and_not_enough_nodes_left() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied), Err(Error::ConsensusUnreachable)); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn consensus_session_fails_if_uninitialized_session_timeouts() { - let mut session = make_master_consensus_session(1, None, None); - assert_eq!(session.on_session_timeout(), Err(Error::ConsensusTemporaryUnreachable)); - } - - #[test] - fn consensus_session_continues_if_session_timeouts_and_enough_nodes_left_for_computation() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3), NodeId::from_low_u64_be(4)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - session.on_consensus_message(&NodeId::from_low_u64_be(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.on_session_timeout(), Ok(true)); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - assert_eq!(session.on_session_timeout(), Ok(false)); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - } - - #[test] - fn consensus_session_continues_if_session_timeouts_and_not_enough_nodes_left_for_computation() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - assert_eq!(session.on_session_timeout(), Err(Error::ConsensusUnreachable)); - assert_eq!(session.state(), ConsensusSessionState::Failed); - } - - #[test] - fn same_consensus_group_returned_after_second_selection() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3)].into_iter().collect()).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - session.on_consensus_message(&NodeId::from_low_u64_be(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - - let consensus_group1 = session.select_consensus_group().unwrap().clone(); - let consensus_group2 = session.select_consensus_group().unwrap().clone(); - assert_eq!(consensus_group1, consensus_group2); - } - - #[test] - fn consensus_session_complete_2_of_4() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3), NodeId::from_low_u64_be(3)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - session.on_job_response(&NodeId::from_low_u64_be(2), 16).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::Finished); - assert_eq!(session.result(), Ok(20)); - } - - #[test] - fn consensus_session_complete_2_of_4_after_restart() { - let mut session = make_master_consensus_session(1, None, None); - session.initialize(vec![NodeId::from_low_u64_be(1), NodeId::from_low_u64_be(2), NodeId::from_low_u64_be(3), NodeId::from_low_u64_be(4)].into_iter().collect()).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - session.on_consensus_message(&NodeId::from_low_u64_be(2), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - session.on_consensus_message(&NodeId::from_low_u64_be(3), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied).unwrap(), true); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - assert_eq!(session.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied).unwrap(), false); - assert_eq!(session.state(), ConsensusSessionState::EstablishingConsensus); - - session.on_consensus_message(&NodeId::from_low_u64_be(4), &ConsensusMessage::ConfirmConsensusInitialization(ConfirmConsensusInitialization { - is_confirmed: true, - })).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::ConsensusEstablished); - - session.disseminate_jobs(SquaredSumJobExecutor, DummyJobTransport::default(), false).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::WaitingForPartialResults); - - session.on_job_response(&NodeId::from_low_u64_be(4), 16).unwrap(); - assert_eq!(session.state(), ConsensusSessionState::Finished); - assert_eq!(session.result(), Ok(20)); - } -} diff --git a/secret-store/src/key_server_cluster/jobs/decryption_job.rs b/secret-store/src/key_server_cluster/jobs/decryption_job.rs deleted file mode 100644 index 94b89dee82d..00000000000 --- a/secret-store/src/key_server_cluster/jobs/decryption_job.rs +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use ethereum_types::H256; -use crypto::publickey::{Public, Secret}; -use crypto::DEFAULT_MAC; -use crypto::publickey::ecies::encrypt; -use key_server_cluster::{Error, NodeId, DocumentKeyShare, EncryptedDocumentKeyShadow}; -use key_server_cluster::math; -use key_server_cluster::jobs::job_session::{JobPartialRequestAction, JobPartialResponseAction, JobExecutor}; - -/// Decryption job. -pub struct DecryptionJob { - /// This node id. - self_node_id: NodeId, - /// Access key. - access_key: Secret, - /// Requester public key. - requester: Public, - /// Key share. - key_share: DocumentKeyShare, - /// Key version. - key_version: H256, - /// Request id. - request_id: Option, - /// Is shadow decryption requested. - is_shadow_decryption: Option, - /// Is broadcast decryption requested. - is_broadcast_session: Option, -} - -/// Decryption job partial request. -#[derive(Debug)] -pub struct PartialDecryptionRequest { - /// Request id. - pub id: Secret, - /// Is shadow decryption requested. - pub is_shadow_decryption: bool, - /// Is broadcast decryption requested. - pub is_broadcast_session: bool, - /// Id of other nodes, participating in decryption. - pub other_nodes_ids: BTreeSet, -} - -/// Decryption job partial response. -#[derive(Clone)] -pub struct PartialDecryptionResponse { - /// Request id. - pub request_id: Secret, - /// Shadow point. - pub shadow_point: Public, - /// Decryption shadow coefficient, if requested. - pub decrypt_shadow: Option>, -} - -impl DecryptionJob { - pub fn new_on_slave(self_node_id: NodeId, access_key: Secret, requester: Public, key_share: DocumentKeyShare, key_version: H256) -> Result { - debug_assert!(key_share.common_point.is_some() && key_share.encrypted_point.is_some()); - Ok(DecryptionJob { - self_node_id: self_node_id, - access_key: access_key, - requester: requester, - key_share: key_share, - key_version: key_version, - request_id: None, - is_shadow_decryption: None, - is_broadcast_session: None, - }) - } - - pub fn new_on_master(self_node_id: NodeId, access_key: Secret, requester: Public, key_share: DocumentKeyShare, key_version: H256, is_shadow_decryption: bool, is_broadcast_session: bool) -> Result { - debug_assert!(key_share.common_point.is_some() && key_share.encrypted_point.is_some()); - Ok(DecryptionJob { - self_node_id: self_node_id, - access_key: access_key, - requester: requester, - key_share: key_share, - key_version: key_version, - request_id: Some(math::generate_random_scalar()?), - is_shadow_decryption: Some(is_shadow_decryption), - is_broadcast_session: Some(is_broadcast_session), - }) - } - - pub fn request_id(&self) -> &Option { - &self.request_id - } - - pub fn set_request_id(&mut self, request_id: Secret) { - self.request_id = Some(request_id); - } -} - -impl JobExecutor for DecryptionJob { - type PartialJobRequest = PartialDecryptionRequest; - type PartialJobResponse = PartialDecryptionResponse; - type JobResponse = EncryptedDocumentKeyShadow; - - fn prepare_partial_request(&self, node: &NodeId, nodes: &BTreeSet) -> Result { - debug_assert!(nodes.len() == self.key_share.threshold + 1); - - let request_id = self.request_id.as_ref() - .expect("prepare_partial_request is only called on master nodes; request_id is filed in constructor on master nodes; qed"); - let is_shadow_decryption = self.is_shadow_decryption - .expect("prepare_partial_request is only called on master nodes; is_shadow_decryption is filed in constructor on master nodes; qed"); - let is_broadcast_session = self.is_broadcast_session - .expect("prepare_partial_request is only called on master nodes; is_broadcast_session is filed in constructor on master nodes; qed"); - let mut other_nodes_ids = nodes.clone(); - other_nodes_ids.remove(node); - - Ok(PartialDecryptionRequest { - id: request_id.clone(), - is_shadow_decryption: is_shadow_decryption, - is_broadcast_session: is_broadcast_session, - other_nodes_ids: other_nodes_ids, - }) - } - - fn process_partial_request(&mut self, partial_request: PartialDecryptionRequest) -> Result, Error> { - let key_version = self.key_share.version(&self.key_version)?; - if partial_request.other_nodes_ids.len() != self.key_share.threshold - || partial_request.other_nodes_ids.contains(&self.self_node_id) - || partial_request.other_nodes_ids.iter().any(|n| !key_version.id_numbers.contains_key(n)) { - return Err(Error::InvalidMessage); - } - - let self_id_number = &key_version.id_numbers[&self.self_node_id]; - let other_id_numbers = partial_request.other_nodes_ids.iter().map(|n| &key_version.id_numbers[n]); - let node_shadow = math::compute_node_shadow(&key_version.secret_share, &self_id_number, other_id_numbers)?; - let decrypt_shadow = if partial_request.is_shadow_decryption { Some(math::generate_random_scalar()?) } else { None }; - let common_point = self.key_share.common_point.as_ref().expect("DecryptionJob is only created when common_point is known; qed"); - let (shadow_point, decrypt_shadow) = math::compute_node_shadow_point(&self.access_key, &common_point, &node_shadow, decrypt_shadow)?; - - Ok(JobPartialRequestAction::Respond(PartialDecryptionResponse { - request_id: partial_request.id, - shadow_point: shadow_point, - decrypt_shadow: match decrypt_shadow.clone() { - None => None, - Some(decrypt_shadow) => Some(encrypt(&self.requester, &DEFAULT_MAC, decrypt_shadow.as_bytes())?), - }, - })) - } - - fn check_partial_response(&mut self, _sender: &NodeId, partial_response: &PartialDecryptionResponse) -> Result { - if Some(&partial_response.request_id) != self.request_id.as_ref() { - return Ok(JobPartialResponseAction::Ignore); - } - if self.is_shadow_decryption != Some(partial_response.decrypt_shadow.is_some()) { - return Ok(JobPartialResponseAction::Reject); - } - Ok(JobPartialResponseAction::Accept) - } - - fn compute_response(&self, partial_responses: &BTreeMap) -> Result { - let is_shadow_decryption = self.is_shadow_decryption - .expect("compute_response is only called on master nodes; is_shadow_decryption is filed in constructor on master nodes; qed"); - let common_point = self.key_share.common_point.as_ref().expect("DecryptionJob is only created when common_point is known; qed"); - let encrypted_point = self.key_share.encrypted_point.as_ref().expect("DecryptionJob is only created when encrypted_point is known; qed"); - let joint_shadow_point = math::compute_joint_shadow_point(partial_responses.values().map(|s| &s.shadow_point))?; - let decrypted_secret = math::decrypt_with_joint_shadow(self.key_share.threshold, &self.access_key, encrypted_point, &joint_shadow_point)?; - Ok(EncryptedDocumentKeyShadow { - decrypted_secret: decrypted_secret, - common_point: if is_shadow_decryption { - Some(math::make_common_shadow_point(self.key_share.threshold, common_point.clone())?) - } else { None }, - decrypt_shadows: if is_shadow_decryption { - Some(partial_responses.values().map(|r| r.decrypt_shadow.as_ref() - .expect("is_shadow_decryption == true; decrypt_shadow.is_some() is checked in check_partial_response; qed") - .clone()) - .collect()) - } else { None }, - }) - } -} diff --git a/secret-store/src/key_server_cluster/jobs/dummy_job.rs b/secret-store/src/key_server_cluster/jobs/dummy_job.rs deleted file mode 100644 index 834d0de7d85..00000000000 --- a/secret-store/src/key_server_cluster/jobs/dummy_job.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeMap, BTreeSet}; -use key_server_cluster::{Error, NodeId}; -use key_server_cluster::jobs::job_session::{JobExecutor, JobTransport, JobPartialRequestAction, JobPartialResponseAction}; - -/// No-work job to use in generics (TODO [Refac]: create separate ShareChangeConsensusSession && remove this) -pub struct DummyJob; - -impl JobExecutor for DummyJob { - type PartialJobRequest = (); - type PartialJobResponse = (); - type JobResponse = (); - - fn prepare_partial_request(&self, _n: &NodeId, _nodes: &BTreeSet) -> Result<(), Error> { - unreachable!("dummy job methods are never called") - } - - fn process_partial_request(&mut self, _r: ()) -> Result, Error> { - unreachable!("dummy job methods are never called") - } - - fn check_partial_response(&mut self, _s: &NodeId, _r: &()) -> Result { - unreachable!("dummy job methods are never called") - } - - fn compute_response(&self, _r: &BTreeMap) -> Result<(), Error> { - unreachable!("dummy job methods are never called") - } -} - -/// No-work job transport to use in generics (TODO [Refac]: create separate ShareChangeConsensusSession && remove this) -pub struct DummyJobTransport; - -impl JobTransport for DummyJobTransport { - type PartialJobRequest = (); - type PartialJobResponse = (); - - fn send_partial_request(&self, _node: &NodeId, _request: ()) -> Result<(), Error> { - unreachable!("dummy transport methods are never called") - } - - fn send_partial_response(&self, _node: &NodeId, _response: ()) -> Result<(), Error> { - unreachable!("dummy transport methods are never called") - } -} diff --git a/secret-store/src/key_server_cluster/jobs/job_session.rs b/secret-store/src/key_server_cluster/jobs/job_session.rs deleted file mode 100644 index d711098121f..00000000000 --- a/secret-store/src/key_server_cluster/jobs/job_session.rs +++ /dev/null @@ -1,661 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use key_server_cluster::{Error, NodeId, SessionMeta}; - -/// Partial response action. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum JobPartialResponseAction { - /// Ignore this response. - Ignore, - /// Mark this response as reject. - Reject, - /// Accept this response. - Accept, -} - -/// Partial request action. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum JobPartialRequestAction { - /// Respond with reject. - Reject(PartialJobResponse), - /// Respond with this response. - Respond(PartialJobResponse), -} - -/// Job executor. -pub trait JobExecutor { - type PartialJobRequest; - type PartialJobResponse: Clone; - type JobResponse; - - /// Prepare job request for given node. - fn prepare_partial_request(&self, node: &NodeId, nodes: &BTreeSet) -> Result; - /// Process partial request. - fn process_partial_request(&mut self, partial_request: Self::PartialJobRequest) -> Result, Error>; - /// Check partial response of given node. - fn check_partial_response(&mut self, sender: &NodeId, partial_response: &Self::PartialJobResponse) -> Result; - /// Compute final job response. - fn compute_response(&self, partial_responses: &BTreeMap) -> Result; -} - -/// Jobs transport. -pub trait JobTransport { - type PartialJobRequest; - type PartialJobResponse; - - /// Send partial request to given node. - fn send_partial_request(&self, node: &NodeId, request: Self::PartialJobRequest) -> Result<(), Error>; - /// Send partial request to given node. - fn send_partial_response(&self, node: &NodeId, response: Self::PartialJobResponse) -> Result<(), Error>; -} - -/// Current state of job session. -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum JobSessionState { - /// Session is inactive. - Inactive, - /// Session is active. - Active, - /// Session is finished. - Finished, - /// Session has failed. - Failed, -} - -/// Basic request-response session on a set of nodes. -pub struct JobSession where Transport: JobTransport { - /// Session meta. - meta: SessionMeta, - /// Job executor. - executor: Executor, - /// Jobs transport. - transport: Transport, - /// Session data. - data: JobSessionData, -} - -/// Data of job session. -struct JobSessionData { - /// Session state. - state: JobSessionState, - /// Mutable session data. - active_data: Option>, -} - -/// Active job session data. -struct ActiveJobSessionData { - /// Active partial requests. - requests: BTreeSet, - /// Rejects to partial requests (maps to true, if reject is fatal). - rejects: BTreeMap, - /// Received partial responses. - responses: BTreeMap, -} - -impl JobSession where Executor: JobExecutor, Transport: JobTransport { - /// Create new session. - pub fn new(meta: SessionMeta, executor: Executor, transport: Transport) -> Self { - JobSession { - meta: meta, - executor: executor, - transport: transport, - data: JobSessionData { - state: JobSessionState::Inactive, - active_data: None, - }, - } - } - - /// Get transport reference. - #[cfg(test)] - pub fn transport(&self) -> &Transport { - &self.transport - } - - /// Get mutable transport reference. - pub fn transport_mut(&mut self) -> &mut Transport { - &mut self.transport - } - - /// Get executor reference. - pub fn executor(&self) -> &Executor { - &self.executor - } - - /// Get mutable executor reference. - pub fn executor_mut(&mut self) -> &mut Executor { - &mut self.executor - } - - /// Get job state. - pub fn state(&self) -> JobSessionState { - self.data.state - } - - /// Get rejects. - #[cfg(test)] - pub fn rejects(&self) -> &BTreeMap { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - - &self.data.active_data.as_ref() - .expect("rejects is only called on master nodes after initialization; on master nodes active_data is filled during initialization; qed") - .rejects - } - - /// Get active requests. - pub fn requests(&self) -> &BTreeSet { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - - &self.data.active_data.as_ref() - .expect("requests is only called on master nodes after initialization; on master nodes active_data is filled during initialization; qed") - .requests - } - - /// Get responses. - pub fn responses(&self) -> &BTreeMap { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - - &self.data.active_data.as_ref() - .expect("responses is only called on master nodes after initialization; on master nodes active_data is filled during initialization; qed") - .responses - } - - /// Returns true if enough responses are ready to compute result. - pub fn is_result_ready(&self) -> bool { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - self.data.active_data.as_ref() - .expect("is_result_ready is only called on master nodes after initialization; on master nodes active_data is filled during initialization; qed") - .responses.len() >= self.meta.threshold + 1 - } - - /// Get job result. - pub fn result(&self) -> Result { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - - if self.data.state != JobSessionState::Finished { - return Err(Error::InvalidStateForRequest); - } - - self.executor.compute_response(&self.data.active_data.as_ref() - .expect("requests is only called on master nodes; on master nodes active_data is filled during initialization; qed") - .responses) - } - - /// Initialize. - pub fn initialize(&mut self, nodes: BTreeSet, self_response: Option, broadcast_self_response: bool) -> Result, Error> { - debug_assert!(self.meta.self_node_id == self.meta.master_node_id); - - if nodes.len() < self.meta.threshold + 1 { - return Err(if self.meta.configured_nodes_count < self.meta.threshold + 1 { - Error::ConsensusUnreachable - } else { - Error::ConsensusTemporaryUnreachable - }); - } - - if self.data.state != JobSessionState::Inactive { - return Err(Error::InvalidStateForRequest); - } - - // result from self - let active_data = ActiveJobSessionData { - requests: nodes.clone(), - rejects: BTreeMap::new(), - responses: BTreeMap::new(), - }; - let waits_for_self = active_data.requests.contains(&self.meta.self_node_id); - let self_response = match self_response { - Some(self_response) => Some(self_response), - None if waits_for_self => { - let partial_request = self.executor.prepare_partial_request(&self.meta.self_node_id, &active_data.requests)?; - let self_response = self.executor.process_partial_request(partial_request)?; - Some(self_response.take_response()) - }, - None => None, - }; - - // update state - self.data.active_data = Some(active_data); - self.data.state = JobSessionState::Active; - - // if we are waiting for response from self => do it - if let Some(self_response) = self_response.clone() { - let self_node_id = self.meta.self_node_id.clone(); - self.on_partial_response(&self_node_id, self_response)?; - } - - // send requests to save nodes. we only send requests if session is still active. - for node in nodes.iter().filter(|n| **n != self.meta.self_node_id) { - if self.data.state == JobSessionState::Active { - self.transport.send_partial_request(node, self.executor.prepare_partial_request(node, &nodes)?)?; - } - if broadcast_self_response { - if let Some(self_response) = self_response.clone() { - self.transport.send_partial_response(node, self_response)?; - } - } - } - - Ok(self_response) - } - - /// When partial request is received by slave node. - pub fn on_partial_request(&mut self, node: &NodeId, request: Executor::PartialJobRequest) -> Result, Error> { - if node != &self.meta.master_node_id { - return Err(Error::InvalidMessage); - } - if self.meta.self_node_id == self.meta.master_node_id { - return Err(Error::InvalidMessage); - } - if self.data.state != JobSessionState::Inactive && self.data.state != JobSessionState::Finished { - return Err(Error::InvalidStateForRequest); - } - - let partial_request_action = self.executor.process_partial_request(request)?; - let partial_response = match partial_request_action { - JobPartialRequestAction::Respond(ref partial_response) => { - self.data.state = JobSessionState::Finished; - partial_response.clone() - }, - JobPartialRequestAction::Reject(ref partial_response) => { - self.data.state = JobSessionState::Failed; - partial_response.clone() - }, - }; - self.transport.send_partial_response(node, partial_response)?; - Ok(partial_request_action) - } - - /// When partial request is received by master node. - pub fn on_partial_response(&mut self, node: &NodeId, response: Executor::PartialJobResponse) -> Result<(), Error> { - if self.meta.self_node_id != self.meta.master_node_id { - return Err(Error::InvalidMessage); - } - if self.data.state != JobSessionState::Active && self.data.state != JobSessionState::Finished { - return Err(Error::InvalidStateForRequest); - } - - let active_data = self.data.active_data.as_mut() - .expect("on_partial_response is only called on master nodes; on master nodes active_data is filled during initialization; qed"); - if !active_data.requests.remove(node) { - return Err(Error::InvalidNodeForRequest); - } - - match self.executor.check_partial_response(node, &response)? { - JobPartialResponseAction::Ignore => Ok(()), - JobPartialResponseAction::Reject => { - // direct reject is always considered as fatal - active_data.rejects.insert(node.clone(), true); - if active_data.requests.len() + active_data.responses.len() >= self.meta.threshold + 1 { - return Ok(()); - } - - self.data.state = JobSessionState::Failed; - Err(consensus_unreachable(&active_data.rejects)) - }, - JobPartialResponseAction::Accept => { - active_data.responses.insert(node.clone(), response); - if active_data.responses.len() < self.meta.threshold + 1 { - return Ok(()); - } - - self.data.state = JobSessionState::Finished; - Ok(()) - }, - } - } - - /// When error from node is received. - pub fn on_node_error(&mut self, node: &NodeId, error: Error) -> Result<(), Error> { - if self.meta.self_node_id != self.meta.master_node_id { - if node != &self.meta.master_node_id { - return Ok(()); - } - - self.data.state = JobSessionState::Failed; - return Err(if !error.is_non_fatal() { - Error::ConsensusUnreachable - } else { - Error::ConsensusTemporaryUnreachable - }); - } - - if let Some(active_data) = self.data.active_data.as_mut() { - if active_data.rejects.contains_key(node) { - return Ok(()); - } - if active_data.requests.remove(node) || active_data.responses.remove(node).is_some() { - active_data.rejects.insert(node.clone(), !error.is_non_fatal()); - if self.data.state == JobSessionState::Finished && active_data.responses.len() < self.meta.threshold + 1 { - self.data.state = JobSessionState::Active; - } - if active_data.requests.len() + active_data.responses.len() >= self.meta.threshold + 1 { - return Ok(()); - } - - self.data.state = JobSessionState::Failed; - return Err(consensus_unreachable(&active_data.rejects)); - } - } - - Ok(()) - } - - /// When session timeouted. - pub fn on_session_timeout(&mut self) -> Result<(), Error> { - if self.data.state == JobSessionState::Finished || self.data.state == JobSessionState::Failed { - return Ok(()); - } - - self.data.state = JobSessionState::Failed; - // we have started session => consensus is possible in theory, but now it has failed with timeout - Err(Error::ConsensusTemporaryUnreachable) - } -} - -impl JobPartialRequestAction { - /// Take actual response. - pub fn take_response(self) -> PartialJobResponse { - match self { - JobPartialRequestAction::Respond(response) => response, - JobPartialRequestAction::Reject(response) => response, - } - } -} - -/// Returns appropriate 'consensus unreachable' error. -fn consensus_unreachable(rejects: &BTreeMap) -> Error { - // when >= 50% of nodes have responded with fatal reject => ConsensusUnreachable - if rejects.values().filter(|r| **r).count() >= rejects.len() / 2 { - Error::ConsensusUnreachable - } else { - Error::ConsensusTemporaryUnreachable - } -} - -#[cfg(test)] -pub mod tests { - use std::collections::{VecDeque, BTreeMap, BTreeSet}; - use parking_lot::Mutex; - use crypto::publickey::Public; - use key_server_cluster::{Error, NodeId, SessionId, SessionMeta}; - use super::{JobPartialResponseAction, JobPartialRequestAction, JobExecutor, JobTransport, JobSession, JobSessionState}; - - pub struct SquaredSumJobExecutor; - - impl JobExecutor for SquaredSumJobExecutor { - type PartialJobRequest = u32; - type PartialJobResponse = u32; - type JobResponse = u32; - - fn prepare_partial_request(&self, _n: &NodeId, _nodes: &BTreeSet) -> Result { Ok(2) } - fn process_partial_request(&mut self, r: u32) -> Result, Error> { if r <= 10 { Ok(JobPartialRequestAction::Respond(r * r)) } else { Err(Error::InvalidMessage) } } - fn check_partial_response(&mut self, _s: &NodeId, r: &u32) -> Result { if r % 2 == 0 { Ok(JobPartialResponseAction::Accept) } else { Ok(JobPartialResponseAction::Reject) } } - fn compute_response(&self, r: &BTreeMap) -> Result { Ok(r.values().fold(0, |v1, v2| v1 + v2)) } - } - - #[derive(Default)] - pub struct DummyJobTransport { - pub requests: Mutex>, - pub responses: Mutex>, - } - - impl DummyJobTransport { - pub fn is_empty_response(&self) -> bool { - self.responses.lock().is_empty() - } - - pub fn response(&self) -> (NodeId, U) { - self.responses.lock().pop_front().unwrap() - } - } - - impl JobTransport for DummyJobTransport { - type PartialJobRequest = T; - type PartialJobResponse = U; - - fn send_partial_request(&self, node: &NodeId, request: T) -> Result<(), Error> { self.requests.lock().push_back((node.clone(), request)); Ok(()) } - fn send_partial_response(&self, node: &NodeId, response: U) -> Result<(), Error> { self.responses.lock().push_back((node.clone(), response)); Ok(()) } - } - - pub fn make_master_session_meta(threshold: usize) -> SessionMeta { - SessionMeta { id: SessionId::default(), master_node_id: NodeId::from_low_u64_be(1), self_node_id: NodeId::from_low_u64_be(1), threshold: threshold, - configured_nodes_count: 5, connected_nodes_count: 5 } - } - - pub fn make_slave_session_meta(threshold: usize) -> SessionMeta { - SessionMeta { id: SessionId::default(), master_node_id: NodeId::from_low_u64_be(1), self_node_id: NodeId::from_low_u64_be(2), threshold: threshold, - configured_nodes_count: 5, connected_nodes_count: 5 } - } - - #[test] - fn job_initialize_fails_if_not_enough_nodes_for_threshold_total() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.meta.configured_nodes_count = 1; - assert_eq!(job.initialize(vec![Public::from_low_u64_be(1)].into_iter().collect(), None, false).unwrap_err(), Error::ConsensusUnreachable); - } - - #[test] - fn job_initialize_fails_if_not_enough_nodes_for_threshold_connected() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.meta.connected_nodes_count = 3; - assert_eq!(job.initialize(vec![Public::from_low_u64_be(1)].into_iter().collect(), None, false).unwrap_err(), Error::ConsensusTemporaryUnreachable); - } - - #[test] - fn job_initialize_fails_if_not_inactive() { - let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.initialize(vec![Public::from_low_u64_be(1)].into_iter().collect(), None, false).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn job_initialization_leads_to_finish_if_single_node_is_required() { - let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Finished); - assert!(job.is_result_ready()); - assert_eq!(job.result(), Ok(4)); - } - - #[test] - fn job_initialization_does_not_leads_to_finish_if_single_other_node_is_required() { - let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - } - - #[test] - fn job_request_fails_if_comes_from_non_master_node() { - let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.on_partial_request(&NodeId::from_low_u64_be(3), 2).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn job_request_fails_if_comes_to_master_node() { - let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.on_partial_request(&NodeId::from_low_u64_be(1), 2).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn job_request_fails_if_comes_to_failed_state() { - let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.on_session_timeout().unwrap_err(); - assert_eq!(job.on_partial_request(&NodeId::from_low_u64_be(1), 2).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn job_request_succeeds_if_comes_to_finished_state() { - let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.on_partial_request(&NodeId::from_low_u64_be(1), 2).unwrap(); - assert_eq!(job.transport().response(), (NodeId::from_low_u64_be(1), 4)); - assert_eq!(job.state(), JobSessionState::Finished); - job.on_partial_request(&NodeId::from_low_u64_be(1), 3).unwrap(); - assert_eq!(job.transport().response(), (NodeId::from_low_u64_be(1), 9)); - assert_eq!(job.state(), JobSessionState::Finished); - } - - #[test] - fn job_response_fails_if_comes_to_slave_node() { - let mut job = JobSession::new(make_slave_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.on_partial_response(&NodeId::from_low_u64_be(1), 2).unwrap_err(), Error::InvalidMessage); - } - - #[test] - fn job_response_fails_if_comes_to_failed_state() { - let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - job.on_session_timeout().unwrap_err(); - assert_eq!(job.on_partial_response(&NodeId::from_low_u64_be(2), 2).unwrap_err(), Error::InvalidStateForRequest); - } - - #[test] - fn job_response_fails_if_comes_from_unknown_node() { - let mut job = JobSession::new(make_master_session_meta(0), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.on_partial_response(&NodeId::from_low_u64_be(3), 2).unwrap_err(), Error::InvalidNodeForRequest); - } - - #[test] - fn job_response_leads_to_failure_if_too_few_nodes_left() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - assert_eq!(job.on_partial_response(&NodeId::from_low_u64_be(2), 3).unwrap_err(), Error::ConsensusUnreachable); - assert_eq!(job.state(), JobSessionState::Failed); - } - - #[test] - fn job_response_succeeds() { - let mut job = JobSession::new(make_master_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - assert!(!job.is_result_ready()); - job.on_partial_response(&NodeId::from_low_u64_be(2), 2).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - assert!(!job.is_result_ready()); - } - - #[test] - fn job_response_leads_to_finish() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - job.on_partial_response(&NodeId::from_low_u64_be(2), 2).unwrap(); - assert_eq!(job.state(), JobSessionState::Finished); - } - - #[test] - fn job_node_error_ignored_when_slave_disconnects_from_slave() { - let mut job = JobSession::new(make_slave_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.state(), JobSessionState::Inactive); - job.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied).unwrap(); - assert_eq!(job.state(), JobSessionState::Inactive); - } - - #[test] - fn job_node_error_leads_to_fail_when_slave_disconnects_from_master() { - let mut job = JobSession::new(make_slave_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.state(), JobSessionState::Inactive); - assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(1), Error::AccessDenied).unwrap_err(), Error::ConsensusUnreachable); - assert_eq!(job.state(), JobSessionState::Failed); - } - - #[test] - fn job_node_error_ignored_when_disconnects_from_rejected() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - job.on_partial_response(&NodeId::from_low_u64_be(2), 3).unwrap(); - job.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - } - - #[test] - fn job_node_error_ignored_when_disconnects_from_unknown() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - job.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - } - - #[test] - fn job_node_error_ignored_when_disconnects_from_requested_and_enough_nodes_left() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - job.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - } - - #[test] - fn job_node_error_leads_to_fail_when_disconnects_from_requested_and_not_enough_nodes_left() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied).unwrap_err(), Error::ConsensusUnreachable); - assert_eq!(job.state(), JobSessionState::Failed); - } - - #[test] - fn job_broadcasts_self_response() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, true).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - assert_eq!(job.transport().response(), (NodeId::from_low_u64_be(2), 4)); - } - - #[test] - fn job_does_not_broadcasts_self_response() { - let mut job = JobSession::new(make_master_session_meta(1), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2)].into_iter().collect(), None, false).unwrap(); - assert_eq!(job.state(), JobSessionState::Active); - assert!(job.transport().is_empty_response()); - } - - #[test] - fn job_fails_with_temp_error_if_more_than_half_nodes_respond_with_temp_error() { - let mut job = JobSession::new(make_master_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3), Public::from_low_u64_be(4)].into_iter().collect(), None, false).unwrap(); - job.on_node_error(&NodeId::from_low_u64_be(2), Error::NodeDisconnected).unwrap(); - assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(3), Error::NodeDisconnected).unwrap_err(), Error::ConsensusTemporaryUnreachable); - } - - #[test] - fn job_fails_with_temp_error_if_more_than_half_rejects_are_temp() { - let mut job = JobSession::new(make_master_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3), Public::from_low_u64_be(4)].into_iter().collect(), None, false).unwrap(); - job.on_node_error(&NodeId::from_low_u64_be(2), Error::NodeDisconnected).unwrap(); - assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(3), Error::NodeDisconnected).unwrap_err(), Error::ConsensusTemporaryUnreachable); - } - - #[test] - fn job_fails_if_more_than_half_rejects_are_non_temp() { - let mut job = JobSession::new(make_master_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); - job.initialize(vec![Public::from_low_u64_be(1), Public::from_low_u64_be(2), Public::from_low_u64_be(3), Public::from_low_u64_be(4)].into_iter().collect(), None, false).unwrap(); - job.on_node_error(&NodeId::from_low_u64_be(2), Error::AccessDenied).unwrap(); - assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(3), Error::AccessDenied).unwrap_err(), Error::ConsensusUnreachable); - } - - #[test] - fn job_fails_with_temp_error_when_temp_error_is_reported_by_master_node() { - let mut job = JobSession::new(make_slave_session_meta(2), SquaredSumJobExecutor, DummyJobTransport::default()); - assert_eq!(job.on_node_error(&NodeId::from_low_u64_be(1), Error::NodeDisconnected).unwrap_err(), Error::ConsensusTemporaryUnreachable); - } -} diff --git a/secret-store/src/key_server_cluster/jobs/key_access_job.rs b/secret-store/src/key_server_cluster/jobs/key_access_job.rs deleted file mode 100644 index 4a30dd79f62..00000000000 --- a/secret-store/src/key_server_cluster/jobs/key_access_job.rs +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeSet, BTreeMap}; -use key_server_cluster::{Error, NodeId, SessionId, Requester, AclStorage}; -use key_server_cluster::jobs::job_session::{JobPartialResponseAction, JobPartialRequestAction, JobExecutor}; - -/// Purpose of this job is to construct set of nodes, which have agreed to provide access to the given key for the given requestor. -pub struct KeyAccessJob { - /// Key id. - id: SessionId, - /// Has key share? - has_key_share: bool, - /// ACL storage. - acl_storage: Arc, - /// Requester data. - requester: Option, -} - -impl KeyAccessJob { - pub fn new_on_slave(id: SessionId, acl_storage: Arc) -> Self { - KeyAccessJob { - id: id, - has_key_share: true, - acl_storage: acl_storage, - requester: None, - } - } - - pub fn new_on_master(id: SessionId, acl_storage: Arc, requester: Requester) -> Self { - KeyAccessJob { - id: id, - has_key_share: true, - acl_storage: acl_storage, - requester: Some(requester), - } - } - - pub fn set_has_key_share(&mut self, has_key_share: bool) { - self.has_key_share = has_key_share; - } - - pub fn set_requester(&mut self, requester: Requester) { - self.requester = Some(requester); - } - - pub fn requester(&self) -> Option<&Requester> { - self.requester.as_ref() - } -} - -impl JobExecutor for KeyAccessJob { - type PartialJobRequest = Requester; - type PartialJobResponse = bool; - type JobResponse = BTreeSet; - - fn prepare_partial_request(&self, _node: &NodeId, _nodes: &BTreeSet) -> Result { - Ok(self.requester.as_ref().expect("prepare_partial_request is only called on master nodes; new_on_master fills the signature; qed").clone()) - } - - fn process_partial_request(&mut self, partial_request: Requester) -> Result, Error> { - if !self.has_key_share { - return Ok(JobPartialRequestAction::Reject(false)); - } - - self.requester = Some(partial_request.clone()); - self.acl_storage.check(partial_request.address(&self.id).map_err(Error::InsufficientRequesterData)?, &self.id) - .map(|is_confirmed| if is_confirmed { JobPartialRequestAction::Respond(true) } else { JobPartialRequestAction::Reject(false) }) - } - - fn check_partial_response(&mut self, _sender: &NodeId, partial_response: &bool) -> Result { - Ok(if *partial_response { JobPartialResponseAction::Accept } else { JobPartialResponseAction::Reject }) - } - - fn compute_response(&self, partial_responses: &BTreeMap) -> Result, Error> { - Ok(partial_responses.keys().cloned().collect()) - } -} diff --git a/secret-store/src/key_server_cluster/jobs/mod.rs b/secret-store/src/key_server_cluster/jobs/mod.rs deleted file mode 100644 index 4e3b8c0d96a..00000000000 --- a/secret-store/src/key_server_cluster/jobs/mod.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -pub mod consensus_session; -pub mod decryption_job; -pub mod dummy_job; -pub mod job_session; -pub mod key_access_job; -pub mod servers_set_change_access_job; -pub mod signing_job_ecdsa; -pub mod signing_job_schnorr; -pub mod unknown_sessions_job; diff --git a/secret-store/src/key_server_cluster/jobs/servers_set_change_access_job.rs b/secret-store/src/key_server_cluster/jobs/servers_set_change_access_job.rs deleted file mode 100644 index 52e8c95dfb7..00000000000 --- a/secret-store/src/key_server_cluster/jobs/servers_set_change_access_job.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use crypto::publickey::{Public, Signature, recover}; -use tiny_keccak::Keccak; -use key_server_cluster::{Error, NodeId, SessionId}; -use key_server_cluster::message::{InitializeConsensusSessionWithServersSet, InitializeConsensusSessionOfShareAdd}; -use key_server_cluster::jobs::job_session::{JobPartialResponseAction, JobPartialRequestAction, JobExecutor}; - -/// Purpose of this job is to check if requestor is administrator of SecretStore (i.e. it have access to change key servers set). -pub struct ServersSetChangeAccessJob { - /// Servers set administrator public key (this could be changed to ACL-based check later). - administrator: Public, - /// Old servers set. - old_servers_set: Option>, - /// New servers set. - new_servers_set: Option>, - /// Old servers set, signed by requester. - old_set_signature: Option, - /// New servers set, signed by requester. - new_set_signature: Option, -} - -/// Servers set change job partial request. -pub struct ServersSetChangeAccessRequest { - /// Old servers set. - pub old_servers_set: BTreeSet, - /// New servers set. - pub new_servers_set: BTreeSet, - /// Hash(old_servers_set), signed by requester. - pub old_set_signature: Signature, - /// Hash(new_servers_set), signed by requester. - pub new_set_signature: Signature, -} - -impl<'a> From<&'a InitializeConsensusSessionWithServersSet> for ServersSetChangeAccessRequest { - fn from(message: &InitializeConsensusSessionWithServersSet) -> Self { - ServersSetChangeAccessRequest { - old_servers_set: message.old_nodes_set.iter().cloned().map(Into::into).collect(), - new_servers_set: message.new_nodes_set.iter().cloned().map(Into::into).collect(), - old_set_signature: message.old_set_signature.clone().into(), - new_set_signature: message.new_set_signature.clone().into(), - } - } -} - -impl<'a> From<&'a InitializeConsensusSessionOfShareAdd> for ServersSetChangeAccessRequest { - fn from(message: &InitializeConsensusSessionOfShareAdd) -> Self { - ServersSetChangeAccessRequest { - old_servers_set: message.old_nodes_set.iter().cloned().map(Into::into).collect(), - new_servers_set: message.new_nodes_map.keys().cloned().map(Into::into).collect(), - old_set_signature: message.old_set_signature.clone().into(), - new_set_signature: message.new_set_signature.clone().into(), - } - } -} - -impl ServersSetChangeAccessJob { - pub fn new_on_slave(administrator: Public) -> Self { - ServersSetChangeAccessJob { - administrator: administrator, - old_servers_set: None, - new_servers_set: None, - old_set_signature: None, - new_set_signature: None, - } - } - - pub fn new_on_master(administrator: Public, old_servers_set: BTreeSet, new_servers_set: BTreeSet, old_set_signature: Signature, new_set_signature: Signature) -> Self { - ServersSetChangeAccessJob { - administrator: administrator, - old_servers_set: Some(old_servers_set), - new_servers_set: Some(new_servers_set), - old_set_signature: Some(old_set_signature), - new_set_signature: Some(new_set_signature), - } - } - - pub fn new_servers_set(&self) -> Option<&BTreeSet> { - self.new_servers_set.as_ref() - } -} - -impl JobExecutor for ServersSetChangeAccessJob { - type PartialJobRequest = ServersSetChangeAccessRequest; - type PartialJobResponse = bool; - type JobResponse = BTreeSet; - - fn prepare_partial_request(&self, _node: &NodeId, _nodes: &BTreeSet) -> Result { - let explanation = "prepare_partial_request is only called on master nodes; this field is filled on master nodes in constructor; qed"; - Ok(ServersSetChangeAccessRequest { - old_servers_set: self.old_servers_set.clone().expect(explanation), - new_servers_set: self.new_servers_set.clone().expect(explanation), - old_set_signature: self.old_set_signature.clone().expect(explanation), - new_set_signature: self.new_set_signature.clone().expect(explanation), - }) - } - - fn process_partial_request(&mut self, partial_request: ServersSetChangeAccessRequest) -> Result, Error> { - let ServersSetChangeAccessRequest { - old_servers_set, - new_servers_set, - old_set_signature, - new_set_signature, - } = partial_request; - - // check old servers set signature - let old_actual_public = recover(&old_set_signature, &ordered_nodes_hash(&old_servers_set).into())?; - let new_actual_public = recover(&new_set_signature, &ordered_nodes_hash(&new_servers_set).into())?; - let is_administrator = old_actual_public == self.administrator && new_actual_public == self.administrator; - self.new_servers_set = Some(new_servers_set); - - Ok(if is_administrator { JobPartialRequestAction::Respond(true) } else { JobPartialRequestAction::Reject(false) }) - } - - fn check_partial_response(&mut self, _sender: &NodeId, partial_response: &bool) -> Result { - Ok(if *partial_response { JobPartialResponseAction::Accept } else { JobPartialResponseAction::Reject }) - } - - fn compute_response(&self, partial_responses: &BTreeMap) -> Result, Error> { - Ok(partial_responses.keys().cloned().collect()) - } -} - -pub fn ordered_nodes_hash(nodes: &BTreeSet) -> SessionId { - let mut nodes_keccak = Keccak::new_keccak256(); - for node in nodes { - nodes_keccak.update(node.as_bytes()); - } - - let mut nodes_keccak_value = [0u8; 32]; - nodes_keccak.finalize(&mut nodes_keccak_value); - - nodes_keccak_value.into() -} diff --git a/secret-store/src/key_server_cluster/jobs/signing_job_ecdsa.rs b/secret-store/src/key_server_cluster/jobs/signing_job_ecdsa.rs deleted file mode 100644 index ecb509ad1b0..00000000000 --- a/secret-store/src/key_server_cluster/jobs/signing_job_ecdsa.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use crypto::publickey::{Public, Secret, Signature}; -use ethereum_types::H256; -use key_server_cluster::{Error, NodeId, DocumentKeyShare}; -use key_server_cluster::math; -use key_server_cluster::jobs::job_session::{JobPartialRequestAction, JobPartialResponseAction, JobExecutor}; - -/// Signing job. -pub struct EcdsaSigningJob { - /// Key share. - key_share: DocumentKeyShare, - /// Key version. - key_version: H256, - /// Share of inv(nonce). - inv_nonce_share: Secret, - /// Nonce public. - nonce_public: Public, - /// Request id. - request_id: Option, - /// ECDSA reversed-nonce coefficient - inversed_nonce_coeff: Option, - /// Message hash. - message_hash: Option, -} - -/// Signing job partial request. -pub struct EcdsaPartialSigningRequest { - /// Request id. - pub id: Secret, - /// ECDSA reversed-nonce coefficient - pub inversed_nonce_coeff: Secret, - /// Message hash to sign. - pub message_hash: H256, -} - -/// Signing job partial response. -#[derive(Clone)] -pub struct EcdsaPartialSigningResponse { - /// Request id. - pub request_id: Secret, - /// Partial signature' s share. - pub partial_signature_s: Secret, -} - -impl EcdsaSigningJob { - pub fn new_on_slave(key_share: DocumentKeyShare, key_version: H256, nonce_public: Public, inv_nonce_share: Secret) -> Result { - Ok(EcdsaSigningJob { - key_share: key_share, - key_version: key_version, - nonce_public: nonce_public, - inv_nonce_share: inv_nonce_share, - request_id: None, - inversed_nonce_coeff: None, - message_hash: None, - }) - } - - pub fn new_on_master(key_share: DocumentKeyShare, key_version: H256, nonce_public: Public, inv_nonce_share: Secret, inversed_nonce_coeff: Secret, message_hash: H256) -> Result { - Ok(EcdsaSigningJob { - key_share: key_share, - key_version: key_version, - nonce_public: nonce_public, - inv_nonce_share: inv_nonce_share, - request_id: Some(math::generate_random_scalar()?), - inversed_nonce_coeff: Some(inversed_nonce_coeff), - message_hash: Some(message_hash), - }) - } -} - -impl JobExecutor for EcdsaSigningJob { - type PartialJobRequest = EcdsaPartialSigningRequest; - type PartialJobResponse = EcdsaPartialSigningResponse; - type JobResponse = Signature; - - fn prepare_partial_request(&self, _node: &NodeId, nodes: &BTreeSet) -> Result { - debug_assert!(nodes.len() == self.key_share.threshold * 2 + 1); - - let request_id = self.request_id.as_ref() - .expect("prepare_partial_request is only called on master nodes; request_id is filed in constructor on master nodes; qed"); - let inversed_nonce_coeff = self.inversed_nonce_coeff.as_ref() - .expect("prepare_partial_request is only called on master nodes; inversed_nonce_coeff is filed in constructor on master nodes; qed"); - let message_hash = self.message_hash.as_ref() - .expect("compute_response is only called on master nodes; message_hash is filed in constructor on master nodes; qed"); - - Ok(EcdsaPartialSigningRequest { - id: request_id.clone(), - inversed_nonce_coeff: inversed_nonce_coeff.clone(), - message_hash: message_hash.clone(), - }) - } - - fn process_partial_request(&mut self, partial_request: EcdsaPartialSigningRequest) -> Result, Error> { - let inversed_nonce_coeff_mul_nonce = math::compute_secret_mul(&partial_request.inversed_nonce_coeff, &self.inv_nonce_share)?; - let key_version = self.key_share.version(&self.key_version)?; - let signature_r = math::compute_ecdsa_r(&self.nonce_public)?; - let inv_nonce_mul_secret = math::compute_secret_mul(&inversed_nonce_coeff_mul_nonce, &key_version.secret_share)?; - let partial_signature_s = math::compute_ecdsa_s_share( - &inversed_nonce_coeff_mul_nonce, - &inv_nonce_mul_secret, - &signature_r, - &math::to_scalar(partial_request.message_hash)?, - )?; - - Ok(JobPartialRequestAction::Respond(EcdsaPartialSigningResponse { - request_id: partial_request.id, - partial_signature_s: partial_signature_s, - })) - } - - fn check_partial_response(&mut self, _sender: &NodeId, partial_response: &EcdsaPartialSigningResponse) -> Result { - if Some(&partial_response.request_id) != self.request_id.as_ref() { - return Ok(JobPartialResponseAction::Ignore); - } - // TODO [Trust]: check_ecdsa_signature_share() - - Ok(JobPartialResponseAction::Accept) - } - - fn compute_response(&self, partial_responses: &BTreeMap) -> Result { - let key_version = self.key_share.version(&self.key_version)?; - if partial_responses.keys().any(|n| !key_version.id_numbers.contains_key(n)) { - return Err(Error::InvalidMessage); - } - - let id_numbers: Vec<_> = partial_responses.keys().map(|n| key_version.id_numbers[n].clone()).collect(); - let signature_s_shares: Vec<_> = partial_responses.values().map(|r| r.partial_signature_s.clone()).collect(); - let signature_s = math::compute_ecdsa_s(self.key_share.threshold, &signature_s_shares, &id_numbers)?; - let signature_r = math::compute_ecdsa_r(&self.nonce_public)?; - - let signature = math::serialize_ecdsa_signature(&self.nonce_public, signature_r, signature_s); - - Ok(signature) - } -} diff --git a/secret-store/src/key_server_cluster/jobs/signing_job_schnorr.rs b/secret-store/src/key_server_cluster/jobs/signing_job_schnorr.rs deleted file mode 100644 index 678d9f32a8e..00000000000 --- a/secret-store/src/key_server_cluster/jobs/signing_job_schnorr.rs +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::{BTreeSet, BTreeMap}; -use crypto::publickey::{Public, Secret}; -use ethereum_types::H256; -use key_server_cluster::{Error, NodeId, DocumentKeyShare}; -use key_server_cluster::math; -use key_server_cluster::jobs::job_session::{JobPartialRequestAction, JobPartialResponseAction, JobExecutor}; - -/// Signing job. -pub struct SchnorrSigningJob { - /// This node id. - self_node_id: NodeId, - /// Key share. - key_share: DocumentKeyShare, - /// Key version. - key_version: H256, - /// Session public key. - session_public: Public, - /// Session secret coefficient. - session_secret_coeff: Secret, - /// Request id. - request_id: Option, - /// Message hash. - message_hash: Option, -} - -/// Signing job partial request. -pub struct SchnorrPartialSigningRequest { - /// Request id. - pub id: Secret, - /// Message hash. - pub message_hash: H256, - /// Id of other nodes, participating in signing. - pub other_nodes_ids: BTreeSet, -} - -/// Signing job partial response. -#[derive(Clone)] -pub struct SchnorrPartialSigningResponse { - /// Request id. - pub request_id: Secret, - /// Partial signature. - pub partial_signature: Secret, -} - -impl SchnorrSigningJob { - pub fn new_on_slave(self_node_id: NodeId, key_share: DocumentKeyShare, key_version: H256, session_public: Public, session_secret_coeff: Secret) -> Result { - Ok(SchnorrSigningJob { - self_node_id: self_node_id, - key_share: key_share, - key_version: key_version, - session_public: session_public, - session_secret_coeff: session_secret_coeff, - request_id: None, - message_hash: None, - }) - } - - pub fn new_on_master(self_node_id: NodeId, key_share: DocumentKeyShare, key_version: H256, session_public: Public, session_secret_coeff: Secret, message_hash: H256) -> Result { - Ok(SchnorrSigningJob { - self_node_id: self_node_id, - key_share: key_share, - key_version: key_version, - session_public: session_public, - session_secret_coeff: session_secret_coeff, - request_id: Some(math::generate_random_scalar()?), - message_hash: Some(message_hash), - }) - } -} - -impl JobExecutor for SchnorrSigningJob { - type PartialJobRequest = SchnorrPartialSigningRequest; - type PartialJobResponse = SchnorrPartialSigningResponse; - type JobResponse = (Secret, Secret); - - fn prepare_partial_request(&self, node: &NodeId, nodes: &BTreeSet) -> Result { - debug_assert!(nodes.len() == self.key_share.threshold + 1); - - let request_id = self.request_id.as_ref() - .expect("prepare_partial_request is only called on master nodes; request_id is filed in constructor on master nodes; qed"); - let message_hash = self.message_hash.as_ref() - .expect("compute_response is only called on master nodes; message_hash is filed in constructor on master nodes; qed"); - let mut other_nodes_ids = nodes.clone(); - other_nodes_ids.remove(node); - - Ok(SchnorrPartialSigningRequest { - id: request_id.clone(), - message_hash: message_hash.clone(), - other_nodes_ids: other_nodes_ids, - }) - } - - fn process_partial_request(&mut self, partial_request: SchnorrPartialSigningRequest) -> Result, Error> { - let key_version = self.key_share.version(&self.key_version)?; - if partial_request.other_nodes_ids.len() != self.key_share.threshold - || partial_request.other_nodes_ids.contains(&self.self_node_id) - || partial_request.other_nodes_ids.iter().any(|n| !key_version.id_numbers.contains_key(n)) { - return Err(Error::InvalidMessage); - } - - let self_id_number = &key_version.id_numbers[&self.self_node_id]; - let other_id_numbers = partial_request.other_nodes_ids.iter().map(|n| &key_version.id_numbers[n]); - let combined_hash = math::combine_message_hash_with_public(&partial_request.message_hash, &self.session_public)?; - Ok(JobPartialRequestAction::Respond(SchnorrPartialSigningResponse { - request_id: partial_request.id, - partial_signature: math::compute_schnorr_signature_share( - self.key_share.threshold, - &combined_hash, - &self.session_secret_coeff, - &key_version.secret_share, - self_id_number, - other_id_numbers - )?, - })) - } - - fn check_partial_response(&mut self, _sender: &NodeId, partial_response: &SchnorrPartialSigningResponse) -> Result { - if Some(&partial_response.request_id) != self.request_id.as_ref() { - return Ok(JobPartialResponseAction::Ignore); - } - // TODO [Trust]: check_schnorr_signature_share() - - Ok(JobPartialResponseAction::Accept) - } - - fn compute_response(&self, partial_responses: &BTreeMap) -> Result<(Secret, Secret), Error> { - let message_hash = self.message_hash.as_ref() - .expect("compute_response is only called on master nodes; message_hash is filed in constructor on master nodes; qed"); - - let signature_c = math::combine_message_hash_with_public(message_hash, &self.session_public)?; - let signature_s = math::compute_schnorr_signature(partial_responses.values().map(|r| &r.partial_signature))?; - - Ok((signature_c, signature_s)) - } -} diff --git a/secret-store/src/key_server_cluster/jobs/unknown_sessions_job.rs b/secret-store/src/key_server_cluster/jobs/unknown_sessions_job.rs deleted file mode 100644 index b9a366ee206..00000000000 --- a/secret-store/src/key_server_cluster/jobs/unknown_sessions_job.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::{BTreeSet, BTreeMap}; -use key_server_cluster::{Error, NodeId, SessionId, KeyStorage}; -use key_server_cluster::jobs::job_session::{JobPartialRequestAction, JobPartialResponseAction, JobExecutor}; - -/// Unknown sessions report job. -pub struct UnknownSessionsJob { - /// Target node id. - target_node_id: Option, - /// Keys storage. - key_storage: Arc, -} - -impl UnknownSessionsJob { - pub fn new_on_slave(key_storage: Arc) -> Self { - UnknownSessionsJob { - target_node_id: None, - key_storage: key_storage, - } - } - - pub fn new_on_master(key_storage: Arc, self_node_id: NodeId) -> Self { - UnknownSessionsJob { - target_node_id: Some(self_node_id), - key_storage: key_storage, - } - } -} - -impl JobExecutor for UnknownSessionsJob { - type PartialJobRequest = NodeId; - type PartialJobResponse = BTreeSet; - type JobResponse = BTreeMap>; - - fn prepare_partial_request(&self, _node: &NodeId, _nodes: &BTreeSet) -> Result { - Ok(self.target_node_id.clone().expect("prepare_partial_request is only called on master nodes; this field is filled on master nodes in constructor; qed")) - } - - fn process_partial_request(&mut self, partial_request: NodeId) -> Result>, Error> { - Ok(JobPartialRequestAction::Respond(self.key_storage.iter() - .filter(|&(_, ref key_share)| !key_share.versions.last().map(|v| v.id_numbers.contains_key(&partial_request)).unwrap_or(true)) - .map(|(id, _)| id.clone()) - .collect())) - } - - fn check_partial_response(&mut self, _sender: &NodeId, _partial_response: &BTreeSet) -> Result { - Ok(JobPartialResponseAction::Accept) - } - - // TODO [Opt]: - // currently ALL unknown sessions are sent at once - it is better to limit messages by size/len => add partial-partial responses - fn compute_response(&self, partial_responses: &BTreeMap>) -> Result>, Error> { - let mut result: BTreeMap> = BTreeMap::new(); - for (node_id, node_sessions) in partial_responses { - for node_session in node_sessions { - result.entry(node_session.clone()) - .or_insert_with(Default::default) - .insert(node_id.clone()); - } - } - - Ok(result) - } -} diff --git a/secret-store/src/key_server_cluster/math.rs b/secret-store/src/key_server_cluster/math.rs deleted file mode 100644 index d48fb9e918d..00000000000 --- a/secret-store/src/key_server_cluster/math.rs +++ /dev/null @@ -1,1082 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use crypto::publickey::{Public, Secret, Signature, Random, Generator, ec_math_utils}; -use ethereum_types::{H256, U256, BigEndianHash}; -use hash::keccak; -use key_server_cluster::Error; - -/// Encryption result. -#[derive(Debug)] -pub struct EncryptedSecret { - /// Common encryption point. - pub common_point: Public, - /// Ecnrypted point. - pub encrypted_point: Public, -} - -/// Create zero scalar. -pub fn zero_scalar() -> Secret { - Secret::zero() -} - -/// Convert hash to EC scalar (modulo curve order). -pub fn to_scalar(hash: H256) -> Result { - let scalar: U256 = hash.into_uint(); - let scalar: H256 = BigEndianHash::from_uint(&(scalar % *ec_math_utils::CURVE_ORDER)); - let scalar = Secret::from(scalar.0); - scalar.check_validity()?; - Ok(scalar) -} - -/// Generate random scalar. -pub fn generate_random_scalar() -> Result { - Ok(Random.generate()?.secret().clone()) -} - -/// Generate random point. -pub fn generate_random_point() -> Result { - Ok(Random.generate()?.public().clone()) -} - -/// Get X coordinate of point. -fn public_x(public: &Public) -> H256 { - H256::from_slice(&public.as_bytes()[0..32]) -} - -/// Get Y coordinate of point. -fn public_y(public: &Public) -> H256 { - H256::from_slice(&public.as_bytes()[32..64]) -} - -/// Compute publics sum. -pub fn compute_public_sum<'a, I>(mut publics: I) -> Result where I: Iterator { - let mut sum = publics.next().expect("compute_public_sum is called when there's at least one public; qed").clone(); - while let Some(public) = publics.next() { - ec_math_utils::public_add(&mut sum, &public)?; - } - Ok(sum) -} - -/// Compute secrets sum. -pub fn compute_secret_sum<'a, I>(mut secrets: I) -> Result where I: Iterator { - let mut sum = secrets.next().expect("compute_secret_sum is called when there's at least one secret; qed").clone(); - while let Some(secret) = secrets.next() { - sum.add(secret)?; - } - Ok(sum) -} - -/// Compute secrets multiplication. -pub fn compute_secret_mul(secret1: &Secret, secret2: &Secret) -> Result { - let mut secret_mul = secret1.clone(); - secret_mul.mul(secret2)?; - Ok(secret_mul) -} - -/// Compute secrets 'shadow' multiplication: coeff * multiplication(s[j] / (s[i] - s[j])) for every i != j -pub fn compute_shadow_mul<'a, I>(coeff: &Secret, self_secret: &Secret, mut other_secrets: I) -> Result where I: Iterator { - // when there are no other secrets, only coeff is left - let other_secret = match other_secrets.next() { - Some(other_secret) => other_secret, - None => return Ok(coeff.clone()), - }; - - let mut shadow_mul = self_secret.clone(); - shadow_mul.sub(other_secret)?; - shadow_mul.inv()?; - shadow_mul.mul(other_secret)?; - while let Some(other_secret) = other_secrets.next() { - let mut shadow_mul_element = self_secret.clone(); - shadow_mul_element.sub(other_secret)?; - shadow_mul_element.inv()?; - shadow_mul_element.mul(other_secret)?; - shadow_mul.mul(&shadow_mul_element)?; - } - - shadow_mul.mul(coeff)?; - Ok(shadow_mul) -} - -/// Update point by multiplying to random scalar -pub fn update_random_point(point: &mut Public) -> Result<(), Error> { - Ok(ec_math_utils::public_mul_secret(point, &generate_random_scalar()?)?) -} - -/// Generate random polynom of threshold degree -pub fn generate_random_polynom(threshold: usize) -> Result, Error> { - (0..threshold + 1) - .map(|_| generate_random_scalar()) - .collect() -} - -/// Compute value of polynom, using `node_number` as argument -pub fn compute_polynom(polynom: &[Secret], node_number: &Secret) -> Result { - debug_assert!(!polynom.is_empty()); - - let mut result = polynom[0].clone(); - for i in 1..polynom.len() { - // calculate pow(node_number, i) - let mut appendum = node_number.clone(); - appendum.pow(i)?; - - // calculate coeff * pow(point, i) - appendum.mul(&polynom[i])?; - - // calculate result + coeff * pow(point, i) - result.add(&appendum)?; - } - - Ok(result) -} - -/// Generate public keys for other participants. -pub fn public_values_generation(threshold: usize, derived_point: &Public, polynom1: &[Secret], polynom2: &[Secret]) -> Result, Error> { - debug_assert_eq!(polynom1.len(), threshold + 1); - debug_assert_eq!(polynom2.len(), threshold + 1); - - // compute t+1 public values - let mut publics = Vec::with_capacity(threshold + 1); - for i in 0..threshold + 1 { - let coeff1 = &polynom1[i]; - - let mut multiplication1 = ec_math_utils::generation_point(); - ec_math_utils::public_mul_secret(&mut multiplication1, &coeff1)?; - - let coeff2 = &polynom2[i]; - let mut multiplication2 = derived_point.clone(); - ec_math_utils::public_mul_secret(&mut multiplication2, &coeff2)?; - - ec_math_utils::public_add(&mut multiplication1, &multiplication2)?; - - publics.push(multiplication1); - } - debug_assert_eq!(publics.len(), threshold + 1); - - Ok(publics) -} - -/// Check keys passed by other participants. -pub fn keys_verification(threshold: usize, derived_point: &Public, number_id: &Secret, secret1: &Secret, secret2: &Secret, publics: &[Public]) -> Result { - // calculate left part - let mut multiplication1 = ec_math_utils::generation_point(); - ec_math_utils::public_mul_secret(&mut multiplication1, secret1)?; - - let mut multiplication2 = derived_point.clone(); - ec_math_utils::public_mul_secret(&mut multiplication2, secret2)?; - - ec_math_utils::public_add(&mut multiplication1, &multiplication2)?; - let left = multiplication1; - - // calculate right part - let mut right = publics[0].clone(); - for i in 1..threshold + 1 { - let mut secret_pow = number_id.clone(); - secret_pow.pow(i)?; - - let mut public_k = publics[i].clone(); - ec_math_utils::public_mul_secret(&mut public_k, &secret_pow)?; - - ec_math_utils::public_add(&mut right, &public_k)?; - } - - Ok(left == right) -} - -/// Compute secret subshare from passed secret value. -pub fn compute_secret_subshare<'a, I>(threshold: usize, secret_value: &Secret, sender_id_number: &Secret, other_id_numbers: I) -> Result where I: Iterator { - let mut subshare = compute_shadow_mul(secret_value, sender_id_number, other_id_numbers)?; - if threshold % 2 != 0 { - subshare.neg()?; - } - - Ok(subshare) -} - -/// Compute secret share. -pub fn compute_secret_share<'a, I>(secret_values: I) -> Result where I: Iterator { - compute_secret_sum(secret_values) -} - -/// Compute public key share. -pub fn compute_public_share(self_secret_value: &Secret) -> Result { - let mut public_share = ec_math_utils::generation_point(); - ec_math_utils::public_mul_secret(&mut public_share, self_secret_value)?; - Ok(public_share) -} - -/// Compute joint public key. -pub fn compute_joint_public<'a, I>(public_shares: I) -> Result where I: Iterator { - compute_public_sum(public_shares) -} - -/// Compute joint secret key from N secret coefficients. -#[cfg(test)] -pub fn compute_joint_secret<'a, I>(secret_coeffs: I) -> Result where I: Iterator { - compute_secret_sum(secret_coeffs) -} - -/// Compute joint secret key from t+1 secret shares. -pub fn compute_joint_secret_from_shares<'a>(t: usize, secret_shares: &[&'a Secret], id_numbers: &[&'a Secret]) -> Result { - let secret_share_0 = secret_shares[0]; - let id_number_0 = id_numbers[0]; - let other_nodes_numbers = id_numbers.iter().skip(1).cloned(); - let mut result = compute_node_shadow(secret_share_0, id_number_0, other_nodes_numbers)?; - for i in 1..secret_shares.len() { - let secret_share_i = secret_shares[i]; - let id_number_i = id_numbers[i]; - let other_nodes_numbers = id_numbers.iter().enumerate().filter(|&(j, _)| j != i).map(|(_, n)| n).cloned(); - let addendum = compute_node_shadow(secret_share_i, id_number_i, other_nodes_numbers)?; - result.add(&addendum)?; - } - - if t % 2 != 0 { - result.neg()?; - } - - Ok(result) -} - -/// Encrypt secret with joint public key. -pub fn encrypt_secret(secret: &Public, joint_public: &Public) -> Result { - // this is performed by KS-cluster client (or KS master) - let key_pair = Random.generate()?; - - // k * T - let mut common_point = ec_math_utils::generation_point(); - ec_math_utils::public_mul_secret(&mut common_point, key_pair.secret())?; - - // M + k * y - let mut encrypted_point = joint_public.clone(); - ec_math_utils::public_mul_secret(&mut encrypted_point, key_pair.secret())?; - ec_math_utils::public_add(&mut encrypted_point, secret)?; - - Ok(EncryptedSecret { - common_point: common_point, - encrypted_point: encrypted_point, - }) -} - -/// Compute shadow for the node. -pub fn compute_node_shadow<'a, I>(node_secret_share: &Secret, node_number: &Secret, other_nodes_numbers: I) -> Result where I: Iterator { - compute_shadow_mul(node_secret_share, node_number, other_nodes_numbers) -} - -/// Compute shadow point for the node. -pub fn compute_node_shadow_point(access_key: &Secret, common_point: &Public, node_shadow: &Secret, decrypt_shadow: Option) -> Result<(Public, Option), Error> { - let mut shadow_key = node_shadow.clone(); - let decrypt_shadow = match decrypt_shadow { - None => None, - Some(mut decrypt_shadow) => { - // update shadow key - shadow_key.mul(&decrypt_shadow)?; - // now udate decrypt shadow itself - decrypt_shadow.dec()?; - decrypt_shadow.mul(node_shadow)?; - Some(decrypt_shadow) - } - }; - shadow_key.mul(access_key)?; - - let mut node_shadow_point = common_point.clone(); - ec_math_utils::public_mul_secret(&mut node_shadow_point, &shadow_key)?; - Ok((node_shadow_point, decrypt_shadow)) -} - -/// Compute joint shadow point. -pub fn compute_joint_shadow_point<'a, I>(nodes_shadow_points: I) -> Result where I: Iterator { - compute_public_sum(nodes_shadow_points) -} - -/// Compute joint shadow point (version for tests). -#[cfg(test)] -pub fn compute_joint_shadow_point_test<'a, I>(access_key: &Secret, common_point: &Public, nodes_shadows: I) -> Result where I: Iterator { - let mut joint_shadow = compute_secret_sum(nodes_shadows)?; - joint_shadow.mul(access_key)?; - - let mut joint_shadow_point = common_point.clone(); - ec_math_utils::public_mul_secret(&mut joint_shadow_point, &joint_shadow)?; - Ok(joint_shadow_point) -} - -/// Decrypt data using joint shadow point. -pub fn decrypt_with_joint_shadow(threshold: usize, access_key: &Secret, encrypted_point: &Public, joint_shadow_point: &Public) -> Result { - let mut inv_access_key = access_key.clone(); - inv_access_key.inv()?; - - let mut mul = joint_shadow_point.clone(); - ec_math_utils::public_mul_secret(&mut mul, &inv_access_key)?; - - let mut decrypted_point = encrypted_point.clone(); - if threshold % 2 != 0 { - ec_math_utils::public_add(&mut decrypted_point, &mul)?; - } else { - ec_math_utils::public_sub(&mut decrypted_point, &mul)?; - } - - Ok(decrypted_point) -} - -/// Prepare common point for shadow decryption. -pub fn make_common_shadow_point(threshold: usize, mut common_point: Public) -> Result { - if threshold % 2 != 1 { - Ok(common_point) - } else { - ec_math_utils::public_negate(&mut common_point)?; - Ok(common_point) - } -} - -/// Decrypt shadow-encrypted secret. -#[cfg(test)] -pub fn decrypt_with_shadow_coefficients(mut decrypted_shadow: Public, mut common_shadow_point: Public, shadow_coefficients: Vec) -> Result { - let shadow_coefficients_sum = compute_secret_sum(shadow_coefficients.iter())?; - ec_math_utils::public_mul_secret(&mut common_shadow_point, &shadow_coefficients_sum)?; - ec_math_utils::public_add(&mut decrypted_shadow, &common_shadow_point)?; - Ok(decrypted_shadow) -} - -/// Decrypt data using joint secret (version for tests). -#[cfg(test)] -pub fn decrypt_with_joint_secret(encrypted_point: &Public, common_point: &Public, joint_secret: &Secret) -> Result { - let mut common_point_mul = common_point.clone(); - ec_math_utils::public_mul_secret(&mut common_point_mul, joint_secret)?; - - let mut decrypted_point = encrypted_point.clone(); - ec_math_utils::public_sub(&mut decrypted_point, &common_point_mul)?; - - Ok(decrypted_point) -} - -/// Combine message hash with public key X coordinate. -pub fn combine_message_hash_with_public(message_hash: &H256, public: &Public) -> Result { - // buffer is just [message_hash | public.x] - let mut buffer = [0; 64]; - buffer[0..32].copy_from_slice(&message_hash[0..32]); - buffer[32..64].copy_from_slice(&public[0..32]); - - // calculate hash of buffer - let hash = keccak(&buffer[..]); - - // map hash to EC finite field value - to_scalar(hash) -} - -/// Compute Schnorr signature share. -pub fn compute_schnorr_signature_share<'a, I>(threshold: usize, combined_hash: &Secret, one_time_secret_coeff: &Secret, node_secret_share: &Secret, node_number: &Secret, other_nodes_numbers: I) - -> Result where I: Iterator { - let mut sum = one_time_secret_coeff.clone(); - let mut subtrahend = compute_shadow_mul(combined_hash, node_number, other_nodes_numbers)?; - subtrahend.mul(node_secret_share)?; - if threshold % 2 == 0 { - sum.sub(&subtrahend)?; - } else { - sum.add(&subtrahend)?; - } - Ok(sum) -} - -/// Check Schnorr signature share. -pub fn _check_schnorr_signature_share<'a, I>(_combined_hash: &Secret, _signature_share: &Secret, _public_share: &Public, _one_time_public_share: &Public, _node_numbers: I) - -> Result where I: Iterator { - // TODO [Trust]: in paper partial signature is checked using comparison: - // sig[i] * T = r[i] - c * lagrange_coeff(i) * y[i] - // => (k[i] - c * lagrange_coeff(i) * s[i]) * T = r[i] - c * lagrange_coeff(i) * y[i] - // => k[i] * T - c * lagrange_coeff(i) * s[i] * T = k[i] * T - c * lagrange_coeff(i) * y[i] - // => this means that y[i] = s[i] * T - // but when verifying signature (for t = 1), nonce public (r) is restored using following expression: - // r = (sig[0] + sig[1]) * T - c * y - // r = (k[0] - c * lagrange_coeff(0) * s[0] + k[1] - c * lagrange_coeff(1) * s[1]) * T - c * y - // r = (k[0] + k[1]) * T - c * (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T - c * y - // r = r - c * (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T - c * y - // => -c * y = c * (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T - // => -y = (lagrange_coeff(0) * s[0] + lagrange_coeff(1) * s[1]) * T - // => y[i] != s[i] * T - // => some other way is required - Ok(true) -} - -/// Compute Schnorr signature. -pub fn compute_schnorr_signature<'a, I>(signature_shares: I) -> Result where I: Iterator { - compute_secret_sum(signature_shares) -} - -/// Locally compute Schnorr signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Signing. -#[cfg(test)] -pub fn local_compute_schnorr_signature(nonce: &Secret, secret: &Secret, message_hash: &Secret) -> Result<(Secret, Secret), Error> { - let mut nonce_public = ec_math_utils::generation_point(); - ec_math_utils::public_mul_secret(&mut nonce_public, &nonce).unwrap(); - - let combined_hash = combine_message_hash_with_public(message_hash, &nonce_public)?; - - let mut sig_subtrahend = combined_hash.clone(); - sig_subtrahend.mul(secret)?; - let mut sig = nonce.clone(); - sig.sub(&sig_subtrahend)?; - - Ok((combined_hash, sig)) -} - -/// Verify Schnorr signature as described in https://en.wikipedia.org/wiki/Schnorr_signature#Verifying. -#[cfg(test)] -pub fn verify_schnorr_signature(public: &Public, signature: &(Secret, Secret), message_hash: &H256) -> Result { - let mut addendum = ec_math_utils::generation_point(); - ec_math_utils::public_mul_secret(&mut addendum, &signature.1)?; - let mut nonce_public = public.clone(); - ec_math_utils::public_mul_secret(&mut nonce_public, &signature.0)?; - ec_math_utils::public_add(&mut nonce_public, &addendum)?; - - let combined_hash = combine_message_hash_with_public(message_hash, &nonce_public)?; - Ok(combined_hash == signature.0) -} - -/// Compute R part of ECDSA signature. -pub fn compute_ecdsa_r(nonce_public: &Public) -> Result { - to_scalar(public_x(nonce_public)) -} - -/// Compute share of S part of ECDSA signature. -pub fn compute_ecdsa_s_share(inv_nonce_share: &Secret, inv_nonce_mul_secret: &Secret, signature_r: &Secret, message_hash: &Secret) -> Result { - let mut nonce_inv_share_mul_message_hash = inv_nonce_share.clone(); - nonce_inv_share_mul_message_hash.mul(&message_hash.clone().into())?; - - let mut nonce_inv_share_mul_secret_share_mul_r = inv_nonce_mul_secret.clone(); - nonce_inv_share_mul_secret_share_mul_r.mul(signature_r)?; - - let mut signature_s_share = nonce_inv_share_mul_message_hash; - signature_s_share.add(&nonce_inv_share_mul_secret_share_mul_r)?; - - Ok(signature_s_share) -} - -/// Compute S part of ECDSA signature from shares. -pub fn compute_ecdsa_s(t: usize, signature_s_shares: &[Secret], id_numbers: &[Secret]) -> Result { - let double_t = t * 2; - debug_assert!(id_numbers.len() >= double_t + 1); - debug_assert_eq!(signature_s_shares.len(), id_numbers.len()); - - compute_joint_secret_from_shares(double_t, - &signature_s_shares.iter().take(double_t + 1).collect::>(), - &id_numbers.iter().take(double_t + 1).collect::>()) -} - -/// Serialize ECDSA signature to [r][s]v form. -pub fn serialize_ecdsa_signature(nonce_public: &Public, signature_r: Secret, mut signature_s: Secret) -> Signature { - // compute recovery param - let mut signature_v = { - let nonce_public_x = public_x(nonce_public); - let nonce_public_y: U256 = public_y(nonce_public).into_uint(); - let nonce_public_y_is_odd = !(nonce_public_y % 2).is_zero(); - let bit0 = if nonce_public_y_is_odd { 1u8 } else { 0u8 }; - let bit1 = if nonce_public_x != *signature_r { 2u8 } else { 0u8 }; - bit0 | bit1 - }; - - // fix high S - let curve_order_half = *ec_math_utils::CURVE_ORDER / 2; - let s_numeric: U256 = (*signature_s).into_uint(); - if s_numeric > curve_order_half { - let signature_s_hash: H256 = BigEndianHash::from_uint(&(*ec_math_utils::CURVE_ORDER - s_numeric)); - signature_s = signature_s_hash.into(); - signature_v ^= 1; - } - - // serialize as [r][s]v - let mut signature = [0u8; 65]; - signature[..32].copy_from_slice(signature_r.as_bytes()); - signature[32..64].copy_from_slice(signature_s.as_bytes()); - signature[64] = signature_v; - - signature.into() -} - -/// Compute share of ECDSA reversed-nonce coefficient. Result of this_coeff * secret_share gives us a share of inv(nonce). -pub fn compute_ecdsa_inversed_secret_coeff_share(secret_share: &Secret, nonce_share: &Secret, zero_share: &Secret) -> Result { - let mut coeff = secret_share.clone(); - coeff.mul(nonce_share).unwrap(); - coeff.add(zero_share).unwrap(); - Ok(coeff) -} - -/// Compute ECDSA reversed-nonce coefficient from its shares. Result of this_coeff * secret_share gives us a share of inv(nonce). -pub fn compute_ecdsa_inversed_secret_coeff_from_shares(t: usize, id_numbers: &[Secret], shares: &[Secret]) -> Result { - debug_assert_eq!(shares.len(), 2 * t + 1); - debug_assert_eq!(shares.len(), id_numbers.len()); - - let u_shares = (0..2*t+1).map(|i| compute_shadow_mul(&shares[i], &id_numbers[i], id_numbers.iter().enumerate() - .filter(|&(j, _)| i != j) - .map(|(_, id)| id) - .take(2 * t))).collect::, _>>()?; - - // compute u - let u = compute_secret_sum(u_shares.iter())?; - - // compute inv(u) - let mut u_inv = u; - u_inv.inv()?; - Ok(u_inv) -} - -#[cfg(test)] -pub mod tests { - use std::iter::once; - use crypto::publickey::{KeyPair, recover, verify_public}; - use super::*; - - #[derive(Clone)] - struct KeyGenerationArtifacts { - id_numbers: Vec, - polynoms1: Vec>, - secrets1: Vec>, - public_shares: Vec, - secret_shares: Vec, - joint_public: Public, - } - - struct ZeroGenerationArtifacts { - polynoms1: Vec>, - secret_shares: Vec, - } - - fn prepare_polynoms1(t: usize, n: usize, secret_required: Option) -> Vec> { - let mut polynoms1: Vec<_> = (0..n).map(|_| generate_random_polynom(t).unwrap()).collect(); - // if we need specific secret to be shared, update polynoms so that sum of their free terms = required secret - if let Some(mut secret_required) = secret_required { - for polynom1 in polynoms1.iter_mut().take(n - 1) { - let secret_coeff1 = generate_random_scalar().unwrap(); - secret_required.sub(&secret_coeff1).unwrap(); - polynom1[0] = secret_coeff1; - } - - polynoms1[n - 1][0] = secret_required; - } - polynoms1 - } - - fn run_key_generation(t: usize, n: usize, id_numbers: Option>, secret_required: Option) -> KeyGenerationArtifacts { - // === PART1: DKG === - - // data, gathered during initialization - let derived_point = Random.generate().unwrap().public().clone(); - let id_numbers: Vec<_> = match id_numbers { - Some(id_numbers) => id_numbers, - None => (0..n).map(|_| generate_random_scalar().unwrap()).collect(), - }; - - // data, generated during keys dissemination - let polynoms1 = prepare_polynoms1(t, n, secret_required); - let secrets1: Vec<_> = (0..n).map(|i| (0..n).map(|j| compute_polynom(&polynoms1[i], &id_numbers[j]).unwrap()).collect::>()).collect(); - - // following data is used only on verification step - let polynoms2: Vec<_> = (0..n).map(|_| generate_random_polynom(t).unwrap()).collect(); - let secrets2: Vec<_> = (0..n).map(|i| (0..n).map(|j| compute_polynom(&polynoms2[i], &id_numbers[j]).unwrap()).collect::>()).collect(); - let publics: Vec<_> = (0..n).map(|i| public_values_generation(t, &derived_point, &polynoms1[i], &polynoms2[i]).unwrap()).collect(); - - // keys verification - (0..n).for_each(|i| { - (0..n) - .filter(|&j| i != j) - .for_each(|j| { - assert!(keys_verification(t, &derived_point, &id_numbers[i], &secrets1[j][i], &secrets2[j][i], &publics[j]).unwrap()); - }) - }); - - // data, generated during keys generation - let public_shares: Vec<_> = (0..n).map(|i| compute_public_share(&polynoms1[i][0]).unwrap()).collect(); - let secret_shares: Vec<_> = (0..n).map(|i| compute_secret_share(secrets1.iter().map(|s| &s[i])).unwrap()).collect(); - - // joint public key, as a result of DKG - let joint_public = compute_joint_public(public_shares.iter()).unwrap(); - - KeyGenerationArtifacts { - id_numbers: id_numbers, - polynoms1: polynoms1, - secrets1: secrets1, - public_shares: public_shares, - secret_shares: secret_shares, - joint_public: joint_public, - } - } - - fn run_zero_key_generation(t: usize, n: usize, id_numbers: &[Secret]) -> ZeroGenerationArtifacts { - // data, generated during keys dissemination - let polynoms1 = prepare_polynoms1(t, n, Some(zero_scalar())); - let secrets1: Vec<_> = (0..n).map(|i| (0..n).map(|j| compute_polynom(&polynoms1[i], &id_numbers[j]).unwrap()).collect::>()).collect(); - - // data, generated during keys generation - let secret_shares: Vec<_> = (0..n).map(|i| compute_secret_share(secrets1.iter().map(|s| &s[i])).unwrap()).collect(); - - ZeroGenerationArtifacts { - polynoms1: polynoms1, - secret_shares: secret_shares, - } - } - - fn run_key_share_refreshing(old_t: usize, new_t: usize, new_n: usize, old_artifacts: &KeyGenerationArtifacts) -> KeyGenerationArtifacts { - // === share refreshing protocol from - // === based on "Verifiable Secret Redistribution for Threshold Sharing Schemes" - // === http://www.cs.cmu.edu/~wing/publications/CMU-CS-02-114.pdf - - // generate new id_numbers for new nodes - let new_nodes = new_n.saturating_sub(old_artifacts.id_numbers.len()); - let id_numbers: Vec<_> = old_artifacts.id_numbers.iter().take(new_n).cloned() - .chain((0..new_nodes).map(|_| generate_random_scalar().unwrap())) - .collect(); - - // on every authorized node: generate random polynomial ai(j) = si + ... + ai[new_t - 1] * j^(new_t - 1) - let mut subshare_polynoms = Vec::new(); - for i in 0..old_t+1 { - let mut subshare_polynom = generate_random_polynom(new_t).unwrap(); - subshare_polynom[0] = old_artifacts.secret_shares[i].clone(); - subshare_polynoms.push(subshare_polynom); - } - - // on every authorized node: calculate subshare for every new node - let mut subshares = Vec::new(); - for j in 0..new_n { - let mut subshares_to_j = Vec::new(); - for i in 0..old_t+1 { - let subshare_from_i_to_j = compute_polynom(&subshare_polynoms[i], &id_numbers[j]).unwrap(); - subshares_to_j.push(subshare_from_i_to_j); - } - subshares.push(subshares_to_j); - } - - // on every new node: generate new share using Lagrange interpolation - // on every node: generate new share using Lagrange interpolation - let mut new_secret_shares = Vec::new(); - for j in 0..new_n { - let mut subshares_to_j = Vec::new(); - for i in 0..old_t+1 { - let subshare_from_i = &subshares[j][i]; - let id_number_i = &id_numbers[i]; - let other_id_numbers = (0usize..old_t+1).filter(|j| *j != i).map(|j| &id_numbers[j]); - let mut subshare_from_i = compute_shadow_mul(subshare_from_i, id_number_i, other_id_numbers).unwrap(); - if old_t % 2 != 0 { - subshare_from_i.neg().unwrap(); - } - subshares_to_j.push(subshare_from_i); - } - new_secret_shares.push(compute_secret_sum(subshares_to_j.iter()).unwrap()); - } - - let mut result = old_artifacts.clone(); - result.id_numbers = id_numbers; - result.secret_shares = new_secret_shares; - result - } - - fn run_multiplication_protocol(t: usize, secret_shares1: &[Secret], secret_shares2: &[Secret]) -> Vec { - let n = secret_shares1.len(); - assert!(t * 2 + 1 <= n); - - // shares of secrets multiplication = multiplication of secrets shares - let mul_shares: Vec<_> = (0..n).map(|i| { - let share1 = secret_shares1[i].clone(); - let share2 = secret_shares2[i].clone(); - let mut mul_share = share1; - mul_share.mul(&share2).unwrap(); - mul_share - }).collect(); - - mul_shares - } - - fn run_reciprocal_protocol(t: usize, artifacts: &KeyGenerationArtifacts) -> Vec { - // === Given a secret x mod r which is shared among n players, it is - // === required to generate shares of inv(x) mod r with out revealing - // === any information about x or inv(x). - // === https://www.researchgate.net/publication/280531698_Robust_Threshold_Elliptic_Curve_Digital_Signature - - // generate shared random secret e for given t - let n = artifacts.id_numbers.len(); - assert!(t * 2 + 1 <= n); - let e_artifacts = run_key_generation(t, n, Some(artifacts.id_numbers.clone()), None); - - // generate shares of zero for 2 * t threshold - let z_artifacts = run_zero_key_generation(2 * t, n, &artifacts.id_numbers); - - // each player computes && broadcast u[i] = x[i] * e[i] + z[i] - let ui: Vec<_> = (0..n).map(|i| compute_ecdsa_inversed_secret_coeff_share(&artifacts.secret_shares[i], - &e_artifacts.secret_shares[i], - &z_artifacts.secret_shares[i]).unwrap()).collect(); - - // players can interpolate the polynomial of degree 2t and compute u && inv(u): - let u_inv = compute_ecdsa_inversed_secret_coeff_from_shares(t, - &artifacts.id_numbers.iter().take(2*t + 1).cloned().collect::>(), - &ui.iter().take(2*t + 1).cloned().collect::>()).unwrap(); - - // each player Pi computes his share of inv(x) as e[i] * inv(u) - let x_inv_shares: Vec<_> = (0..n).map(|i| { - let mut x_inv_share = e_artifacts.secret_shares[i].clone(); - x_inv_share.mul(&u_inv).unwrap(); - x_inv_share - }).collect(); - - x_inv_shares - } - - pub fn do_encryption_and_decryption(t: usize, joint_public: &Public, id_numbers: &[Secret], secret_shares: &[Secret], joint_secret: Option<&Secret>, document_secret_plain: Public) -> (Public, Public) { - // === PART2: encryption using joint public key === - - // the next line is executed on KeyServer-client - let encrypted_secret = encrypt_secret(&document_secret_plain, &joint_public).unwrap(); - - // === PART3: decryption === - - // next line is executed on KeyServer client - let access_key = generate_random_scalar().unwrap(); - - // use t + 1 nodes to compute joint shadow point - let nodes_shadows: Vec<_> = (0..t + 1).map(|i| - compute_node_shadow(&secret_shares[i], &id_numbers[i], id_numbers.iter() - .enumerate() - .filter(|&(j, _)| j != i) - .take(t) - .map(|(_, id_number)| id_number)).unwrap()).collect(); - - let nodes_shadow_points: Vec<_> = nodes_shadows.iter() - .map(|s| compute_node_shadow_point(&access_key, &encrypted_secret.common_point, s, None).unwrap()) - .map(|sp| sp.0) - .collect(); - - assert_eq!(nodes_shadows.len(), t + 1); - assert_eq!(nodes_shadow_points.len(), t + 1); - - let joint_shadow_point = compute_joint_shadow_point(nodes_shadow_points.iter()).unwrap(); - let joint_shadow_point_test = compute_joint_shadow_point_test(&access_key, &encrypted_secret.common_point, nodes_shadows.iter()).unwrap(); - assert_eq!(joint_shadow_point, joint_shadow_point_test); - - // decrypt encrypted secret using joint shadow point - let document_secret_decrypted = decrypt_with_joint_shadow(t, &access_key, &encrypted_secret.encrypted_point, &joint_shadow_point).unwrap(); - - // decrypt encrypted secret using joint secret [just for test] - let document_secret_decrypted_test = match joint_secret { - Some(joint_secret) => decrypt_with_joint_secret(&encrypted_secret.encrypted_point, &encrypted_secret.common_point, joint_secret).unwrap(), - None => document_secret_decrypted.clone(), - }; - - (document_secret_decrypted, document_secret_decrypted_test) - } - - #[test] - fn full_encryption_math_session() { - let test_cases = [(0, 2), (1, 2), (1, 3), (2, 3), (1, 4), (2, 4), (3, 4), (1, 5), (2, 5), (3, 5), (4, 5), - (1, 10), (2, 10), (3, 10), (4, 10), (5, 10), (6, 10), (7, 10), (8, 10), (9, 10)]; - for &(t, n) in &test_cases { - let artifacts = run_key_generation(t, n, None, None); - - // compute joint private key [just for test] - let joint_secret = compute_joint_secret(artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); - let joint_key_pair = KeyPair::from_secret(joint_secret.clone()).unwrap(); - assert_eq!(&artifacts.joint_public, joint_key_pair.public()); - - // check secret shares computation [just for test] - let secret_shares_polynom: Vec<_> = (0..t + 1).map(|k| compute_secret_share(artifacts.polynoms1.iter().map(|p| &p[k])).unwrap()).collect(); - let secret_shares_calculated_from_polynom: Vec<_> = artifacts.id_numbers.iter().map(|id_number| compute_polynom(&*secret_shares_polynom, id_number).unwrap()).collect(); - assert_eq!(artifacts.secret_shares, secret_shares_calculated_from_polynom); - - // now encrypt and decrypt data - let document_secret_plain = generate_random_point().unwrap(); - let (document_secret_decrypted, document_secret_decrypted_test) = - do_encryption_and_decryption(t, &artifacts.joint_public, &artifacts.id_numbers, &artifacts.secret_shares, Some(&joint_secret), document_secret_plain.clone()); - - assert_eq!(document_secret_plain, document_secret_decrypted_test); - assert_eq!(document_secret_plain, document_secret_decrypted); - } - } - - #[test] - fn local_signature_works() { - let key_pair = Random.generate().unwrap(); - let message_hash = "0000000000000000000000000000000000000000000000000000000000000042".parse().unwrap(); - let nonce = generate_random_scalar().unwrap(); - let signature = local_compute_schnorr_signature(&nonce, key_pair.secret(), &message_hash).unwrap(); - assert_eq!(verify_schnorr_signature(key_pair.public(), &signature, &message_hash), Ok(true)); - } - - #[test] - fn full_schnorr_signature_math_session() { - let test_cases = [(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (1, 4), (2, 4), (3, 4), (1, 5), (2, 5), (3, 5), (4, 5), - (1, 10), (2, 10), (3, 10), (4, 10), (5, 10), (6, 10), (7, 10), (8, 10), (9, 10)]; - for &(t, n) in &test_cases { - // hash of the message to be signed - let message_hash: Secret = "0000000000000000000000000000000000000000000000000000000000000042".parse().unwrap(); - - // === MiDS-S algorithm === - // setup: all nodes share master secret key && every node knows master public key - let artifacts = run_key_generation(t, n, None, None); - - // in this gap (not related to math): - // master node should ask every other node if it is able to do a signing - // if there are < than t+1 nodes, able to sign => error - // select t+1 nodes for signing session - // all steps below are for this subset of nodes - let n = t + 1; - - // step 1: run DKG to generate one-time secret key (nonce) - let id_numbers = artifacts.id_numbers.iter().cloned().take(n).collect(); - let one_time_artifacts = run_key_generation(t, n, Some(id_numbers), None); - - // step 2: message hash && x coordinate of one-time public value are combined - let combined_hash = combine_message_hash_with_public(&message_hash, &one_time_artifacts.joint_public).unwrap(); - - // step 3: compute signature shares - let partial_signatures: Vec<_> = (0..n) - .map(|i| compute_schnorr_signature_share( - t, - &combined_hash, - &one_time_artifacts.polynoms1[i][0], - &artifacts.secret_shares[i], - &artifacts.id_numbers[i], - artifacts.id_numbers.iter() - .enumerate() - .filter(|&(j, _)| i != j) - .map(|(_, n)| n) - .take(t) - ).unwrap()) - .collect(); - - // step 4: receive and verify signatures shares from other nodes - let received_signatures: Vec> = (0..n) - .map(|i| (0..n) - .filter(|j| i != *j) - .map(|j| { - let signature_share = partial_signatures[j].clone(); - assert!(_check_schnorr_signature_share(&combined_hash, - &signature_share, - &artifacts.public_shares[j], - &one_time_artifacts.public_shares[j], - artifacts.id_numbers.iter().take(t)).unwrap_or(false)); - signature_share - }) - .collect()) - .collect(); - - // step 5: compute signature - let signatures: Vec<_> = (0..n) - .map(|i| (combined_hash.clone(), compute_schnorr_signature(received_signatures[i].iter().chain(once(&partial_signatures[i]))).unwrap())) - .collect(); - - // === verify signature === - let master_secret = compute_joint_secret(artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); - let nonce = compute_joint_secret(one_time_artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); - let local_signature = local_compute_schnorr_signature(&nonce, &master_secret, &message_hash).unwrap(); - for signature in &signatures { - assert_eq!(signature, &local_signature); - assert_eq!(verify_schnorr_signature(&artifacts.joint_public, signature, &message_hash), Ok(true)); - } - } - } - - #[test] - fn full_ecdsa_signature_math_session() { - let test_cases = [(2, 5), (2, 6), (3, 11), (4, 11)]; - for &(t, n) in &test_cases { - // values that can be hardcoded - let joint_secret: Secret = Random.generate().unwrap().secret().clone(); - let joint_nonce: Secret = Random.generate().unwrap().secret().clone(); - let message_hash: H256 = H256::random(); - - // convert message hash to EC scalar - let message_hash_scalar = to_scalar(message_hash.clone()).unwrap(); - - // generate secret key shares - let artifacts = run_key_generation(t, n, None, Some(joint_secret)); - - // generate nonce shares - let nonce_artifacts = run_key_generation(t, n, Some(artifacts.id_numbers.clone()), Some(joint_nonce)); - - // compute nonce public - // x coordinate (mapped to EC field) of this public is the r-portion of signature - let nonce_public_shares: Vec<_> = (0..n).map(|i| compute_public_share(&nonce_artifacts.polynoms1[i][0]).unwrap()).collect(); - let nonce_public = compute_joint_public(nonce_public_shares.iter()).unwrap(); - let signature_r = compute_ecdsa_r(&nonce_public).unwrap(); - - // compute shares of inv(nonce) so that both nonce && inv(nonce) are still unknown to all nodes - let nonce_inv_shares = run_reciprocal_protocol(t, &nonce_artifacts); - - // compute multiplication of secret-shares * inv-nonce-shares - let mul_shares = run_multiplication_protocol(t, &artifacts.secret_shares, &nonce_inv_shares); - - // compute shares for s portion of signature: nonce_inv * (message_hash + secret * signature_r) - // every node broadcasts this share - let double_t = 2 * t; - let signature_s_shares: Vec<_> = (0..double_t+1).map(|i| compute_ecdsa_s_share( - &nonce_inv_shares[i], - &mul_shares[i], - &signature_r, - &message_hash_scalar - ).unwrap()).collect(); - - // compute signature_s from received shares - let signature_s = compute_ecdsa_s(t, - &signature_s_shares, - &artifacts.id_numbers.iter().take(double_t + 1).cloned().collect::>() - ).unwrap(); - - // check signature - let signature_actual = serialize_ecdsa_signature(&nonce_public, signature_r, signature_s); - let joint_secret = compute_joint_secret(artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); - let joint_secret_pair = KeyPair::from_secret(joint_secret).unwrap(); - assert_eq!(recover(&signature_actual, &message_hash).unwrap(), *joint_secret_pair.public()); - assert!(verify_public(joint_secret_pair.public(), &signature_actual, &message_hash).unwrap()); - } - } - - #[test] - fn full_generation_math_session_with_refreshing_shares() { - let test_cases = vec![(1, 4), (6, 10)]; - for (t, n) in test_cases { - // generate key using t-of-n session - let artifacts1 = run_key_generation(t, n, None, None); - let joint_secret1 = compute_joint_secret(artifacts1.polynoms1.iter().map(|p1| &p1[0])).unwrap(); - - // let's say we want to refresh existing secret shares - // by doing this every T seconds, and assuming that in each T-second period adversary KS is not able to collect t+1 secret shares - // we can be sure that the scheme is secure - let artifacts2 = run_key_share_refreshing(t, t, n, &artifacts1); - let joint_secret2 = compute_joint_secret_from_shares(t, &artifacts2.secret_shares.iter().take(t + 1).collect::>(), - &artifacts2.id_numbers.iter().take(t + 1).collect::>()).unwrap(); - assert_eq!(joint_secret1, joint_secret2); - - // refresh again - let artifacts3 = run_key_share_refreshing(t, t, n, &artifacts2); - let joint_secret3 = compute_joint_secret_from_shares(t, &artifacts3.secret_shares.iter().take(t + 1).collect::>(), - &artifacts3.id_numbers.iter().take(t + 1).collect::>()).unwrap(); - assert_eq!(joint_secret1, joint_secret3); - } - } - - #[test] - fn full_generation_math_session_with_adding_new_nodes() { - let test_cases = vec![(1, 3), (1, 4), (6, 10)]; - for (t, n) in test_cases { - // generate key using t-of-n session - let artifacts1 = run_key_generation(t, n, None, None); - let joint_secret1 = compute_joint_secret(artifacts1.polynoms1.iter().map(|p1| &p1[0])).unwrap(); - - // let's say we want to include additional couple of servers to the set - // so that scheme becames t-of-n+2 - let artifacts2 = run_key_share_refreshing(t, t, n + 2, &artifacts1); - let joint_secret2 = compute_joint_secret_from_shares(t, &artifacts2.secret_shares.iter().take(t + 1).collect::>(), - &artifacts2.id_numbers.iter().take(t + 1).collect::>()).unwrap(); - assert_eq!(joint_secret1, joint_secret2); - - // include another server (t-of-n+3) - let artifacts3 = run_key_share_refreshing(t, t, n + 3, &artifacts2); - let joint_secret3 = compute_joint_secret_from_shares(t, &artifacts3.secret_shares.iter().take(t + 1).collect::>(), - &artifacts3.id_numbers.iter().take(t + 1).collect::>()).unwrap(); - assert_eq!(joint_secret1, joint_secret3); - } - } - - #[test] - fn full_generation_math_session_with_decreasing_threshold() { - let (t, n) = (3, 5); - - // generate key using t-of-n session - let artifacts1 = run_key_generation(t, n, None, None); - - let joint_secret1 = compute_joint_secret(artifacts1.polynoms1.iter().map(|p1| &p1[0])).unwrap(); - - // let's say we want to decrease threshold so that it becames (t-1)-of-n - let new_t = t - 1; - let artifacts2 = run_key_share_refreshing(t, new_t, n, &artifacts1); - let joint_secret2 = compute_joint_secret_from_shares(new_t, &artifacts2.secret_shares.iter().take(new_t + 1).collect::>(), - &artifacts2.id_numbers.iter().take(new_t + 1).collect::>()).unwrap(); - assert_eq!(joint_secret1, joint_secret2); - - // let's say we want to decrease threshold once again so that it becames (t-2)-of-n - let t = t - 1; - let new_t = t - 2; - let artifacts3 = run_key_share_refreshing(t, new_t, n, &artifacts2); - let joint_secret3 = compute_joint_secret_from_shares(new_t, &artifacts3.secret_shares.iter().take(new_t + 1).collect::>(), - &artifacts3.id_numbers.iter().take(new_t + 1).collect::>()).unwrap(); - assert_eq!(joint_secret1, joint_secret3); - } - - #[test] - fn full_zero_secret_generation_math_session() { - let test_cases = vec![(1, 4), (2, 4)]; - for (t, n) in test_cases { - // run joint zero generation session - let id_numbers: Vec<_> = (0..n).map(|_| generate_random_scalar().unwrap()).collect(); - let artifacts = run_zero_key_generation(t, n, &id_numbers); - - // check that zero secret is generated - // we can't compute secrets sum here, because result will be zero (invalid secret, unsupported by SECP256k1) - // so just use complement trick: x + (-x) = 0 - // TODO [Refac]: switch to SECP256K1-free scalar EC arithmetic - let partial_joint_secret = compute_secret_sum(artifacts.polynoms1.iter().take(n - 1).map(|p| &p[0])).unwrap(); - let mut partial_joint_secret_complement = artifacts.polynoms1[n - 1][0].clone(); - partial_joint_secret_complement.neg().unwrap(); - assert_eq!(partial_joint_secret, partial_joint_secret_complement); - } - } - - #[test] - fn full_generation_with_multiplication() { - let test_cases = vec![(1, 3), (2, 5), (2, 7), (3, 8)]; - for (t, n) in test_cases { - // generate two shared secrets - let artifacts1 = run_key_generation(t, n, None, None); - let artifacts2 = run_key_generation(t, n, Some(artifacts1.id_numbers.clone()), None); - - // multiplicate original secrets - let joint_secret1 = compute_joint_secret(artifacts1.polynoms1.iter().map(|p| &p[0])).unwrap(); - let joint_secret2 = compute_joint_secret(artifacts2.polynoms1.iter().map(|p| &p[0])).unwrap(); - let mut expected_joint_secret_mul = joint_secret1; - expected_joint_secret_mul.mul(&joint_secret2).unwrap(); - - // run multiplication protocol - let joint_secret_mul_shares = run_multiplication_protocol(t, &artifacts1.secret_shares, &artifacts2.secret_shares); - - // calculate actual secrets multiplication - let double_t = t * 2; - let actual_joint_secret_mul = compute_joint_secret_from_shares(double_t, - &joint_secret_mul_shares.iter().take(double_t + 1).collect::>(), - &artifacts1.id_numbers.iter().take(double_t + 1).collect::>()).unwrap(); - - assert_eq!(actual_joint_secret_mul, expected_joint_secret_mul); - } - } - - #[test] - fn full_generation_with_reciprocal() { - let test_cases = vec![(1, 3), (2, 5), (2, 7), (2, 7), (3, 8)]; - for (t, n) in test_cases { - // generate shared secret - let artifacts = run_key_generation(t, n, None, None); - - // calculate inversion of original shared secret - let joint_secret = compute_joint_secret(artifacts.polynoms1.iter().map(|p| &p[0])).unwrap(); - let mut expected_joint_secret_inv = joint_secret.clone(); - expected_joint_secret_inv.inv().unwrap(); - - // run inversion protocol - let reciprocal_shares = run_reciprocal_protocol(t, &artifacts); - - // calculate actual secret inversion - let double_t = t * 2; - let actual_joint_secret_inv = compute_joint_secret_from_shares(double_t, - &reciprocal_shares.iter().take(double_t + 1).collect::>(), - &artifacts.id_numbers.iter().take(double_t + 1).collect::>()).unwrap(); - - assert_eq!(actual_joint_secret_inv, expected_joint_secret_inv); - } - } -} diff --git a/secret-store/src/key_server_cluster/message.rs b/secret-store/src/key_server_cluster/message.rs deleted file mode 100644 index 396c5a99381..00000000000 --- a/secret-store/src/key_server_cluster/message.rs +++ /dev/null @@ -1,1557 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::fmt; -use std::collections::{BTreeSet, BTreeMap}; -use crypto::publickey::Secret; -use key_server_cluster::SessionId; -use super::{Error, SerializableH256, SerializablePublic, SerializableSecret, - SerializableSignature, SerializableMessageHash, SerializableRequester, SerializableAddress}; - -pub type MessageSessionId = SerializableH256; -pub type MessageNodeId = SerializablePublic; - -/// All possible messages that can be sent during encryption/decryption sessions. -#[derive(Clone, Debug)] -pub enum Message { - /// Cluster message. - Cluster(ClusterMessage), - /// Key generation message. - Generation(GenerationMessage), - /// Encryption message. - Encryption(EncryptionMessage), - /// Decryption message. - Decryption(DecryptionMessage), - /// Schnorr signing message. - SchnorrSigning(SchnorrSigningMessage), - /// ECDSA signing message. - EcdsaSigning(EcdsaSigningMessage), - /// Key version negotiation message. - KeyVersionNegotiation(KeyVersionNegotiationMessage), - /// Share add message. - ShareAdd(ShareAddMessage), - /// Servers set change message. - ServersSetChange(ServersSetChangeMessage), -} - -/// All possible cluster-level messages. -#[derive(Clone, Debug)] -pub enum ClusterMessage { - /// Introduce node public key. - NodePublicKey(NodePublicKey), - /// Confirm that node owns its private key. - NodePrivateKeySignature(NodePrivateKeySignature), - /// Keep alive message. - KeepAlive(KeepAlive), - /// Keep alive message response. - KeepAliveResponse(KeepAliveResponse), -} - -/// All possible messages that can be sent during key generation session. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum GenerationMessage { - /// Initialize new DKG session. - InitializeSession(InitializeSession), - /// Confirm DKG session initialization. - ConfirmInitialization(ConfirmInitialization), - /// Broadcast data, calculated during session initialization phase. - CompleteInitialization(CompleteInitialization), - /// Generated keys are sent to every node. - KeysDissemination(KeysDissemination), - /// Broadcast self public key portion. - PublicKeyShare(PublicKeyShare), - /// When session error has occured. - SessionError(SessionError), - /// When session is completed. - SessionCompleted(SessionCompleted), -} - -/// All possible messages that can be sent during encryption session. -#[derive(Clone, Debug)] -pub enum EncryptionMessage { - /// Initialize encryption session. - InitializeEncryptionSession(InitializeEncryptionSession), - /// Confirm/reject encryption session initialization. - ConfirmEncryptionInitialization(ConfirmEncryptionInitialization), - /// When encryption session error has occured. - EncryptionSessionError(EncryptionSessionError), -} - -/// All possible messages that can be sent during consensus establishing. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum ConsensusMessage { - /// Initialize consensus session. - InitializeConsensusSession(InitializeConsensusSession), - /// Confirm/reject consensus session initialization. - ConfirmConsensusInitialization(ConfirmConsensusInitialization), -} - -/// All possible messages that can be sent during servers-set consensus establishing. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum ConsensusMessageWithServersSet { - /// Initialize consensus session. - InitializeConsensusSession(InitializeConsensusSessionWithServersSet), - /// Confirm/reject consensus session initialization. - ConfirmConsensusInitialization(ConfirmConsensusInitialization), -} - -/// All possible messages that can be sent during share add consensus establishing. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum ConsensusMessageOfShareAdd { - /// Initialize consensus session. - InitializeConsensusSession(InitializeConsensusSessionOfShareAdd), - /// Confirm/reject consensus session initialization. - ConfirmConsensusInitialization(ConfirmConsensusInitialization), -} - -/// All possible messages that can be sent during decryption session. -#[derive(Clone, Debug)] -pub enum DecryptionMessage { - /// Consensus establishing message. - DecryptionConsensusMessage(DecryptionConsensusMessage), - /// Request partial decryption from node. - RequestPartialDecryption(RequestPartialDecryption), - /// Partial decryption is completed. - PartialDecryption(PartialDecryption), - /// When decryption session error has occured. - DecryptionSessionError(DecryptionSessionError), - /// When decryption session is completed. - DecryptionSessionCompleted(DecryptionSessionCompleted), - /// When decryption session is delegated to another node. - DecryptionSessionDelegation(DecryptionSessionDelegation), - /// When delegated decryption session is completed. - DecryptionSessionDelegationCompleted(DecryptionSessionDelegationCompleted), -} - -/// All possible messages that can be sent during Schnorr signing session. -#[derive(Clone, Debug)] -pub enum SchnorrSigningMessage { - /// Consensus establishing message. - SchnorrSigningConsensusMessage(SchnorrSigningConsensusMessage), - /// Session key generation message. - SchnorrSigningGenerationMessage(SchnorrSigningGenerationMessage), - /// Request partial signature from node. - SchnorrRequestPartialSignature(SchnorrRequestPartialSignature), - /// Partial signature is generated. - SchnorrPartialSignature(SchnorrPartialSignature), - /// Signing error occured. - SchnorrSigningSessionError(SchnorrSigningSessionError), - /// Signing session completed. - SchnorrSigningSessionCompleted(SchnorrSigningSessionCompleted), - /// When signing session is delegated to another node. - SchnorrSigningSessionDelegation(SchnorrSigningSessionDelegation), - /// When delegated signing session is completed. - SchnorrSigningSessionDelegationCompleted(SchnorrSigningSessionDelegationCompleted), -} - -/// All possible messages that can be sent during ECDSA signing session. -#[derive(Clone, Debug)] -pub enum EcdsaSigningMessage { - /// Consensus establishing message. - EcdsaSigningConsensusMessage(EcdsaSigningConsensusMessage), - /// Signature nonce generation message. - EcdsaSignatureNonceGenerationMessage(EcdsaSignatureNonceGenerationMessage), - /// Inversion nonce generation message. - EcdsaInversionNonceGenerationMessage(EcdsaInversionNonceGenerationMessage), - /// Inversion zero generation message. - EcdsaInversionZeroGenerationMessage(EcdsaInversionZeroGenerationMessage), - /// Inversed nonce coefficient share. - EcdsaSigningInversedNonceCoeffShare(EcdsaSigningInversedNonceCoeffShare), - /// Request partial signature from node. - EcdsaRequestPartialSignature(EcdsaRequestPartialSignature), - /// Partial signature is generated. - EcdsaPartialSignature(EcdsaPartialSignature), - /// Signing error occured. - EcdsaSigningSessionError(EcdsaSigningSessionError), - /// Signing session completed. - EcdsaSigningSessionCompleted(EcdsaSigningSessionCompleted), - /// When signing session is delegated to another node. - EcdsaSigningSessionDelegation(EcdsaSigningSessionDelegation), - /// When delegated signing session is completed. - EcdsaSigningSessionDelegationCompleted(EcdsaSigningSessionDelegationCompleted), -} - -/// All possible messages that can be sent during servers set change session. -#[derive(Clone, Debug)] -pub enum ServersSetChangeMessage { - /// Consensus establishing message. - ServersSetChangeConsensusMessage(ServersSetChangeConsensusMessage), - /// Unknown sessions ids request. - UnknownSessionsRequest(UnknownSessionsRequest), - /// Unknown sessions ids. - UnknownSessions(UnknownSessions), - /// Negotiating key version to use as a base for ShareAdd session. - ShareChangeKeyVersionNegotiation(ShareChangeKeyVersionNegotiation), - /// Initialize share change session(s). - InitializeShareChangeSession(InitializeShareChangeSession), - /// Confirm share change session(s) initialization. - ConfirmShareChangeSessionInitialization(ConfirmShareChangeSessionInitialization), - /// Share change session delegation. - ServersSetChangeDelegate(ServersSetChangeDelegate), - /// Share change session delegation response. - ServersSetChangeDelegateResponse(ServersSetChangeDelegateResponse), - /// Share add message. - ServersSetChangeShareAddMessage(ServersSetChangeShareAddMessage), - /// Servers set change session completed. - ServersSetChangeError(ServersSetChangeError), - /// Servers set change session completed. - ServersSetChangeCompleted(ServersSetChangeCompleted), -} - -/// All possible messages that can be sent during share add session. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum ShareAddMessage { - /// Consensus establishing message. - ShareAddConsensusMessage(ShareAddConsensusMessage), - /// Common key share data is sent to new node. - KeyShareCommon(KeyShareCommon), - /// Generated keys are sent to every node. - NewKeysDissemination(NewKeysDissemination), - /// When session error has occured. - ShareAddError(ShareAddError), -} - -/// All possible messages that can be sent during key version negotiation message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum KeyVersionNegotiationMessage { - /// Request key versions. - RequestKeyVersions(RequestKeyVersions), - /// Key versions. - KeyVersions(KeyVersions), - /// When session error has occured. - KeyVersionsError(KeyVersionsError), -} - -/// Introduce node public key. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct NodePublicKey { - /// Node identifier (aka node public key). - pub node_id: MessageNodeId, - /// Random data, which must be signed by peer to prove that he owns the corresponding private key. - pub confirmation_plain: SerializableH256, - /// The same random `confirmation_plain`, signed with one-time session key. - pub confirmation_signed_session: SerializableSignature, -} - -/// Confirm that node owns the private key of previously passed public key (aka node id). -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct NodePrivateKeySignature { - /// Previously passed `confirmation_plain`, signed with node private key. - pub confirmation_signed: SerializableSignature, -} - -/// Ask if the node is still alive. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeepAlive { -} - -/// Confirm that the node is still alive. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeepAliveResponse { - /// Session id, if used for session-level keep alive. - pub session_id: Option, -} - -/// Initialize new DKG session. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializeSession { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Session origin address (if any). - pub origin: Option, - /// Session author. - pub author: SerializableAddress, - /// All session participants along with their identification numbers. - pub nodes: BTreeMap, - /// Is zero secret generation session? - pub is_zero: bool, - /// Decryption threshold. During decryption threshold-of-route.len() nodes must came to - /// consensus to successfully decrypt message. - pub threshold: usize, - /// Derived generation point. Starting from originator, every node must multiply this - /// point by random scalar (unknown by other nodes). At the end of initialization - /// `point` will be some (k1 * k2 * ... * kn) * G = `point` where `(k1 * k2 * ... * kn)` - /// is unknown for every node. - pub derived_point: SerializablePublic, -} - -/// Confirm DKG session initialization. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ConfirmInitialization { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Derived generation point. - pub derived_point: SerializablePublic, -} - -/// Broadcast generated point to every other node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct CompleteInitialization { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Derived generation point. - pub derived_point: SerializablePublic, -} - -/// Generated keys are sent to every node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeysDissemination { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Secret 1. - pub secret1: SerializableSecret, - /// Secret 2. - pub secret2: SerializableSecret, - /// Public values. - pub publics: Vec, -} - -/// Node is sharing its public key share. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct PublicKeyShare { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Public key share. - pub public_share: SerializablePublic, -} - -/// When session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SessionError { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// When session is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SessionCompleted { - /// Session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// Node is requested to prepare for saving encrypted data. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializeEncryptionSession { - /// Encryption session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Requester. - pub requester: SerializableRequester, - /// Common point. - pub common_point: SerializablePublic, - /// Encrypted data. - pub encrypted_point: SerializablePublic, -} - -/// Node is responding to encryption initialization request. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ConfirmEncryptionInitialization { - /// Encryption session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// When encryption session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EncryptionSessionError { - /// Encryption session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// Node is asked to be part of consensus group. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializeConsensusSession { - /// Requester. - pub requester: SerializableRequester, - /// Key version. - pub version: SerializableH256, -} - -/// Node is responding to consensus initialization request. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ConfirmConsensusInitialization { - /// Is node confirmed consensus participation. - pub is_confirmed: bool, -} - -/// Node is asked to be part of servers-set consensus group. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializeConsensusSessionWithServersSet { - /// Migration id (if any). - pub migration_id: Option, - /// Old nodes set. - pub old_nodes_set: BTreeSet, - /// New nodes set. - pub new_nodes_set: BTreeSet, - /// Old server set, signed by requester. - pub old_set_signature: SerializableSignature, - /// New server set, signed by requester. - pub new_set_signature: SerializableSignature, -} - -/// Node is asked to be part of servers-set consensus group. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializeConsensusSessionOfShareAdd { - /// Key version. - pub version: SerializableH256, - /// Nodes that have reported version ownership. - pub version_holders: BTreeSet, - /// threshold+1 nodes from old_nodes_set selected for shares redistribution. - pub consensus_group: BTreeSet, - /// Old nodes set: all non-isolated owners of selected key share version. - pub old_nodes_set: BTreeSet, - /// New nodes map: node id => node id number. - pub new_nodes_map: BTreeMap, - /// Old server set, signed by requester. - pub old_set_signature: SerializableSignature, - /// New server set, signed by requester. - pub new_set_signature: SerializableSignature, -} - -/// Consensus-related Schnorr signing message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrSigningConsensusMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Consensus message. - pub message: ConsensusMessage, -} - -/// Session key generation message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrSigningGenerationMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Generation message. - pub message: GenerationMessage, -} - -/// Request partial Schnorr signature. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrRequestPartialSignature { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Request id. - pub request_id: SerializableSecret, - /// Message hash. - pub message_hash: SerializableMessageHash, - /// Selected nodes. - pub nodes: BTreeSet, -} - -/// Partial Schnorr signature. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrPartialSignature { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Request id. - pub request_id: SerializableSecret, - /// S part of signature. - pub partial_signature: SerializableSecret, -} - -/// When Schnorr signing session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrSigningSessionError { - /// Encryption session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// Schnorr signing session completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrSigningSessionCompleted { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// When Schnorr signing session is delegated to another node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrSigningSessionDelegation { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Requester. - pub requester: SerializableRequester, - /// Key version. - pub version: SerializableH256, - /// Message hash. - pub message_hash: SerializableH256, -} - -/// When delegated Schnorr signing session is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SchnorrSigningSessionDelegationCompleted { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// S-portion of signature. - pub signature_s: SerializableSecret, - /// C-portion of signature. - pub signature_c: SerializableSecret, -} - -/// Consensus-related ECDSA signing message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSigningConsensusMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Consensus message. - pub message: ConsensusMessage, -} - -/// ECDSA signature nonce generation message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSignatureNonceGenerationMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Generation message. - pub message: GenerationMessage, -} - -/// ECDSA inversion nonce generation message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaInversionNonceGenerationMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Generation message. - pub message: GenerationMessage, -} - -/// ECDSA inversed nonce share message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSigningInversedNonceCoeffShare { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Inversed nonce coefficient share. - pub inversed_nonce_coeff_share: SerializableSecret, -} - -/// ECDSA inversion zero generation message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaInversionZeroGenerationMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Generation message. - pub message: GenerationMessage, -} - -/// Request partial ECDSA signature. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaRequestPartialSignature { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Request id. - pub request_id: SerializableSecret, - /// ECDSA reversed-nonce coefficient - pub inversed_nonce_coeff: SerializableSecret, - /// Message hash. - pub message_hash: SerializableMessageHash, -} - -/// Partial ECDSA signature. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaPartialSignature { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Request id. - pub request_id: SerializableSecret, - /// Partial S part of signature. - pub partial_signature_s: SerializableSecret, -} - -/// When ECDSA signing session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSigningSessionError { - /// Encryption session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// ECDSA signing session completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSigningSessionCompleted { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// When ECDSA signing session is delegated to another node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSigningSessionDelegation { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Requestor signature. - pub requester: SerializableRequester, - /// Key version. - pub version: SerializableH256, - /// Message hash. - pub message_hash: SerializableH256, -} - -/// When delegated ECDSA signing session is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct EcdsaSigningSessionDelegationCompleted { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Signature. - pub signature: SerializableSignature, -} - -/// Consensus-related decryption message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DecryptionConsensusMessage { - /// Generation session Id. - pub session: MessageSessionId, - /// Signing session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Session origin (in consensus initialization message). - pub origin: Option, - /// Consensus message. - pub message: ConsensusMessage, -} - -/// Node is requested to do a partial decryption. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct RequestPartialDecryption { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Request id. - pub request_id: SerializableSecret, - /// Is shadow decryption requested? When true, decryption result - /// will be visible to the owner of requestor public key only. - pub is_shadow_decryption: bool, - /// Decryption result must be reconstructed on all participating nodes. This is useful - /// for service contract API so that all nodes from consensus group can confirm decryption. - pub is_broadcast_session: bool, - /// Nodes that are agreed to do a decryption. - pub nodes: BTreeSet, -} - -/// Node has partially decrypted the secret. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct PartialDecryption { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Request id. - pub request_id: SerializableSecret, - /// Partially decrypted secret. - pub shadow_point: SerializablePublic, - /// Decrypt shadow coefficient (if requested), encrypted with requestor public. - pub decrypt_shadow: Option>, -} - -/// When decryption session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DecryptionSessionError { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// When decryption session is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DecryptionSessionCompleted { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// When decryption session is delegated to another node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DecryptionSessionDelegation { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Session origin. - pub origin: Option, - /// Requester. - pub requester: SerializableRequester, - /// Key version. - pub version: SerializableH256, - /// Is shadow decryption requested? When true, decryption result - /// will be visible to the owner of requestor public key only. - pub is_shadow_decryption: bool, - /// Decryption result must be reconstructed on all participating nodes. This is useful - /// for service contract API so that all nodes from consensus group can confirm decryption. - pub is_broadcast_session: bool, -} - -/// When delegated decryption session is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct DecryptionSessionDelegationCompleted { - /// Encryption session Id. - pub session: MessageSessionId, - /// Decryption session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Decrypted secret point. It is partially decrypted if shadow decrpytion was requested. - pub decrypted_secret: SerializablePublic, - /// Shared common point. - pub common_point: Option, - /// If shadow decryption was requested: shadow decryption coefficients, encrypted with requestor public. - pub decrypt_shadows: Option>>, -} - -/// Consensus-related servers set change message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ServersSetChangeConsensusMessage { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Consensus message. - pub message: ConsensusMessageWithServersSet, -} - -/// Unknown sessions ids request. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct UnknownSessionsRequest { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// Unknown session ids. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct UnknownSessions { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Unknown session id. - pub unknown_sessions: BTreeSet, -} - -/// Key version negotiation message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ShareChangeKeyVersionNegotiation { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Key version negotiation message. - pub message: KeyVersionNegotiationMessage, -} - -/// Master node opens share initialize session on other nodes. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct InitializeShareChangeSession { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Key id. - pub key_id: MessageSessionId, - /// Key vesion to use in ShareAdd session. - pub version: SerializableH256, - /// Nodes that have confirmed version ownership. - pub version_holders: BTreeSet, - /// Master node. - pub master_node_id: MessageNodeId, - /// Consensus group to use in ShareAdd session. - pub consensus_group: BTreeSet, - /// Shares to add. Values are filled for new nodes only. - pub new_nodes_map: BTreeMap>, -} - -/// Slave node confirms session initialization. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ConfirmShareChangeSessionInitialization { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Sessions that are confirmed. - pub key_id: MessageSessionId, -} - -/// Share change is requested. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ServersSetChangeDelegate { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Key id. - pub key_id: MessageSessionId, -} - -/// Share change is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ServersSetChangeDelegateResponse { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Key id. - pub key_id: MessageSessionId, -} - -/// Servers set change share add message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ServersSetChangeShareAddMessage { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Unknown session id. - pub message: ShareAddMessage, -} - -/// When servers set change session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ServersSetChangeError { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// When servers set change session is completed. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ServersSetChangeCompleted { - /// Servers set change session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// Consensus-related share add session message. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ShareAddConsensusMessage { - /// Share add session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Consensus message. - pub message: ConsensusMessageOfShareAdd, -} - -/// Key share common data is passed to new node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeyShareCommon { - /// Generation session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Common key data. - pub key_common: CommonKeyData, - /// Common (shared) encryption point. - pub common_point: Option, - /// Encrypted point. - pub encrypted_point: Option, - /// Selected version id numbers. - pub id_numbers: BTreeMap, -} - -/// Generated keys are sent to every node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct NewKeysDissemination { - /// Generation session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Sub share of rcevier' secret share. - pub secret_subshare: SerializableSecret, -} - -/// When share add session error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct ShareAddError { - /// Generation session Id. - pub session: MessageSessionId, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, -} - -/// Key versions are requested. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct RequestKeyVersions { - /// Generation session id. - pub session: MessageSessionId, - /// Version negotiation session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, -} - -/// Key versions are sent. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeyVersions { - /// Generation session id. - pub session: MessageSessionId, - /// Version negotiation session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Common key data, shared by all versions. - pub key_common: Option, - /// Key versions. - pub versions: Vec, -} - -/// Common key data. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct CommonKeyData { - /// Key threshold. - pub threshold: usize, - /// Author of the key entry. - pub author: SerializableAddress, - /// Joint public. - pub public: SerializablePublic, -} - -/// When key versions error has occured. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct KeyVersionsError { - /// Generation session id. - pub session: MessageSessionId, - /// Version negotiation session Id. - pub sub_session: SerializableSecret, - /// Session-level nonce. - pub session_nonce: u64, - /// Error message. - pub error: Error, - /// Continue action from failed node (if any). This field is oly filled - /// when error has occured when trying to compute result on master node. - pub continue_with: Option, -} - -/// Key version continue action from failed node. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum FailedKeyVersionContinueAction { - /// Decryption session: origin + requester. - Decrypt(Option, SerializableAddress), -} - -impl Message { - pub fn is_initialization_message(&self) -> bool { - match *self { - Message::Generation(GenerationMessage::InitializeSession(_)) => true, - Message::Encryption(EncryptionMessage::InitializeEncryptionSession(_)) => true, - Message::Decryption(DecryptionMessage::DecryptionConsensusMessage(ref msg)) => match msg.message { - ConsensusMessage::InitializeConsensusSession(_) => true, - _ => false - }, - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref msg)) => match msg.message { - ConsensusMessage::InitializeConsensusSession(_) => true, - _ => false - }, - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref msg)) => match msg.message { - ConsensusMessage::InitializeConsensusSession(_) => true, - _ => false - }, - Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::RequestKeyVersions(_)) => true, - Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersionsError(ref msg)) if msg.continue_with.is_some() => true, - Message::ShareAdd(ShareAddMessage::ShareAddConsensusMessage(ref msg)) => match msg.message { - ConsensusMessageOfShareAdd::InitializeConsensusSession(_) => true, - _ => false - }, - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeConsensusMessage(ref msg)) => match msg.message { - ConsensusMessageWithServersSet::InitializeConsensusSession(_) => true, - _ => false - }, - _ => false, - } - } - - pub fn is_delegation_message(&self) -> bool { - match *self { - Message::Decryption(DecryptionMessage::DecryptionSessionDelegation(_)) => true, - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionDelegation(_)) => true, - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionDelegation(_)) => true, - _ => false, - } - } - - pub fn is_error_message(&self) -> bool { - match *self { - Message::Generation(GenerationMessage::SessionError(_)) => true, - Message::Encryption(EncryptionMessage::EncryptionSessionError(_)) => true, - Message::Decryption(DecryptionMessage::DecryptionSessionError(_)) => true, - Message::SchnorrSigning(SchnorrSigningMessage::SchnorrSigningSessionError(_)) => true, - Message::EcdsaSigning(EcdsaSigningMessage::EcdsaSigningSessionError(_)) => true, - Message::KeyVersionNegotiation(KeyVersionNegotiationMessage::KeyVersionsError(_)) => true, - Message::ShareAdd(ShareAddMessage::ShareAddError(_)) => true, - Message::ServersSetChange(ServersSetChangeMessage::ServersSetChangeError(_)) => true, - _ => false, - } - } - - pub fn is_exclusive_session_message(&self) -> bool { - match *self { - Message::ServersSetChange(_) => true, - _ => false, - } - } - - pub fn session_nonce(&self) -> Option { - match *self { - Message::Cluster(_) => None, - Message::Generation(ref message) => Some(message.session_nonce()), - Message::Encryption(ref message) => Some(message.session_nonce()), - Message::Decryption(ref message) => Some(message.session_nonce()), - Message::SchnorrSigning(ref message) => Some(message.session_nonce()), - Message::EcdsaSigning(ref message) => Some(message.session_nonce()), - Message::ShareAdd(ref message) => Some(message.session_nonce()), - Message::ServersSetChange(ref message) => Some(message.session_nonce()), - Message::KeyVersionNegotiation(ref message) => Some(message.session_nonce()), - } - } -} - -impl GenerationMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - GenerationMessage::InitializeSession(ref msg) => &msg.session, - GenerationMessage::ConfirmInitialization(ref msg) => &msg.session, - GenerationMessage::CompleteInitialization(ref msg) => &msg.session, - GenerationMessage::KeysDissemination(ref msg) => &msg.session, - GenerationMessage::PublicKeyShare(ref msg) => &msg.session, - GenerationMessage::SessionError(ref msg) => &msg.session, - GenerationMessage::SessionCompleted(ref msg) => &msg.session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - GenerationMessage::InitializeSession(ref msg) => msg.session_nonce, - GenerationMessage::ConfirmInitialization(ref msg) => msg.session_nonce, - GenerationMessage::CompleteInitialization(ref msg) => msg.session_nonce, - GenerationMessage::KeysDissemination(ref msg) => msg.session_nonce, - GenerationMessage::PublicKeyShare(ref msg) => msg.session_nonce, - GenerationMessage::SessionError(ref msg) => msg.session_nonce, - GenerationMessage::SessionCompleted(ref msg) => msg.session_nonce, - } - } -} - -impl EncryptionMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - EncryptionMessage::InitializeEncryptionSession(ref msg) => &msg.session, - EncryptionMessage::ConfirmEncryptionInitialization(ref msg) => &msg.session, - EncryptionMessage::EncryptionSessionError(ref msg) => &msg.session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - EncryptionMessage::InitializeEncryptionSession(ref msg) => msg.session_nonce, - EncryptionMessage::ConfirmEncryptionInitialization(ref msg) => msg.session_nonce, - EncryptionMessage::EncryptionSessionError(ref msg) => msg.session_nonce, - } - } -} - -impl DecryptionMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - DecryptionMessage::DecryptionConsensusMessage(ref msg) => &msg.session, - DecryptionMessage::RequestPartialDecryption(ref msg) => &msg.session, - DecryptionMessage::PartialDecryption(ref msg) => &msg.session, - DecryptionMessage::DecryptionSessionError(ref msg) => &msg.session, - DecryptionMessage::DecryptionSessionCompleted(ref msg) => &msg.session, - DecryptionMessage::DecryptionSessionDelegation(ref msg) => &msg.session, - DecryptionMessage::DecryptionSessionDelegationCompleted(ref msg) => &msg.session, - } - } - - pub fn sub_session_id(&self) -> &Secret { - match *self { - DecryptionMessage::DecryptionConsensusMessage(ref msg) => &msg.sub_session, - DecryptionMessage::RequestPartialDecryption(ref msg) => &msg.sub_session, - DecryptionMessage::PartialDecryption(ref msg) => &msg.sub_session, - DecryptionMessage::DecryptionSessionError(ref msg) => &msg.sub_session, - DecryptionMessage::DecryptionSessionCompleted(ref msg) => &msg.sub_session, - DecryptionMessage::DecryptionSessionDelegation(ref msg) => &msg.sub_session, - DecryptionMessage::DecryptionSessionDelegationCompleted(ref msg) => &msg.sub_session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - DecryptionMessage::DecryptionConsensusMessage(ref msg) => msg.session_nonce, - DecryptionMessage::RequestPartialDecryption(ref msg) => msg.session_nonce, - DecryptionMessage::PartialDecryption(ref msg) => msg.session_nonce, - DecryptionMessage::DecryptionSessionError(ref msg) => msg.session_nonce, - DecryptionMessage::DecryptionSessionCompleted(ref msg) => msg.session_nonce, - DecryptionMessage::DecryptionSessionDelegation(ref msg) => msg.session_nonce, - DecryptionMessage::DecryptionSessionDelegationCompleted(ref msg) => msg.session_nonce, - } - } -} - -impl SchnorrSigningMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrRequestPartialSignature(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrPartialSignature(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrSigningSessionError(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrSigningSessionCompleted(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref msg) => &msg.session, - SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(ref msg) => &msg.session, - } - } - - pub fn sub_session_id(&self) -> &Secret { - match *self { - SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrRequestPartialSignature(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrPartialSignature(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrSigningSessionError(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrSigningSessionCompleted(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref msg) => &msg.sub_session, - SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(ref msg) => &msg.sub_session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrRequestPartialSignature(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrPartialSignature(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrSigningSessionError(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrSigningSessionCompleted(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrSigningSessionDelegation(ref msg) => msg.session_nonce, - SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(ref msg) => msg.session_nonce, - } - } -} - -impl EcdsaSigningMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaRequestPartialSignature(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaPartialSignature(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaSigningSessionError(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaSigningSessionCompleted(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref msg) => &msg.session, - EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(ref msg) => &msg.session, - } - } - - pub fn sub_session_id(&self) -> &Secret { - match *self { - EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaRequestPartialSignature(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaPartialSignature(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaSigningSessionError(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaSigningSessionCompleted(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref msg) => &msg.sub_session, - EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(ref msg) => &msg.sub_session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaRequestPartialSignature(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaPartialSignature(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaSigningSessionError(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaSigningSessionCompleted(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaSigningSessionDelegation(ref msg) => msg.session_nonce, - EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(ref msg) => msg.session_nonce, - } - } -} - -impl ServersSetChangeMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - ServersSetChangeMessage::ServersSetChangeConsensusMessage(ref msg) => &msg.session, - ServersSetChangeMessage::UnknownSessionsRequest(ref msg) => &msg.session, - ServersSetChangeMessage::UnknownSessions(ref msg) => &msg.session, - ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ref msg) => &msg.session, - ServersSetChangeMessage::InitializeShareChangeSession(ref msg) => &msg.session, - ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(ref msg) => &msg.session, - ServersSetChangeMessage::ServersSetChangeDelegate(ref msg) => &msg.session, - ServersSetChangeMessage::ServersSetChangeDelegateResponse(ref msg) => &msg.session, - ServersSetChangeMessage::ServersSetChangeShareAddMessage(ref msg) => &msg.session, - ServersSetChangeMessage::ServersSetChangeError(ref msg) => &msg.session, - ServersSetChangeMessage::ServersSetChangeCompleted(ref msg) => &msg.session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - ServersSetChangeMessage::ServersSetChangeConsensusMessage(ref msg) => msg.session_nonce, - ServersSetChangeMessage::UnknownSessionsRequest(ref msg) => msg.session_nonce, - ServersSetChangeMessage::UnknownSessions(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ref msg) => msg.session_nonce, - ServersSetChangeMessage::InitializeShareChangeSession(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ServersSetChangeDelegate(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ServersSetChangeDelegateResponse(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ServersSetChangeShareAddMessage(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ServersSetChangeError(ref msg) => msg.session_nonce, - ServersSetChangeMessage::ServersSetChangeCompleted(ref msg) => msg.session_nonce, - } - } -} - -impl ShareAddMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - ShareAddMessage::ShareAddConsensusMessage(ref msg) => &msg.session, - ShareAddMessage::KeyShareCommon(ref msg) => &msg.session, - ShareAddMessage::NewKeysDissemination(ref msg) => &msg.session, - ShareAddMessage::ShareAddError(ref msg) => &msg.session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - ShareAddMessage::ShareAddConsensusMessage(ref msg) => msg.session_nonce, - ShareAddMessage::KeyShareCommon(ref msg) => msg.session_nonce, - ShareAddMessage::NewKeysDissemination(ref msg) => msg.session_nonce, - ShareAddMessage::ShareAddError(ref msg) => msg.session_nonce, - } - } -} - -impl KeyVersionNegotiationMessage { - pub fn session_id(&self) -> &SessionId { - match *self { - KeyVersionNegotiationMessage::RequestKeyVersions(ref msg) => &msg.session, - KeyVersionNegotiationMessage::KeyVersions(ref msg) => &msg.session, - KeyVersionNegotiationMessage::KeyVersionsError(ref msg) => &msg.session, - } - } - - pub fn sub_session_id(&self) -> &Secret { - match *self { - KeyVersionNegotiationMessage::RequestKeyVersions(ref msg) => &msg.sub_session, - KeyVersionNegotiationMessage::KeyVersions(ref msg) => &msg.sub_session, - KeyVersionNegotiationMessage::KeyVersionsError(ref msg) => &msg.sub_session, - } - } - - pub fn session_nonce(&self) -> u64 { - match *self { - KeyVersionNegotiationMessage::RequestKeyVersions(ref msg) => msg.session_nonce, - KeyVersionNegotiationMessage::KeyVersions(ref msg) => msg.session_nonce, - KeyVersionNegotiationMessage::KeyVersionsError(ref msg) => msg.session_nonce, - } - } -} - -impl fmt::Display for Message { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Message::Cluster(ref message) => write!(f, "Cluster.{}", message), - Message::Generation(ref message) => write!(f, "Generation.{}", message), - Message::Encryption(ref message) => write!(f, "Encryption.{}", message), - Message::Decryption(ref message) => write!(f, "Decryption.{}", message), - Message::SchnorrSigning(ref message) => write!(f, "SchnorrSigning.{}", message), - Message::EcdsaSigning(ref message) => write!(f, "EcdsaSigning.{}", message), - Message::ServersSetChange(ref message) => write!(f, "ServersSetChange.{}", message), - Message::ShareAdd(ref message) => write!(f, "ShareAdd.{}", message), - Message::KeyVersionNegotiation(ref message) => write!(f, "KeyVersionNegotiation.{}", message), - } - } -} - -impl fmt::Display for ClusterMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ClusterMessage::NodePublicKey(_) => write!(f, "NodePublicKey"), - ClusterMessage::NodePrivateKeySignature(_) => write!(f, "NodePrivateKeySignature"), - ClusterMessage::KeepAlive(_) => write!(f, "KeepAlive"), - ClusterMessage::KeepAliveResponse(_) => write!(f, "KeepAliveResponse"), - } - } -} - -impl fmt::Display for GenerationMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - GenerationMessage::InitializeSession(_) => write!(f, "InitializeSession"), - GenerationMessage::ConfirmInitialization(_) => write!(f, "ConfirmInitialization"), - GenerationMessage::CompleteInitialization(_) => write!(f, "CompleteInitialization"), - GenerationMessage::KeysDissemination(_) => write!(f, "KeysDissemination"), - GenerationMessage::PublicKeyShare(_) => write!(f, "PublicKeyShare"), - GenerationMessage::SessionError(ref msg) => write!(f, "SessionError({})", msg.error), - GenerationMessage::SessionCompleted(_) => write!(f, "SessionCompleted"), - } - } -} - -impl fmt::Display for EncryptionMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - EncryptionMessage::InitializeEncryptionSession(_) => write!(f, "InitializeEncryptionSession"), - EncryptionMessage::ConfirmEncryptionInitialization(_) => write!(f, "ConfirmEncryptionInitialization"), - EncryptionMessage::EncryptionSessionError(ref msg) => write!(f, "EncryptionSessionError({})", msg.error), - } - } -} - -impl fmt::Display for ConsensusMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ConsensusMessage::InitializeConsensusSession(_) => write!(f, "InitializeConsensusSession"), - ConsensusMessage::ConfirmConsensusInitialization(ref msg) => write!(f, "ConfirmConsensusInitialization({})", msg.is_confirmed), - } - } -} - -impl fmt::Display for ConsensusMessageWithServersSet { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ConsensusMessageWithServersSet::InitializeConsensusSession(_) => write!(f, "InitializeConsensusSession"), - ConsensusMessageWithServersSet::ConfirmConsensusInitialization(ref msg) => write!(f, "ConfirmConsensusInitialization({})", msg.is_confirmed), - } - } -} - -impl fmt::Display for ConsensusMessageOfShareAdd { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ConsensusMessageOfShareAdd::InitializeConsensusSession(_) => write!(f, "InitializeConsensusSession"), - ConsensusMessageOfShareAdd::ConfirmConsensusInitialization(ref msg) => write!(f, "ConfirmConsensusInitialization({})", msg.is_confirmed), - } - } -} - -impl fmt::Display for DecryptionMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - DecryptionMessage::DecryptionConsensusMessage(ref m) => write!(f, "DecryptionConsensusMessage.{}", m.message), - DecryptionMessage::RequestPartialDecryption(_) => write!(f, "RequestPartialDecryption"), - DecryptionMessage::PartialDecryption(_) => write!(f, "PartialDecryption"), - DecryptionMessage::DecryptionSessionError(_) => write!(f, "DecryptionSessionError"), - DecryptionMessage::DecryptionSessionCompleted(_) => write!(f, "DecryptionSessionCompleted"), - DecryptionMessage::DecryptionSessionDelegation(_) => write!(f, "DecryptionSessionDelegation"), - DecryptionMessage::DecryptionSessionDelegationCompleted(_) => write!(f, "DecryptionSessionDelegationCompleted"), - } - } -} - -impl fmt::Display for SchnorrSigningMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - SchnorrSigningMessage::SchnorrSigningConsensusMessage(ref m) => write!(f, "SchnorrSigningConsensusMessage.{}", m.message), - SchnorrSigningMessage::SchnorrSigningGenerationMessage(ref m) => write!(f, "SchnorrSigningGenerationMessage.{}", m.message), - SchnorrSigningMessage::SchnorrRequestPartialSignature(_) => write!(f, "SchnorrRequestPartialSignature"), - SchnorrSigningMessage::SchnorrPartialSignature(_) => write!(f, "SchnorrPartialSignature"), - SchnorrSigningMessage::SchnorrSigningSessionError(_) => write!(f, "SchnorrSigningSessionError"), - SchnorrSigningMessage::SchnorrSigningSessionCompleted(_) => write!(f, "SchnorrSigningSessionCompleted"), - SchnorrSigningMessage::SchnorrSigningSessionDelegation(_) => write!(f, "SchnorrSigningSessionDelegation"), - SchnorrSigningMessage::SchnorrSigningSessionDelegationCompleted(_) => write!(f, "SchnorrSigningSessionDelegationCompleted"), - } - } -} - -impl fmt::Display for EcdsaSigningMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - EcdsaSigningMessage::EcdsaSigningConsensusMessage(ref m) => write!(f, "EcdsaSigningConsensusMessage.{}", m.message), - EcdsaSigningMessage::EcdsaSignatureNonceGenerationMessage(ref m) => write!(f, "EcdsaSignatureNonceGenerationMessage.{}", m.message), - EcdsaSigningMessage::EcdsaInversionNonceGenerationMessage(ref m) => write!(f, "EcdsaInversionNonceGenerationMessage.{}", m.message), - EcdsaSigningMessage::EcdsaInversionZeroGenerationMessage(ref m) => write!(f, "EcdsaInversionZeroGenerationMessage.{}", m.message), - EcdsaSigningMessage::EcdsaSigningInversedNonceCoeffShare(_) => write!(f, "EcdsaSigningInversedNonceCoeffShare"), - EcdsaSigningMessage::EcdsaRequestPartialSignature(_) => write!(f, "EcdsaRequestPartialSignature"), - EcdsaSigningMessage::EcdsaPartialSignature(_) => write!(f, "EcdsaPartialSignature"), - EcdsaSigningMessage::EcdsaSigningSessionError(_) => write!(f, "EcdsaSigningSessionError"), - EcdsaSigningMessage::EcdsaSigningSessionCompleted(_) => write!(f, "EcdsaSigningSessionCompleted"), - EcdsaSigningMessage::EcdsaSigningSessionDelegation(_) => write!(f, "EcdsaSigningSessionDelegation"), - EcdsaSigningMessage::EcdsaSigningSessionDelegationCompleted(_) => write!(f, "EcdsaSigningSessionDelegationCompleted"), - } - } -} - -impl fmt::Display for ServersSetChangeMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ServersSetChangeMessage::ServersSetChangeConsensusMessage(ref m) => write!(f, "ServersSetChangeConsensusMessage.{}", m.message), - ServersSetChangeMessage::UnknownSessionsRequest(_) => write!(f, "UnknownSessionsRequest"), - ServersSetChangeMessage::UnknownSessions(_) => write!(f, "UnknownSessions"), - ServersSetChangeMessage::ShareChangeKeyVersionNegotiation(ref m) => write!(f, "ShareChangeKeyVersionNegotiation.{}", m.message), - ServersSetChangeMessage::InitializeShareChangeSession(_) => write!(f, "InitializeShareChangeSession"), - ServersSetChangeMessage::ConfirmShareChangeSessionInitialization(_) => write!(f, "ConfirmShareChangeSessionInitialization"), - ServersSetChangeMessage::ServersSetChangeDelegate(_) => write!(f, "ServersSetChangeDelegate"), - ServersSetChangeMessage::ServersSetChangeDelegateResponse(_) => write!(f, "ServersSetChangeDelegateResponse"), - ServersSetChangeMessage::ServersSetChangeShareAddMessage(ref m) => write!(f, "ServersSetChangeShareAddMessage.{}", m.message), - ServersSetChangeMessage::ServersSetChangeError(_) => write!(f, "ServersSetChangeError"), - ServersSetChangeMessage::ServersSetChangeCompleted(_) => write!(f, "ServersSetChangeCompleted"), - } - } -} - -impl fmt::Display for ShareAddMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - ShareAddMessage::ShareAddConsensusMessage(ref m) => write!(f, "ShareAddConsensusMessage.{}", m.message), - ShareAddMessage::KeyShareCommon(_) => write!(f, "KeyShareCommon"), - ShareAddMessage::NewKeysDissemination(_) => write!(f, "NewKeysDissemination"), - ShareAddMessage::ShareAddError(_) => write!(f, "ShareAddError"), - - } - } -} - -impl fmt::Display for KeyVersionNegotiationMessage { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - KeyVersionNegotiationMessage::RequestKeyVersions(_) => write!(f, "RequestKeyVersions"), - KeyVersionNegotiationMessage::KeyVersions(_) => write!(f, "KeyVersions"), - KeyVersionNegotiationMessage::KeyVersionsError(_) => write!(f, "KeyVersionsError"), - } - } -} diff --git a/secret-store/src/key_server_cluster/mod.rs b/secret-store/src/key_server_cluster/mod.rs deleted file mode 100644 index 7a264b70ae4..00000000000 --- a/secret-store/src/key_server_cluster/mod.rs +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use super::types::ServerKeyId; - -pub use super::blockchain::SigningKeyPair; -pub use super::types::{Error, NodeId, Requester, EncryptedDocumentKeyShadow}; -pub use super::acl_storage::AclStorage; -pub use super::key_storage::{KeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; -pub use super::key_server_set::{is_migration_required, KeyServerSet, KeyServerSetSnapshot, KeyServerSetMigration}; -pub use super::serialization::{SerializableSignature, SerializableH256, SerializableSecret, SerializablePublic, - SerializableRequester, SerializableMessageHash, SerializableAddress}; -pub use self::cluster::{new_network_cluster, ClusterCore, ClusterConfiguration, ClusterClient}; -pub use self::cluster_connections_net::NetConnectionsManagerConfig; -pub use self::cluster_sessions::{ClusterSession, ClusterSessionsListener, WaitableSession}; -#[cfg(test)] -pub use self::cluster::tests::DummyClusterClient; - -#[cfg(test)] -pub use super::node_key_pair::PlainNodeKeyPair; -#[cfg(test)] -pub use super::key_storage::tests::DummyKeyStorage; -pub use super::acl_storage::DummyAclStorage; -#[cfg(test)] -pub use super::key_server_set::tests::MapKeyServerSet; - -pub type SessionId = ServerKeyId; - -/// Session metadata. -#[derive(Debug, Clone)] -pub struct SessionMeta { - /// Key id. - pub id: SessionId, - /// Id of node, which has started this session. - pub master_node_id: NodeId, - /// Id of node, on which this session is running. - pub self_node_id: NodeId, - /// Session threshold. - pub threshold: usize, - /// Count of all configured key server nodes (valid at session start time). - pub configured_nodes_count: usize, - /// Count of all connected key server nodes (valid at session start time). - pub connected_nodes_count: usize, -} - -mod admin_sessions; -mod client_sessions; - -pub use self::admin_sessions::key_version_negotiation_session; -pub use self::admin_sessions::servers_set_change_session; -pub use self::admin_sessions::share_add_session; -pub use self::admin_sessions::share_change_session; - -pub use self::client_sessions::decryption_session; -pub use self::client_sessions::encryption_session; -pub use self::client_sessions::generation_session; -pub use self::client_sessions::signing_session_ecdsa; -pub use self::client_sessions::signing_session_schnorr; - -mod cluster; -mod cluster_connections; -mod cluster_connections_net; -mod cluster_message_processor; -mod cluster_sessions; -mod cluster_sessions_creator; -mod connection_trigger; -mod connection_trigger_with_migration; -mod io; -mod jobs; -pub mod math; -mod message; -mod net; diff --git a/secret-store/src/key_server_cluster/net/accept_connection.rs b/secret-store/src/key_server_cluster/net/accept_connection.rs deleted file mode 100644 index 8ad2e952a21..00000000000 --- a/secret-store/src/key_server_cluster/net/accept_connection.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::io; -use std::sync::Arc; -use std::net::SocketAddr; -use std::time::Duration; -use futures::{Future, Poll}; -use tokio::net::TcpStream; -use blockchain::SigningKeyPair; -use key_server_cluster::Error; -use key_server_cluster::io::{accept_handshake, Handshake, Deadline, deadline}; -use key_server_cluster::net::Connection; - -/// Create future for accepting incoming connection. -pub fn accept_connection(stream: TcpStream, self_key_pair: Arc) -> Deadline { - // TODO: This could fail so it would be better either to accept the - // address as a separate argument or return a result. - let address = stream.peer_addr().expect("Unable to determine tcp peer address"); - - let accept = AcceptConnection { - handshake: accept_handshake(stream, self_key_pair), - address: address, - }; - - deadline(Duration::new(5, 0), accept).expect("Failed to create timeout") -} - -/// Future for accepting incoming connection. -pub struct AcceptConnection { - handshake: Handshake, - address: SocketAddr, -} - -impl Future for AcceptConnection { - type Item = Result; - type Error = io::Error; - - fn poll(&mut self) -> Poll { - let (stream, result) = try_ready!(self.handshake.poll()); - let result = match result { - Ok(result) => result, - Err(err) => return Ok(Err(err).into()), - }; - let connection = Connection { - stream: stream.into(), - address: self.address, - node_id: result.node_id, - key: result.shared_key, - }; - Ok(Ok(connection).into()) - } -} diff --git a/secret-store/src/key_server_cluster/net/connect.rs b/secret-store/src/key_server_cluster/net/connect.rs deleted file mode 100644 index 532f0105b44..00000000000 --- a/secret-store/src/key_server_cluster/net/connect.rs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::collections::BTreeSet; -use std::io; -use std::time::Duration; -use std::net::SocketAddr; -use futures::{Future, Poll, Async}; -use tokio::net::{TcpStream, tcp::ConnectFuture}; -use blockchain::SigningKeyPair; -use key_server_cluster::{Error, NodeId}; -use key_server_cluster::io::{handshake, Handshake, Deadline, deadline}; -use key_server_cluster::net::Connection; - -/// Create future for connecting to other node. -pub fn connect(address: &SocketAddr, self_key_pair: Arc, trusted_nodes: BTreeSet) -> Deadline { - let connect = Connect { - state: ConnectState::TcpConnect(TcpStream::connect(address)), - address: address.clone(), - self_key_pair: self_key_pair, - trusted_nodes: trusted_nodes, - }; - - deadline(Duration::new(5, 0), connect).expect("Failed to create timeout") -} - -enum ConnectState { - TcpConnect(ConnectFuture), - Handshake(Handshake), - Connected, -} - -/// Future for connecting to other node. -pub struct Connect { - state: ConnectState, - address: SocketAddr, - self_key_pair: Arc, - trusted_nodes: BTreeSet, -} - -impl Future for Connect { - type Item = Result; - type Error = io::Error; - - fn poll(&mut self) -> Poll { - let (next, result) = match self.state { - ConnectState::TcpConnect(ref mut future) => { - let stream = try_ready!(future.poll()); - let handshake = handshake(stream, self.self_key_pair.clone(), self.trusted_nodes.clone()); - (ConnectState::Handshake(handshake), Async::NotReady) - }, - ConnectState::Handshake(ref mut future) => { - let (stream, result) = try_ready!(future.poll()); - let result = match result { - Ok(result) => result, - Err(err) => return Ok(Async::Ready(Err(err))), - }; - let connection = Connection { - stream: stream.into(), - address: self.address, - node_id: result.node_id, - key: result.shared_key, - }; - (ConnectState::Connected, Async::Ready(Ok(connection))) - }, - ConnectState::Connected => panic!("poll Connect after it's done"), - }; - - self.state = next; - match result { - // by polling again, we register new future - Async::NotReady => self.poll(), - result => Ok(result) - } - } -} diff --git a/secret-store/src/key_server_cluster/net/connection.rs b/secret-store/src/key_server_cluster/net/connection.rs deleted file mode 100644 index cf3306a8862..00000000000 --- a/secret-store/src/key_server_cluster/net/connection.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::net; -use crypto::publickey::KeyPair; -use key_server_cluster::NodeId; -use key_server_cluster::io::SharedTcpStream; - -/// Established connection data -pub struct Connection { - /// Peer address. - pub address: net::SocketAddr, - /// Connection stream. - pub stream: SharedTcpStream, - /// Peer node id. - pub node_id: NodeId, - /// Encryption key. - pub key: KeyPair, -} diff --git a/secret-store/src/key_server_cluster/net/mod.rs b/secret-store/src/key_server_cluster/net/mod.rs deleted file mode 100644 index 2ba3201d7f0..00000000000 --- a/secret-store/src/key_server_cluster/net/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -mod accept_connection; -mod connect; -mod connection; - -pub use self::accept_connection::{AcceptConnection, accept_connection}; -pub use self::connect::{Connect, connect}; -pub use self::connection::Connection; diff --git a/secret-store/src/key_server_set.rs b/secret-store/src/key_server_set.rs deleted file mode 100644 index b4c5622ec31..00000000000 --- a/secret-store/src/key_server_set.rs +++ /dev/null @@ -1,825 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use std::net::SocketAddr; -use std::collections::{BTreeMap, HashSet}; -use parking_lot::Mutex; -use ethabi::FunctionOutputDecoder; -use ethereum_types::{H256, Address}; -use crypto::publickey::public_to_address; -use bytes::Bytes; -use types::{Error, Public, NodeAddress, NodeId}; -use blockchain::{SecretStoreChain, NewBlocksNotify, SigningKeyPair, ContractAddress, BlockId}; - -use_contract!(key_server, "res/key_server_set.json"); - -/// Name of KeyServerSet contract in registry. -const KEY_SERVER_SET_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_server_set"; -/// Number of blocks (since latest new_set change) required before actually starting migration. -const MIGRATION_CONFIRMATIONS_REQUIRED: u64 = 5; -/// Number of blocks before the same-migration transaction (be it start or confirmation) will be retried. -const TRANSACTION_RETRY_INTERVAL_BLOCKS: u64 = 30; - -#[derive(Default, Debug, Clone, PartialEq)] -/// Key Server Set state. -pub struct KeyServerSetSnapshot { - /// Current set of key servers. - pub current_set: BTreeMap, - /// New set of key servers. - pub new_set: BTreeMap, - /// Current migration data. - pub migration: Option, -} - -#[derive(Default, Debug, Clone, PartialEq)] -/// Key server set migration. -pub struct KeyServerSetMigration { - /// Migration id. - pub id: H256, - /// Migration set of key servers. It is the new_set at the moment of migration start. - pub set: BTreeMap, - /// Master node of the migration process. - pub master: NodeId, - /// Is migration confirmed by this node? - pub is_confirmed: bool, -} - -/// Key Server Set -pub trait KeyServerSet: Send + Sync { - /// Is this node currently isolated from the set? - fn is_isolated(&self) -> bool; - /// Get server set state. - fn snapshot(&self) -> KeyServerSetSnapshot; - /// Start migration. - fn start_migration(&self, migration_id: H256); - /// Confirm migration. - fn confirm_migration(&self, migration_id: H256); -} - -/// On-chain Key Server set implementation. -pub struct OnChainKeyServerSet { - /// Cached on-chain contract. - contract: Mutex, -} - -#[derive(Default, Debug, Clone, PartialEq)] -/// Non-finalized new_set. -struct FutureNewSet { - /// New servers set. - pub new_set: BTreeMap, - /// Hash of block, when this set has appeared for first time. - pub block: H256, -} - -#[derive(Default, Debug, Clone, PartialEq)] -/// Migration-related transaction information. -struct PreviousMigrationTransaction { - /// Migration id. - pub migration_id: H256, - /// Latest actual block number at the time this transaction has been sent. - pub block: u64, -} - -/// Cached on-chain Key Server set contract. -struct CachedContract { - /// Blockchain client. - client: Arc, - /// Contract address source. - contract_address_source: Option, - /// Current contract address. - contract_address: Option
, - /// Is auto-migrate enabled? - auto_migrate_enabled: bool, - /// Current contract state. - snapshot: KeyServerSetSnapshot, - /// Scheduled contract state (if any). - future_new_set: Option, - /// Previous start migration transaction. - start_migration_tx: Option, - /// Previous confirm migration transaction. - confirm_migration_tx: Option, - /// This node key pair. - self_key_pair: Arc, -} - -impl OnChainKeyServerSet { - pub fn new(trusted_client: Arc, contract_address_source: Option, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result, Error> { - let key_server_set = Arc::new(OnChainKeyServerSet { - contract: Mutex::new(CachedContract::new(trusted_client.clone(), contract_address_source, self_key_pair, auto_migrate_enabled, key_servers)?), - }); - trusted_client.add_listener(key_server_set.clone()); - Ok(key_server_set) - } -} - -impl KeyServerSet for OnChainKeyServerSet { - fn is_isolated(&self) -> bool { - self.contract.lock().is_isolated() - } - - fn snapshot(&self) -> KeyServerSetSnapshot { - self.contract.lock().snapshot() - } - - fn start_migration(&self, migration_id: H256) { - self.contract.lock().start_migration(migration_id) - } - - fn confirm_migration(&self, migration_id: H256) { - self.contract.lock().confirm_migration(migration_id); - } -} - -impl NewBlocksNotify for OnChainKeyServerSet { - fn new_blocks(&self, _new_enacted_len: usize) { - self.contract.lock().update() - } -} - -trait KeyServerSubset) -> Result, String>> { - fn read_list(&self, f: &F) -> Result, String>; - - fn read_public(&self, address: Address, f: &F) -> Result; - - fn read_address(&self, address: Address, f: &F) -> Result; -} - -struct CurrentKeyServerSubset; - -impl ) -> Result, String>> KeyServerSubset for CurrentKeyServerSubset { - fn read_list(&self, f: &F) -> Result, String> { - let (encoded, decoder) = key_server::functions::get_current_key_servers::call(); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } - - fn read_public(&self, address: Address, f: &F) -> Result { - let (encoded, decoder) = key_server::functions::get_current_key_server_public::call(address); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } - - fn read_address(&self, address: Address, f: &F) -> Result { - let (encoded, decoder) = key_server::functions::get_current_key_server_address::call(address); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } -} - -struct MigrationKeyServerSubset; - -impl ) -> Result, String>> KeyServerSubset for MigrationKeyServerSubset { - fn read_list(&self, f: &F) -> Result, String> { - let (encoded, decoder) = key_server::functions::get_migration_key_servers::call(); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } - - fn read_public(&self, address: Address, f: &F) -> Result { - let (encoded, decoder) = key_server::functions::get_migration_key_server_public::call(address); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } - - fn read_address(&self, address: Address, f: &F) -> Result { - let (encoded, decoder) = key_server::functions::get_migration_key_server_address::call(address); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } -} - -struct NewKeyServerSubset; - -impl ) -> Result, String>> KeyServerSubset for NewKeyServerSubset { - fn read_list(&self, f: &F) -> Result, String> { - let (encoded, decoder) = key_server::functions::get_new_key_servers::call(); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } - - fn read_public(&self, address: Address, f: &F) -> Result { - let (encoded, decoder) = key_server::functions::get_new_key_server_public::call(address); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } - - fn read_address(&self, address: Address, f: &F) -> Result { - let (encoded, decoder) = key_server::functions::get_new_key_server_address::call(address); - decoder.decode(&f(encoded)?).map_err(|e| e.to_string()) - } -} - -impl CachedContract { - pub fn new(client: Arc, contract_address_source: Option, self_key_pair: Arc, auto_migrate_enabled: bool, key_servers: BTreeMap) -> Result { - let server_set = match contract_address_source.is_none() { - true => key_servers.into_iter() - .map(|(p, addr)| { - let addr = format!("{}:{}", addr.address, addr.port).parse() - .map_err(|err| Error::Internal(format!("error parsing node address: {}", err)))?; - Ok((p, addr)) - }) - .collect::, Error>>()?, - false => Default::default(), - }; - - let mut contract = CachedContract { - client: client, - contract_address_source: contract_address_source, - contract_address: None, - auto_migrate_enabled: auto_migrate_enabled, - future_new_set: None, - confirm_migration_tx: None, - start_migration_tx: None, - snapshot: KeyServerSetSnapshot { - current_set: server_set.clone(), - new_set: server_set, - ..Default::default() - }, - self_key_pair: self_key_pair, - }; - contract.update_contract_address(); - - Ok(contract) - } - - pub fn update_contract_address(&mut self) { - if let Some(ref contract_address_source) = self.contract_address_source { - let contract_address = self.client.read_contract_address( - KEY_SERVER_SET_CONTRACT_REGISTRY_NAME, - contract_address_source - ); - if contract_address != self.contract_address { - trace!(target: "secretstore", "{}: Configuring for key server set contract from address {:?}", - self.self_key_pair.public(), contract_address); - - self.contract_address = contract_address; - } - } - } - - pub fn update(&mut self) { - // no need to update when servers set is hardcoded - if self.contract_address_source.is_none() { - return; - } - - if self.client.is_trusted() { - // read new snapshot from reqistry - self.update_contract_address(); - self.read_from_registry(); - - // update number of confirmations (if there's future new set) - self.update_number_of_confirmations_if_required(); - } - } - - fn is_isolated(&self) -> bool { - !self.snapshot.current_set.contains_key(self.self_key_pair.public()) - } - - fn snapshot(&self) -> KeyServerSetSnapshot { - self.snapshot.clone() - } - - fn start_migration(&mut self, migration_id: H256) { - // trust is not needed here, because it is the reaction to the read of the trusted client - if let Some(contract_address) = self.contract_address.as_ref() { - // check if we need to send start migration transaction - if !update_last_transaction_block(&*self.client, &migration_id, &mut self.start_migration_tx) { - return; - } - - // prepare transaction data - let transaction_data = key_server::functions::start_migration::encode_input(migration_id); - - // send transaction - match self.client.transact_contract(*contract_address, transaction_data) { - Ok(_) => trace!(target: "secretstore_net", "{}: sent auto-migration start transaction", - self.self_key_pair.public()), - Err(error) => warn!(target: "secretstore_net", "{}: failed to submit auto-migration start transaction: {}", - self.self_key_pair.public(), error), - } - } - } - - fn confirm_migration(&mut self, migration_id: H256) { - // trust is not needed here, because we have already completed the action - if let (true, Some(contract_address)) = (self.client.is_trusted(), self.contract_address) { - // check if we need to send start migration transaction - if !update_last_transaction_block(&*self.client, &migration_id, &mut self.confirm_migration_tx) { - return; - } - - // prepare transaction data - let transaction_data = key_server::functions::confirm_migration::encode_input(migration_id); - - // send transaction - match self.client.transact_contract(contract_address, transaction_data) { - Ok(_) => trace!(target: "secretstore_net", "{}: sent auto-migration confirm transaction", - self.self_key_pair.public()), - Err(error) => warn!(target: "secretstore_net", "{}: failed to submit auto-migration confirmation transaction: {}", - self.self_key_pair.public(), error), - } - } - } - - fn read_from_registry(&mut self) { - let contract_address = match self.contract_address { - Some(contract_address) => contract_address, - None => { - // no contract installed => empty snapshot - // WARNING: after restart current_set will be reset to the set from configuration file - // even though we have reset to empty set here. We are not considering this as an issue - // because it is actually the issue of administrator. - self.snapshot = Default::default(); - self.future_new_set = None; - return; - }, - }; - - let do_call = |data| self.client.call_contract(BlockId::Latest, contract_address, data); - - let current_set = Self::read_key_server_set(CurrentKeyServerSubset, &do_call); - - // read migration-related data if auto migration is enabled - let (new_set, migration) = match self.auto_migrate_enabled { - true => { - let new_set = Self::read_key_server_set(NewKeyServerSubset, &do_call); - let migration_set = Self::read_key_server_set(MigrationKeyServerSubset, &do_call); - - let migration_id = match migration_set.is_empty() { - false => { - let (encoded, decoder) = key_server::functions::get_migration_id::call(); - do_call(encoded) - .map_err(|e| e.to_string()) - .and_then(|data| decoder.decode(&data).map_err(|e| e.to_string())) - .map_err(|err| { trace!(target: "secretstore", "Error {} reading migration id from contract", err); err }) - .ok() - }, - true => None, - }; - - let migration_master = match migration_set.is_empty() { - false => { - let (encoded, decoder) = key_server::functions::get_migration_master::call(); - do_call(encoded) - .map_err(|e| e.to_string()) - .and_then(|data| decoder.decode(&data).map_err(|e| e.to_string())) - .map_err(|err| { trace!(target: "secretstore", "Error {} reading migration master from contract", err); err }) - .ok() - .and_then(|address| current_set.keys().chain(migration_set.keys()) - .find(|public| public_to_address(public) == address) - .cloned()) - }, - true => None, - }; - - let is_migration_confirmed = match migration_set.is_empty() { - false if current_set.contains_key(self.self_key_pair.public()) || migration_set.contains_key(self.self_key_pair.public()) => { - let (encoded, decoder) = key_server::functions::is_migration_confirmed::call(self.self_key_pair.address()); - do_call(encoded) - .map_err(|e| e.to_string()) - .and_then(|data| decoder.decode(&data).map_err(|e| e.to_string())) - .map_err(|err| { trace!(target: "secretstore", "Error {} reading migration confirmation from contract", err); err }) - .ok() - }, - _ => None, - }; - - let migration = match (migration_set.is_empty(), migration_id, migration_master, is_migration_confirmed) { - (false, Some(migration_id), Some(migration_master), Some(is_migration_confirmed)) => - Some(KeyServerSetMigration { - id: migration_id, - master: migration_master, - set: migration_set, - is_confirmed: is_migration_confirmed, - }), - _ => None, - }; - - (new_set, migration) - } - false => (current_set.clone(), None), - }; - - let mut new_snapshot = KeyServerSetSnapshot { - current_set: current_set, - new_set: new_set, - migration: migration, - }; - - // we might want to adjust new_set if auto migration is enabled - if self.auto_migrate_enabled { - let block = self.client.block_hash(BlockId::Latest).unwrap_or_default(); - update_future_set(&mut self.future_new_set, &mut new_snapshot, block); - } - - self.snapshot = new_snapshot; - } - - fn read_key_server_set(subset: T, do_call: F) -> BTreeMap - where - T: KeyServerSubset, - F: Fn(Vec) -> Result, String> { - let mut key_servers = BTreeMap::new(); - let mut key_servers_addresses = HashSet::new(); - let key_servers_list = subset.read_list(&do_call) - .map_err(|err| { warn!(target: "secretstore_net", "error {} reading list of key servers from contract", err); err }) - .unwrap_or_default(); - for key_server in key_servers_list { - let key_server_public = subset.read_public(key_server, &do_call) - .and_then(|p| if p.len() == 64 { Ok(Public::from_slice(&p)) } else { Err(format!("Invalid public length {}", p.len())) }); - let key_server_address: Result = subset.read_address(key_server, &do_call) - .and_then(|a| a.parse().map_err(|e| format!("Invalid ip address: {}", e))); - - // only add successfully parsed nodes - match (key_server_public, key_server_address) { - (Ok(key_server_public), Ok(key_server_address)) => { - if !key_servers_addresses.insert(key_server_address.clone()) { - warn!(target: "secretstore_net", "the same address ({}) specified twice in list of contracts. Ignoring server {}", - key_server_address, key_server_public); - continue; - } - - key_servers.insert(key_server_public, key_server_address); - }, - (Err(public_err), _) => warn!(target: "secretstore_net", "received invalid public from key server set contract: {}", public_err), - (_, Err(ip_err)) => warn!(target: "secretstore_net", "received invalid IP from key server set contract: {}", ip_err), - } - } - key_servers - } - - fn update_number_of_confirmations_if_required(&mut self) { - if !self.auto_migrate_enabled { - return; - } - let client = &*self.client; - update_number_of_confirmations( - &|| latest_block_hash(client), - &|block| block_confirmations(client, block), - &mut self.future_new_set, &mut self.snapshot); - } -} - -/// Check if two sets are equal (in terms of migration requirements). We do not need migration if only -/// addresses are changed - simply adjusting connections is enough in this case. -pub fn is_migration_required(current_set: &BTreeMap, new_set: &BTreeMap) -> bool { - let no_nodes_removed = current_set.keys().all(|n| new_set.contains_key(n)); - let no_nodes_added = new_set.keys().all(|n| current_set.contains_key(n)); - !no_nodes_removed || !no_nodes_added -} - -fn update_future_set(future_new_set: &mut Option, new_snapshot: &mut KeyServerSetSnapshot, block: H256) { - // migration has already started => no need to delay visibility - if new_snapshot.migration.is_some() { - *future_new_set = None; - return; - } - - // no migration is required => no need to delay visibility - if !is_migration_required(&new_snapshot.current_set, &new_snapshot.new_set) { - *future_new_set = None; - return; - } - - // when auto-migrate is enabled, we do not want to start migration right after new_set is changed, because of: - // 1) there could be a fork && we could start migration to forked version (and potentially lose secrets) - // 2) there must be some period for new_set changes finalization (i.e. adding/removing more servers) - let mut new_set = new_snapshot.current_set.clone(); - ::std::mem::swap(&mut new_set, &mut new_snapshot.new_set); - - // if nothing has changed in future_new_set, then we want to preserve previous block hash - let block = match Some(&new_set) == future_new_set.as_ref().map(|f| &f.new_set) { - true => future_new_set.as_ref().map(|f| &f.block).cloned().unwrap_or_else(|| block), - false => block, - }; - - *future_new_set = Some(FutureNewSet { - new_set: new_set, - block: block, - }); -} - -fn update_number_of_confirmations H256, F2: Fn(H256) -> Option>(latest_block: &F1, confirmations: &F2, future_new_set: &mut Option, snapshot: &mut KeyServerSetSnapshot) { - match future_new_set.as_mut() { - // no future new set is scheduled => do nothing, - None => return, - // else we should calculate number of confirmations for future new set - Some(future_new_set) => match confirmations(future_new_set.block.clone()) { - // we have enough confirmations => should move new_set from future to snapshot - Some(confirmations) if confirmations >= MIGRATION_CONFIRMATIONS_REQUIRED => (), - // not enough confirmations => do nothing - Some(_) => return, - // if number of confirmations is None, then reorg has happened && we need to reset block - // (some more intelligent strategy is possible, but let's stick to simplest one) - None => { - future_new_set.block = latest_block(); - return; - } - } - } - - let future_new_set = future_new_set.take() - .expect("we only pass through match above when future_new_set is some; qed"); - snapshot.new_set = future_new_set.new_set; -} - -fn update_last_transaction_block(client: &dyn SecretStoreChain, migration_id: &H256, previous_transaction: &mut Option) -> bool { - let last_block = client.block_number(BlockId::Latest).unwrap_or_default(); - match previous_transaction.as_ref() { - // no previous transaction => send immediately - None => (), - // previous transaction has been sent for other migration process => send immediately - Some(tx) if tx.migration_id != *migration_id => (), - // if we have sent the same type of transaction recently => do nothing (hope it will be mined eventually) - // if we have sent the same transaction some time ago => - // assume that our tx queue was full - // or we didn't have enough eth fot this tx - // or the transaction has been removed from the queue (and never reached any miner node) - // if we have restarted after sending tx => assume we have never sent it - Some(tx) => { - if tx.block > last_block || last_block - tx.block < TRANSACTION_RETRY_INTERVAL_BLOCKS { - return false; - } - }, - } - - *previous_transaction = Some(PreviousMigrationTransaction { - migration_id: migration_id.clone(), - block: last_block, - }); - - true -} - -fn latest_block_hash(client: &dyn SecretStoreChain) -> H256 { - client.block_hash(BlockId::Latest).unwrap_or_default() -} - -fn block_confirmations(client: &dyn SecretStoreChain, block: H256) -> Option { - client.block_number(BlockId::Hash(block)) - .and_then(|block| client.block_number(BlockId::Latest).map(|last_block| (block, last_block))) - .map(|(block, last_block)| last_block - block) -} - -#[cfg(test)] -pub mod tests { - use std::collections::BTreeMap; - use std::net::SocketAddr; - use ethereum_types::{H256, H512}; - use crypto::publickey::Public; - use super::{update_future_set, update_number_of_confirmations, FutureNewSet, - KeyServerSet, KeyServerSetSnapshot, MIGRATION_CONFIRMATIONS_REQUIRED}; - - #[derive(Default)] - pub struct MapKeyServerSet { - is_isolated: bool, - nodes: BTreeMap, - } - - impl MapKeyServerSet { - pub fn new(is_isolated: bool, nodes: BTreeMap) -> Self { - MapKeyServerSet { - is_isolated: is_isolated, - nodes: nodes, - } - } - } - - impl KeyServerSet for MapKeyServerSet { - fn is_isolated(&self) -> bool { - self.is_isolated - } - - fn snapshot(&self) -> KeyServerSetSnapshot { - KeyServerSetSnapshot { - current_set: self.nodes.clone(), - new_set: self.nodes.clone(), - ..Default::default() - } - } - - fn start_migration(&self, _migration_id: H256) { - unimplemented!("test-only") - } - - fn confirm_migration(&self, _migration_id: H256) { - unimplemented!("test-only") - } - } - - #[test] - fn future_set_is_updated_to_none_when_migration_has_already_started() { - let mut future_new_set = Some(Default::default()); - let mut new_snapshot = KeyServerSetSnapshot { - migration: Some(Default::default()), - ..Default::default() - }; - let new_snapshot_copy = new_snapshot.clone(); - update_future_set(&mut future_new_set, &mut new_snapshot, Default::default()); - assert_eq!(future_new_set, None); - assert_eq!(new_snapshot, new_snapshot_copy); - } - - #[test] - fn future_set_is_updated_to_none_when_no_migration_is_required() { - let node_id = Default::default(); - let address1 = "127.0.0.1:12000".parse().unwrap(); - let address2 = "127.0.0.1:12001".parse().unwrap(); - - // addresses are different, but node set is the same => no migration is required - let mut future_new_set = Some(Default::default()); - let mut new_snapshot = KeyServerSetSnapshot { - current_set: vec![(node_id, address1)].into_iter().collect(), - new_set: vec![(node_id, address2)].into_iter().collect(), - ..Default::default() - }; - let new_snapshot_copy = new_snapshot.clone(); - update_future_set(&mut future_new_set, &mut new_snapshot, Default::default()); - assert_eq!(future_new_set, None); - assert_eq!(new_snapshot, new_snapshot_copy); - - // everything is the same => no migration is required - let mut future_new_set = Some(Default::default()); - let mut new_snapshot = KeyServerSetSnapshot { - current_set: vec![(node_id, address1)].into_iter().collect(), - new_set: vec![(node_id, address1)].into_iter().collect(), - ..Default::default() - }; - let new_snapshot_copy = new_snapshot.clone(); - update_future_set(&mut future_new_set, &mut new_snapshot, Default::default()); - assert_eq!(future_new_set, None); - assert_eq!(new_snapshot, new_snapshot_copy); - } - - #[test] - fn future_set_is_initialized() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = None; - let mut new_snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - ..Default::default() - }; - update_future_set(&mut future_new_set, &mut new_snapshot, Default::default()); - assert_eq!(future_new_set, Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: Default::default(), - })); - assert_eq!(new_snapshot, KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }); - } - - #[test] - fn future_set_is_updated_when_set_differs() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: Default::default(), - }); - let mut new_snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(3), address)].into_iter().collect(), - ..Default::default() - }; - update_future_set(&mut future_new_set, &mut new_snapshot, H256::from_low_u64_be(1)); - assert_eq!(future_new_set, Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(3), address)].into_iter().collect(), - block: H256::from_low_u64_be(1), - })); - assert_eq!(new_snapshot, KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }); - } - - #[test] - fn future_set_is_not_updated_when_set_is_the_same() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: Default::default(), - }); - let mut new_snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - ..Default::default() - }; - update_future_set(&mut future_new_set, &mut new_snapshot, H256::from_low_u64_be(1)); - assert_eq!(future_new_set, Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: Default::default(), - })); - assert_eq!(new_snapshot, KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }); - } - - #[test] - fn when_updating_confirmations_nothing_is_changed_if_no_future_set() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = None; - let mut snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }; - let snapshot_copy = snapshot.clone(); - update_number_of_confirmations( - &|| H256::from_low_u64_be(1), - &|_| Some(MIGRATION_CONFIRMATIONS_REQUIRED), - &mut future_new_set, &mut snapshot); - assert_eq!(future_new_set, None); - assert_eq!(snapshot, snapshot_copy); - } - - #[test] - fn when_updating_confirmations_migration_is_scheduled() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: Default::default(), - }); - let mut snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }; - update_number_of_confirmations( - &|| H256::from_low_u64_be(1), - &|_| Some(MIGRATION_CONFIRMATIONS_REQUIRED), - &mut future_new_set, &mut snapshot); - assert_eq!(future_new_set, None); - assert_eq!(snapshot, KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - ..Default::default() - }); - } - - #[test] - fn when_updating_confirmations_migration_is_not_scheduled_when_not_enough_confirmations() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: Default::default(), - }); - let mut snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }; - let future_new_set_copy = future_new_set.clone(); - let snapshot_copy = snapshot.clone(); - update_number_of_confirmations( - &|| H256::from_low_u64_be(1), - &|_| Some(MIGRATION_CONFIRMATIONS_REQUIRED - 1), - &mut future_new_set, &mut snapshot); - assert_eq!(future_new_set, future_new_set_copy); - assert_eq!(snapshot, snapshot_copy); - } - - #[test] - fn when_updating_confirmations_migration_is_reset_when_reorganized() { - let address = "127.0.0.1:12000".parse().unwrap(); - - let mut future_new_set = Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: H256::from_low_u64_be(1), - }); - let mut snapshot = KeyServerSetSnapshot { - current_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - new_set: vec![(H512::from_low_u64_be(1), address)].into_iter().collect(), - ..Default::default() - }; - let snapshot_copy = snapshot.clone(); - update_number_of_confirmations( - &|| H256::from_low_u64_be(2), - &|_| None, - &mut future_new_set, &mut snapshot); - assert_eq!(future_new_set, Some(FutureNewSet { - new_set: vec![(H512::from_low_u64_be(2), address)].into_iter().collect(), - block: H256::from_low_u64_be(2), - })); - assert_eq!(snapshot, snapshot_copy); - } -} diff --git a/secret-store/src/key_storage.rs b/secret-store/src/key_storage.rs deleted file mode 100644 index c5d082b440b..00000000000 --- a/secret-store/src/key_storage.rs +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeMap; -use std::sync::Arc; -use serde_json; -use tiny_keccak::Keccak; -use ethereum_types::{H256, Address}; -use crypto::publickey::{Secret, Public}; -use kvdb::KeyValueDB; -use types::{Error, ServerKeyId, NodeId}; -use serialization::{SerializablePublic, SerializableSecret, SerializableH256, SerializableAddress}; - -/// Encrypted key share, stored by key storage on the single key server. -#[derive(Debug, Default, Clone, PartialEq)] -pub struct DocumentKeyShare { - /// Author of the entry. - pub author: Address, - /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). - pub threshold: usize, - /// Server public key. - pub public: Public, - /// Common (shared) encryption point. - pub common_point: Option, - /// Encrypted point. - pub encrypted_point: Option, - /// Key share versions. - pub versions: Vec, -} - -/// Versioned portion of document key share. -#[derive(Debug, Clone, PartialEq)] -pub struct DocumentKeyShareVersion { - /// Version hash (Keccak(time + id_numbers)). - pub hash: H256, - /// Nodes ids numbers. - pub id_numbers: BTreeMap, - /// Node secret share. - pub secret_share: Secret, -} - -/// Document encryption keys storage -pub trait KeyStorage: Send + Sync { - /// Insert document encryption key - fn insert(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error>; - /// Update document encryption key - fn update(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error>; - /// Get document encryption key - fn get(&self, document: &ServerKeyId) -> Result, Error>; - /// Remove document encryption key - fn remove(&self, document: &ServerKeyId) -> Result<(), Error>; - /// Clears the database - fn clear(&self) -> Result<(), Error>; - /// Check if storage contains document encryption key - fn contains(&self, document: &ServerKeyId) -> bool; - /// Iterate through storage - fn iter<'a>(&'a self) -> Box + 'a>; -} - -/// Persistent document encryption keys storage -pub struct PersistentKeyStorage { - db: Arc, -} - -/// Persistent document encryption keys storage iterator -pub struct PersistentKeyStorageIterator<'a> { - iter: Box, Box<[u8]>)> + 'a>, -} - -/// V3 of encrypted key share, as it is stored by key storage on the single key server. -#[derive(Serialize, Deserialize)] -struct SerializableDocumentKeyShareV3 { - /// Author of the entry. - pub author: SerializableAddress, - /// Decryption threshold (at least threshold + 1 nodes are required to decrypt data). - pub threshold: usize, - /// Server public. - pub public: SerializablePublic, - /// Common (shared) encryption point. - pub common_point: Option, - /// Encrypted point. - pub encrypted_point: Option, - /// Versions. - pub versions: Vec -} - -/// V3 of encrypted key share version, as it is stored by key storage on the single key server. -#[derive(Serialize, Deserialize)] -struct SerializableDocumentKeyShareVersionV3 { - /// Version hash. - pub hash: SerializableH256, - /// Nodes ids numbers. - pub id_numbers: BTreeMap, - /// Node secret share. - pub secret_share: SerializableSecret, -} - -impl PersistentKeyStorage { - /// Create new persistent document encryption keys storage - pub fn new(db: Arc) -> Result { - Ok(Self { db }) - } -} - -impl KeyStorage for PersistentKeyStorage { - fn insert(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { - let key: SerializableDocumentKeyShareV3 = key.into(); - let key = serde_json::to_vec(&key).map_err(|e| Error::Database(e.to_string()))?; - let mut batch = self.db.transaction(); - batch.put(0, document.as_bytes(), &key); - self.db.write(batch).map_err(Into::into) - } - - fn update(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { - self.insert(document, key) - } - - fn get(&self, document: &ServerKeyId) -> Result, Error> { - self.db.get(0, document.as_bytes()) - .map_err(|e| Error::Database(e.to_string())) - .and_then(|key| match key { - None => Ok(None), - Some(key) => serde_json::from_slice::(&key) - .map_err(|e| Error::Database(e.to_string())) - .map(Into::into) - .map(Some), - }) - } - - fn remove(&self, document: &ServerKeyId) -> Result<(), Error> { - let mut batch = self.db.transaction(); - batch.delete(0, document.as_bytes()); - self.db.write(batch).map_err(Into::into) - } - - fn clear(&self) -> Result<(), Error> { - let mut batch = self.db.transaction(); - for (key, _) in self.iter() { - batch.delete(0, key.as_bytes()); - } - self.db.write(batch) - .map_err(|e| Error::Database(e.to_string())) - } - - fn contains(&self, document: &ServerKeyId) -> bool { - self.db.get(0, document.as_bytes()) - .map(|k| k.is_some()) - .unwrap_or(false) - } - - fn iter<'a>(&'a self) -> Box + 'a> { - Box::new(PersistentKeyStorageIterator { - iter: self.db.iter(0), - }) - } -} - -impl<'a> Iterator for PersistentKeyStorageIterator<'a> { - type Item = (ServerKeyId, DocumentKeyShare); - - fn next(&mut self) -> Option<(ServerKeyId, DocumentKeyShare)> { - self.iter.as_mut().next() - .and_then(|(db_key, db_val)| serde_json::from_slice::(&db_val) - .ok() - .map(|key| (ServerKeyId::from_slice(&*db_key), key.into()))) - } -} - -impl DocumentKeyShare { - /// Get last version reference. - #[cfg(test)] - pub fn last_version(&self) -> Result<&DocumentKeyShareVersion, Error> { - self.versions.iter().rev() - .nth(0) - .ok_or_else(|| Error::Database("key version is not found".into())) - } - - /// Get given version reference. - pub fn version(&self, version: &H256) -> Result<&DocumentKeyShareVersion, Error> { - self.versions.iter().rev() - .find(|v| &v.hash == version) - .ok_or_else(|| Error::Database("key version is not found".into())) - } -} - -impl DocumentKeyShareVersion { - /// Create new version - pub fn new(id_numbers: BTreeMap, secret_share: Secret) -> Self { - DocumentKeyShareVersion { - hash: Self::data_hash(id_numbers.iter().map(|(k, v)| (k.as_bytes(), v.as_bytes()))), - id_numbers: id_numbers, - secret_share: secret_share, - } - } - - /// Calculate hash of given version data. - pub fn data_hash<'a, I>(id_numbers: I) -> H256 where I: Iterator { - let mut nodes_keccak = Keccak::new_keccak256(); - - for (node, node_number) in id_numbers { - nodes_keccak.update(node); - nodes_keccak.update(node_number); - } - - let mut nodes_keccak_value = [0u8; 32]; - nodes_keccak.finalize(&mut nodes_keccak_value); - - nodes_keccak_value.into() - } -} - -impl From for SerializableDocumentKeyShareV3 { - fn from(key: DocumentKeyShare) -> Self { - SerializableDocumentKeyShareV3 { - author: key.author.into(), - threshold: key.threshold, - public: key.public.into(), - common_point: key.common_point.map(Into::into), - encrypted_point: key.encrypted_point.map(Into::into), - versions: key.versions.into_iter().map(Into::into).collect(), - } - } -} - -impl From for SerializableDocumentKeyShareVersionV3 { - fn from(version: DocumentKeyShareVersion) -> Self { - SerializableDocumentKeyShareVersionV3 { - hash: version.hash.into(), - id_numbers: version.id_numbers.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), - secret_share: version.secret_share.into(), - } - } -} - -impl From for DocumentKeyShare { - fn from(key: SerializableDocumentKeyShareV3) -> Self { - DocumentKeyShare { - author: key.author.into(), - threshold: key.threshold, - public: key.public.into(), - common_point: key.common_point.map(Into::into), - encrypted_point: key.encrypted_point.map(Into::into), - versions: key.versions.into_iter() - .map(|v| DocumentKeyShareVersion { - hash: v.hash.into(), - id_numbers: v.id_numbers.into_iter().map(|(k, v)| (k.into(), v.into())).collect(), - secret_share: v.secret_share.into(), - }) - .collect(), - } - } -} - -#[cfg(test)] -pub mod tests { - use std::collections::HashMap; - use std::sync::Arc; - use parking_lot::RwLock; - use tempdir::TempDir; - use crypto::publickey::{Random, Generator, Public}; - use kvdb_rocksdb::{Database, DatabaseConfig}; - use types::{Error, ServerKeyId}; - use super::{KeyStorage, PersistentKeyStorage, DocumentKeyShare, DocumentKeyShareVersion}; - - /// In-memory document encryption keys storage - #[derive(Default)] - pub struct DummyKeyStorage { - keys: RwLock>, - } - - impl KeyStorage for DummyKeyStorage { - fn insert(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { - self.keys.write().insert(document, key); - Ok(()) - } - - fn update(&self, document: ServerKeyId, key: DocumentKeyShare) -> Result<(), Error> { - self.keys.write().insert(document, key); - Ok(()) - } - - fn get(&self, document: &ServerKeyId) -> Result, Error> { - Ok(self.keys.read().get(document).cloned()) - } - - fn remove(&self, document: &ServerKeyId) -> Result<(), Error> { - self.keys.write().remove(document); - Ok(()) - } - - fn clear(&self) -> Result<(), Error> { - self.keys.write().clear(); - Ok(()) - } - - fn contains(&self, document: &ServerKeyId) -> bool { - self.keys.read().contains_key(document) - } - - fn iter<'a>(&'a self) -> Box + 'a> { - Box::new(self.keys.read().clone().into_iter()) - } - } - - #[test] - fn persistent_key_storage() { - let tempdir = TempDir::new("").unwrap(); - let key1 = ServerKeyId::from_low_u64_be(1); - let value1 = DocumentKeyShare { - author: Default::default(), - threshold: 100, - public: Public::default(), - common_point: Some(Random.generate().unwrap().public().clone()), - encrypted_point: Some(Random.generate().unwrap().public().clone()), - versions: vec![DocumentKeyShareVersion { - hash: Default::default(), - id_numbers: vec![ - (Random.generate().unwrap().public().clone(), Random.generate().unwrap().secret().clone()) - ].into_iter().collect(), - secret_share: Random.generate().unwrap().secret().clone(), - }], - }; - let key2 = ServerKeyId::from_low_u64_be(2); - let value2 = DocumentKeyShare { - author: Default::default(), - threshold: 200, - public: Public::default(), - common_point: Some(Random.generate().unwrap().public().clone()), - encrypted_point: Some(Random.generate().unwrap().public().clone()), - versions: vec![DocumentKeyShareVersion { - hash: Default::default(), - id_numbers: vec![ - (Random.generate().unwrap().public().clone(), Random.generate().unwrap().secret().clone()) - ].into_iter().collect(), - secret_share: Random.generate().unwrap().secret().clone(), - }], - }; - let key3 = ServerKeyId::from_low_u64_be(3); - - let db_config = DatabaseConfig::with_columns(1); - let db = Database::open(&db_config, &tempdir.path().display().to_string()).unwrap(); - - let key_storage = PersistentKeyStorage::new(Arc::new(db)).unwrap(); - key_storage.insert(key1.clone(), value1.clone()).unwrap(); - key_storage.insert(key2.clone(), value2.clone()).unwrap(); - assert_eq!(key_storage.get(&key1), Ok(Some(value1.clone()))); - assert_eq!(key_storage.get(&key2), Ok(Some(value2.clone()))); - assert_eq!(key_storage.get(&key3), Ok(None)); - drop(key_storage); - - let db = Database::open(&db_config, &tempdir.path().display().to_string()).unwrap(); - - let key_storage = PersistentKeyStorage::new(Arc::new(db)).unwrap(); - assert_eq!(key_storage.get(&key1), Ok(Some(value1))); - assert_eq!(key_storage.get(&key2), Ok(Some(value2))); - assert_eq!(key_storage.get(&key3), Ok(None)); - } -} diff --git a/secret-store/src/lib.rs b/secret-store/src/lib.rs deleted file mode 100644 index 2b7ff592ff2..00000000000 --- a/secret-store/src/lib.rs +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -extern crate byteorder; -extern crate ethabi; -extern crate ethereum_types; -extern crate hyper; -extern crate keccak_hash as hash; -extern crate kvdb; -extern crate kvdb_rocksdb; -extern crate parity_bytes as bytes; -extern crate parity_crypto as crypto; -extern crate parity_runtime; -extern crate parking_lot; -extern crate percent_encoding; -extern crate rustc_hex; -extern crate serde; -extern crate serde_json; -extern crate tiny_keccak; -extern crate tokio; -extern crate tokio_io; -extern crate tokio_service; -extern crate url; -extern crate jsonrpc_server_utils; - -extern crate ethabi_derive; -#[macro_use] -extern crate ethabi_contract; -#[macro_use] -extern crate futures; -#[macro_use] -extern crate serde_derive; -#[macro_use] -extern crate lazy_static; -#[macro_use] -extern crate log; - -#[cfg(test)] -extern crate env_logger; -#[cfg(test)] -extern crate tempdir; - -mod key_server_cluster; -mod types; - -mod traits; -mod acl_storage; -mod key_server; -mod key_storage; -mod serialization; -mod key_server_set; -mod node_key_pair; -mod listener; -mod blockchain; -mod migration; - -use std::sync::Arc; -use kvdb::KeyValueDB; -use kvdb_rocksdb::{Database, DatabaseConfig}; -use parity_runtime::Executor; - -pub use types::{ServerKeyId, EncryptedDocumentKey, RequestSignature, Public, - Error, NodeAddress, ServiceConfiguration, ClusterConfiguration}; -pub use traits::KeyServer; -pub use blockchain::{SecretStoreChain, SigningKeyPair, ContractAddress, BlockId, BlockNumber, NewBlocksNotify, Filter}; -pub use self::node_key_pair::PlainNodeKeyPair; - -/// Open a secret store DB using the given secret store data path. The DB path is one level beneath the data path. -pub fn open_secretstore_db(data_path: &str) -> Result, String> { - use std::path::PathBuf; - - migration::upgrade_db(data_path).map_err(|e| e.to_string())?; - - let mut db_path = PathBuf::from(data_path); - db_path.push("db"); - let db_path = db_path.to_str().ok_or_else(|| "Invalid secretstore path".to_string())?; - - let config = DatabaseConfig::with_columns(1); - Ok(Arc::new(Database::open(&config, &db_path).map_err(|e| format!("Error opening database: {:?}", e))?)) -} - -/// Start new key server instance -pub fn start(trusted_client: Arc, self_key_pair: Arc, mut config: ServiceConfiguration, - db: Arc, executor: Executor) -> Result, Error> -{ - let acl_storage: Arc = match config.acl_check_contract_address.take() { - Some(acl_check_contract_address) => acl_storage::OnChainAclStorage::new(trusted_client.clone(), acl_check_contract_address)?, - None => Arc::new(acl_storage::DummyAclStorage::default()), - }; - - let key_server_set = key_server_set::OnChainKeyServerSet::new(trusted_client.clone(), config.cluster_config.key_server_set_contract_address.take(), - self_key_pair.clone(), config.cluster_config.auto_migrate_enabled, config.cluster_config.nodes.clone())?; - let key_storage = Arc::new(key_storage::PersistentKeyStorage::new(db)?); - let key_server = Arc::new(key_server::KeyServerImpl::new(&config.cluster_config, key_server_set.clone(), self_key_pair.clone(), - acl_storage.clone(), key_storage.clone(), executor.clone())?); - let cluster = key_server.cluster(); - let key_server: Arc = key_server; - - // prepare HTTP listener - let http_listener = match config.listener_address { - Some(listener_address) => Some(listener::http_listener::KeyServerHttpListener::start(listener_address, config.cors, Arc::downgrade(&key_server), executor)?), - None => None, - }; - - // prepare service contract listeners - let create_service_contract = |address, name, api_mask| - Arc::new(listener::service_contract::OnChainServiceContract::new( - api_mask, - trusted_client.clone(), - name, - address, - self_key_pair.clone())); - - let mut contracts: Vec> = Vec::new(); - config.service_contract_address.map(|address| - create_service_contract(address, - listener::service_contract::SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), - listener::ApiMask::all())) - .map(|l| contracts.push(l)); - config.service_contract_srv_gen_address.map(|address| - create_service_contract(address, - listener::service_contract::SRV_KEY_GEN_SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), - listener::ApiMask { server_key_generation_requests: true, ..Default::default() })) - .map(|l| contracts.push(l)); - config.service_contract_srv_retr_address.map(|address| - create_service_contract(address, - listener::service_contract::SRV_KEY_RETR_SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), - listener::ApiMask { server_key_retrieval_requests: true, ..Default::default() })) - .map(|l| contracts.push(l)); - config.service_contract_doc_store_address.map(|address| - create_service_contract(address, - listener::service_contract::DOC_KEY_STORE_SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), - listener::ApiMask { document_key_store_requests: true, ..Default::default() })) - .map(|l| contracts.push(l)); - config.service_contract_doc_sretr_address.map(|address| - create_service_contract(address, - listener::service_contract::DOC_KEY_SRETR_SERVICE_CONTRACT_REGISTRY_NAME.to_owned(), - listener::ApiMask { document_key_shadow_retrieval_requests: true, ..Default::default() })) - .map(|l| contracts.push(l)); - - let contract: Option> = match contracts.len() { - 0 => None, - 1 => Some(contracts.pop().expect("contract.len() is 1; qed")), - _ => Some(Arc::new(listener::service_contract_aggregate::OnChainServiceContractAggregate::new(contracts))), - }; - - let contract_listener = match contract { - Some(contract) => Some({ - let listener = listener::service_contract_listener::ServiceContractListener::new( - listener::service_contract_listener::ServiceContractListenerParams { - contract: contract, - self_key_pair: self_key_pair.clone(), - key_server_set: key_server_set, - acl_storage: acl_storage, - cluster: cluster, - key_storage: key_storage, - } - )?; - trusted_client.add_listener(listener.clone()); - listener - }), - None => None, - }; - - Ok(Box::new(listener::Listener::new(key_server, http_listener, contract_listener))) -} diff --git a/secret-store/src/listener/http_listener.rs b/secret-store/src/listener/http_listener.rs deleted file mode 100644 index 663164fa00c..00000000000 --- a/secret-store/src/listener/http_listener.rs +++ /dev/null @@ -1,548 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeSet; -use std::sync::{Arc, Weak}; -use futures::future::{ok, result}; -use hyper::{self, Uri, Request as HttpRequest, Response as HttpResponse, Method as HttpMethod, - StatusCode as HttpStatusCode, Body, - header::{self, HeaderValue}, - server::conn::Http, - service::Service, -}; -use serde::Serialize; -use serde_json; -use tokio; -use tokio::net::TcpListener; -use parity_runtime::Executor; -use futures::{future, Future, Stream}; -use percent_encoding::percent_decode; - -use traits::KeyServer; -use serialization::{SerializableEncryptedDocumentKeyShadow, SerializableBytes, SerializablePublic}; -use types::{Error, Public, MessageHash, NodeAddress, RequestSignature, ServerKeyId, - EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId}; -use jsonrpc_server_utils::cors::{self, AllowCors, AccessControlAllowOrigin}; - -/// Key server http-requests listener. Available requests: -/// To generate server key: POST /shadow/{server_key_id}/{signature}/{threshold} -/// To store pregenerated encrypted document key: POST /shadow/{server_key_id}/{signature}/{common_point}/{encrypted_key} -/// To generate server && document key: POST /{server_key_id}/{signature}/{threshold} -/// To get public portion of server key: GET /server/{server_key_id}/{signature} -/// To get document key: GET /{server_key_id}/{signature} -/// To get document key shadow: GET /shadow/{server_key_id}/{signature} -/// To generate Schnorr signature with server key: GET /schnorr/{server_key_id}/{signature}/{message_hash} -/// To generate ECDSA signature with server key: GET /ecdsa/{server_key_id}/{signature}/{message_hash} -/// To change servers set: POST /admin/servers_set_change/{old_signature}/{new_signature} + BODY: json array of hex-encoded nodes ids - -type CorsDomains = Option>; - -pub struct KeyServerHttpListener { - _executor: Executor, - _handler: Arc, -} - -/// Parsed http request -#[derive(Debug, Clone, PartialEq)] -enum Request { - /// Invalid request - Invalid, - /// Generate server key. - GenerateServerKey(ServerKeyId, RequestSignature, usize), - /// Store document key. - StoreDocumentKey(ServerKeyId, RequestSignature, Public, Public), - /// Generate encryption key. - GenerateDocumentKey(ServerKeyId, RequestSignature, usize), - /// Request public portion of server key. - GetServerKey(ServerKeyId, RequestSignature), - /// Request encryption key of given document for given requestor. - GetDocumentKey(ServerKeyId, RequestSignature), - /// Request shadow of encryption key of given document for given requestor. - GetDocumentKeyShadow(ServerKeyId, RequestSignature), - /// Generate Schnorr signature for the message. - SchnorrSignMessage(ServerKeyId, RequestSignature, MessageHash), - /// Generate ECDSA signature for the message. - EcdsaSignMessage(ServerKeyId, RequestSignature, MessageHash), - /// Change servers set. - ChangeServersSet(RequestSignature, RequestSignature, BTreeSet), -} - -/// Cloneable http handler -#[derive(Clone)] -struct KeyServerHttpHandler { - handler: Arc, - cors: CorsDomains, -} - -/// Shared http handler -struct KeyServerSharedHttpHandler { - key_server: Weak, -} - - -impl KeyServerHttpListener { - /// Start KeyServer http listener - pub fn start(listener_address: NodeAddress, cors_domains: Option>, key_server: Weak, executor: Executor) -> Result { - let shared_handler = Arc::new(KeyServerSharedHttpHandler { - key_server: key_server, - }); - let cors: CorsDomains = cors_domains.map(|domains| domains.into_iter().map(AccessControlAllowOrigin::from).collect()); - let listener_address = format!("{}:{}", listener_address.address, listener_address.port).parse()?; - let listener = TcpListener::bind(&listener_address)?; - - let shared_handler2 = shared_handler.clone(); - - let server = listener.incoming() - .map_err(|e| warn!("Key server listener error: {:?}", e)) - .for_each(move |socket| { - let http = Http::new(); - let serve = http.serve_connection(socket, - KeyServerHttpHandler { handler: shared_handler2.clone(), cors: cors.clone() } - ).map(|_| ()).map_err(|e| { - warn!("Key server handler error: {:?}", e); - }); - - tokio::spawn(serve) - }); - - executor.spawn(server); - - let listener = KeyServerHttpListener { - _executor: executor, - _handler: shared_handler, - }; - - Ok(listener) - } -} - -impl KeyServerHttpHandler { - fn key_server(&self) -> Result, Error> { - self.handler.key_server.upgrade() - .ok_or_else(|| Error::Internal("KeyServer is already destroyed".into())) - } - - fn process( - self, - req_method: HttpMethod, - req_uri: Uri, - path: &str, - req_body: &[u8], - cors: AllowCors, - ) -> Box, Error=hyper::Error> + Send> { - match parse_request(&req_method, &path, &req_body) { - Request::GenerateServerKey(document, signature, threshold) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.generate_key(document, signature.into(), threshold)) - .then(move |result| ok(return_server_public_key("GenerateServerKey", &req_uri, cors, result)))), - Request::StoreDocumentKey(document, signature, common_point, encrypted_document_key) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.store_document_key( - document, - signature.into(), - common_point, - encrypted_document_key, - )) - .then(move |result| ok(return_empty("StoreDocumentKey", &req_uri, cors, result)))), - Request::GenerateDocumentKey(document, signature, threshold) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.generate_document_key( - document, - signature.into(), - threshold, - )) - .then(move |result| ok(return_document_key("GenerateDocumentKey", &req_uri, cors, result)))), - Request::GetServerKey(document, signature) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.restore_key_public( - document, - signature.into(), - )) - .then(move |result| ok(return_server_public_key("GetServerKey", &req_uri, cors, result)))), - Request::GetDocumentKey(document, signature) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.restore_document_key(document, signature.into())) - .then(move |result| ok(return_document_key("GetDocumentKey", &req_uri, cors, result)))), - Request::GetDocumentKeyShadow(document, signature) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.restore_document_key_shadow(document, signature.into())) - .then(move |result| ok(return_document_key_shadow("GetDocumentKeyShadow", &req_uri, cors, result)))), - Request::SchnorrSignMessage(document, signature, message_hash) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.sign_message_schnorr( - document, - signature.into(), - message_hash, - )) - .then(move |result| ok(return_message_signature("SchnorrSignMessage", &req_uri, cors, result)))), - Request::EcdsaSignMessage(document, signature, message_hash) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.sign_message_ecdsa( - document, - signature.into(), - message_hash, - )) - .then(move |result| ok(return_message_signature("EcdsaSignMessage", &req_uri, cors, result)))), - Request::ChangeServersSet(old_set_signature, new_set_signature, new_servers_set) => - Box::new(result(self.key_server()) - .and_then(move |key_server| key_server.change_servers_set( - old_set_signature, - new_set_signature, - new_servers_set, - )) - .then(move |result| ok(return_empty("ChangeServersSet", &req_uri, cors, result)))), - Request::Invalid => { - warn!(target: "secretstore", "Ignoring invalid {}-request {}", req_method, req_uri); - Box::new(ok(HttpResponse::builder() - .status(HttpStatusCode::BAD_REQUEST) - .body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed"))) - }, - } - } -} - -impl Service for KeyServerHttpHandler { - type ReqBody = Body; - type ResBody = Body; - type Error = hyper::Error; - type Future = Box, Error=Self::Error> + Send>; - - fn call(&mut self, req: HttpRequest) -> Self::Future { - let cors = cors::get_cors_allow_origin( - req.headers().get(header::ORIGIN).and_then(|value| value.to_str().ok()), - req.headers().get(header::HOST).and_then(|value| value.to_str().ok()), - &self.cors - ); - match cors { - AllowCors::Invalid => { - warn!(target: "secretstore", "Ignoring {}-request {} with unauthorized Origin header", req.method(), req.uri()); - Box::new(future::ok(HttpResponse::builder() - .status(HttpStatusCode::NOT_FOUND) - .body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed"))) - }, - _ => { - let req_method = req.method().clone(); - let req_uri = req.uri().clone(); - let path = req_uri.path().to_string(); - // We cannot consume Self because of the Service trait requirement. - let this = self.clone(); - - Box::new(req.into_body().concat2() - .and_then(move |body| this.process(req_method, req_uri, &path, &body, cors))) - } - } - } -} - -fn return_empty(req_type: &str, req_uri: &Uri, cors: AllowCors, empty: Result<(), Error>) -> HttpResponse { - return_bytes::(req_type, req_uri, cors, empty.map(|_| None)) -} - -fn return_server_public_key( - req_type: &str, - req_uri: &Uri, - cors: AllowCors, - server_public: Result, -) -> HttpResponse { - return_bytes(req_type, req_uri, cors, server_public.map(|k| Some(SerializablePublic(k)))) -} - -fn return_message_signature( - req_type: &str, - req_uri: &Uri, - cors: AllowCors, - signature: Result, -) -> HttpResponse { - return_bytes(req_type, req_uri, cors, signature.map(|s| Some(SerializableBytes(s)))) -} - -fn return_document_key( - req_type: &str, - req_uri: &Uri, - cors: AllowCors, - document_key: Result, -) -> HttpResponse { - return_bytes(req_type, req_uri, cors, document_key.map(|k| Some(SerializableBytes(k)))) -} - -fn return_document_key_shadow( - req_type: &str, - req_uri: &Uri, - cors: AllowCors, - document_key_shadow: Result, -) -> HttpResponse { - return_bytes(req_type, req_uri, cors, document_key_shadow.map(|k| Some(SerializableEncryptedDocumentKeyShadow { - decrypted_secret: k.decrypted_secret.into(), - common_point: k.common_point.expect("always filled when requesting document_key_shadow; qed").into(), - decrypt_shadows: k.decrypt_shadows.expect("always filled when requesting document_key_shadow; qed").into_iter().map(Into::into).collect() - }))) -} - -fn return_bytes( - req_type: &str, - req_uri: &Uri, - cors: AllowCors, - result: Result, Error>, -) -> HttpResponse { - match result { - Ok(Some(result)) => match serde_json::to_vec(&result) { - Ok(result) => { - let body: Body = result.into(); - let mut builder = HttpResponse::builder(); - builder.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8")); - if let AllowCors::Ok(AccessControlAllowOrigin::Value(origin)) = cors { - builder.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin.to_string()); - } - builder.body(body).expect("Error creating http response") - }, - Err(err) => { - warn!(target: "secretstore", "response to request {} has failed with: {}", req_uri, err); - HttpResponse::builder() - .status(HttpStatusCode::INTERNAL_SERVER_ERROR) - .body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed") - } - }, - Ok(None) => { - let mut builder = HttpResponse::builder(); - builder.status(HttpStatusCode::OK); - if let AllowCors::Ok(AccessControlAllowOrigin::Value(origin)) = cors { - builder.header(header::ACCESS_CONTROL_ALLOW_ORIGIN, origin.to_string()); - } - builder.body(Body::empty()).expect("Nothing to parse, cannot fail; qed") - }, - Err(err) => { - warn!(target: "secretstore", "{} request {} has failed with: {}", req_type, req_uri, err); - return_error(err) - }, - } -} - -fn return_error(err: Error) -> HttpResponse { - let status = match err { - | Error::AccessDenied - | Error::ConsensusUnreachable - | Error::ConsensusTemporaryUnreachable => - HttpStatusCode::FORBIDDEN, - | Error::ServerKeyIsNotFound - | Error::DocumentKeyIsNotFound => - HttpStatusCode::NOT_FOUND, - | Error::InsufficientRequesterData(_) - | Error::Hyper(_) - | Error::Serde(_) - | Error::DocumentKeyAlreadyStored - | Error::ServerKeyAlreadyGenerated => - HttpStatusCode::BAD_REQUEST, - _ => HttpStatusCode::INTERNAL_SERVER_ERROR, - }; - - let mut res = HttpResponse::builder(); - res.status(status); - - // return error text. ignore errors when returning error - let error_text = format!("\"{}\"", err); - if let Ok(error_text) = serde_json::to_vec(&error_text) { - res.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json; charset=utf-8")); - res.body(error_text.into()) - .expect("`error_text` is a formatted string, parsing cannot fail; qed") - } else { - res.body(Body::empty()) - .expect("Nothing to parse, cannot fail; qed") - } -} - -fn parse_request(method: &HttpMethod, uri_path: &str, body: &[u8]) -> Request { - let uri_path = match percent_decode(uri_path.as_bytes()).decode_utf8() { - Ok(path) => path, - Err(_) => return Request::Invalid, - }; - - let path: Vec = uri_path.trim_start_matches('/').split('/').map(Into::into).collect(); - if path.len() == 0 { - return Request::Invalid; - } - - if path[0] == "admin" { - return parse_admin_request(method, path, body); - } - - let is_known_prefix = &path[0] == "shadow" || &path[0] == "schnorr" || &path[0] == "ecdsa" || &path[0] == "server"; - let (prefix, args_offset) = if is_known_prefix { (&*path[0], 1) } else { ("", 0) }; - let args_count = path.len() - args_offset; - if args_count < 2 || path[args_offset].is_empty() || path[args_offset + 1].is_empty() { - return Request::Invalid; - } - - let document = match path[args_offset].parse() { - Ok(document) => document, - _ => return Request::Invalid, - }; - let signature = match path[args_offset + 1].parse() { - Ok(signature) => signature, - _ => return Request::Invalid, - }; - - let threshold = path.get(args_offset + 2).map(|v| v.parse()); - let message_hash = path.get(args_offset + 2).map(|v| v.parse()); - let common_point = path.get(args_offset + 2).map(|v| v.parse()); - let encrypted_key = path.get(args_offset + 3).map(|v| v.parse()); - match (prefix, args_count, method, threshold, message_hash, common_point, encrypted_key) { - ("shadow", 3, &HttpMethod::POST, Some(Ok(threshold)), _, _, _) => - Request::GenerateServerKey(document, signature, threshold), - ("shadow", 4, &HttpMethod::POST, _, _, Some(Ok(common_point)), Some(Ok(encrypted_key))) => - Request::StoreDocumentKey(document, signature, common_point, encrypted_key), - ("", 3, &HttpMethod::POST, Some(Ok(threshold)), _, _, _) => - Request::GenerateDocumentKey(document, signature, threshold), - ("server", 2, &HttpMethod::GET, _, _, _, _) => - Request::GetServerKey(document, signature), - ("", 2, &HttpMethod::GET, _, _, _, _) => - Request::GetDocumentKey(document, signature), - ("shadow", 2, &HttpMethod::GET, _, _, _, _) => - Request::GetDocumentKeyShadow(document, signature), - ("schnorr", 3, &HttpMethod::GET, _, Some(Ok(message_hash)), _, _) => - Request::SchnorrSignMessage(document, signature, message_hash), - ("ecdsa", 3, &HttpMethod::GET, _, Some(Ok(message_hash)), _, _) => - Request::EcdsaSignMessage(document, signature, message_hash), - _ => Request::Invalid, - } -} - -fn parse_admin_request(method: &HttpMethod, path: Vec, body: &[u8]) -> Request { - let args_count = path.len(); - if *method != HttpMethod::POST || args_count != 4 || path[1] != "servers_set_change" { - return Request::Invalid; - } - - let old_set_signature = match path[2].parse() { - Ok(signature) => signature, - _ => return Request::Invalid, - }; - - let new_set_signature = match path[3].parse() { - Ok(signature) => signature, - _ => return Request::Invalid, - }; - - let new_servers_set: BTreeSet = match serde_json::from_slice(body) { - Ok(new_servers_set) => new_servers_set, - _ => return Request::Invalid, - }; - - Request::ChangeServersSet(old_set_signature, new_set_signature, - new_servers_set.into_iter().map(Into::into).collect()) -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::str::FromStr; - use hyper::Method as HttpMethod; - use crypto::publickey::Public; - use traits::KeyServer; - use key_server::tests::DummyKeyServer; - use types::NodeAddress; - use parity_runtime::Runtime; - use ethereum_types::H256; - use super::{parse_request, Request, KeyServerHttpListener}; - - #[test] - fn http_listener_successfully_drops() { - let key_server: Arc = Arc::new(DummyKeyServer::default()); - let address = NodeAddress { address: "127.0.0.1".into(), port: 9000 }; - let runtime = Runtime::with_thread_count(1); - let listener = KeyServerHttpListener::start(address, None, Arc::downgrade(&key_server), - runtime.executor()).unwrap(); - drop(listener); - } - - #[test] - fn parse_request_successful() { - // POST /shadow/{server_key_id}/{signature}/{threshold} => generate server key - assert_eq!(parse_request(&HttpMethod::POST, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()), - Request::GenerateServerKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - 2)); - // POST /shadow/{server_key_id}/{signature}/{common_point}/{encrypted_key} => store encrypted document key - assert_eq!(parse_request(&HttpMethod::POST, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8/1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb", Default::default()), - Request::StoreDocumentKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - "b486d3840218837b035c66196ecb15e6b067ca20101e11bd5e626288ab6806ecc70b8307012626bd512bad1559112d11d21025cef48cc7a1d2f3976da08f36c8".parse().unwrap(), - "1395568277679f7f583ab7c0992da35f26cde57149ee70e524e49bdae62db3e18eb96122501e7cbb798b784395d7bb5a499edead0706638ad056d886e56cf8fb".parse().unwrap())); - // POST /{server_key_id}/{signature}/{threshold} => generate server && document key - assert_eq!(parse_request(&HttpMethod::POST, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/2", Default::default()), - Request::GenerateDocumentKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - 2)); - // GET /server/{server_key_id}/{signature} => get public portion of server key - assert_eq!(parse_request(&HttpMethod::GET, "/server/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()), - Request::GetServerKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); - // GET /{server_key_id}/{signature} => get document key - assert_eq!(parse_request(&HttpMethod::GET, "/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()), - Request::GetDocumentKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); - assert_eq!(parse_request(&HttpMethod::GET, "/%30000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()), - Request::GetDocumentKey(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); - // GET /shadow/{server_key_id}/{signature} => get document key shadow - assert_eq!(parse_request(&HttpMethod::GET, "/shadow/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", Default::default()), - Request::GetDocumentKeyShadow(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap())); - // GET /schnorr/{server_key_id}/{signature}/{message_hash} => schnorr-sign message with server key - assert_eq!(parse_request(&HttpMethod::GET, "/schnorr/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()), - Request::SchnorrSignMessage(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap())); - // GET /ecdsa/{server_key_id}/{signature}/{message_hash} => ecdsa-sign message with server key - assert_eq!(parse_request(&HttpMethod::GET, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c", Default::default()), - Request::EcdsaSignMessage(H256::from_str("0000000000000000000000000000000000000000000000000000000000000001").unwrap(), - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - "281b6bf43cb86d0dc7b98e1b7def4a80f3ce16d28d2308f934f116767306f06c".parse().unwrap())); - // POST /admin/servers_set_change/{old_set_signature}/{new_set_signature} + body - let node1: Public = "843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91".parse().unwrap(); - let node2: Public = "07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3".parse().unwrap(); - let nodes = vec![node1, node2].into_iter().collect(); - assert_eq!(parse_request(&HttpMethod::POST, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/b199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", - &r#"["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91", - "0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#.as_bytes()), - Request::ChangeServersSet( - "a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - "b199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01".parse().unwrap(), - nodes, - )); - } - - #[test] - fn parse_request_failed() { - assert_eq!(parse_request(&HttpMethod::GET, "", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/shadow", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "///2", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/shadow///2", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/0000000000000000000000000000000000000000000000000000000000000001", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/0000000000000000000000000000000000000000000000000000000000000001/", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/a/b", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/schnorr/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::GET, "/ecdsa/0000000000000000000000000000000000000000000000000000000000000001/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/0000000000000000000000000000000000000000000000000000000000000002/0000000000000000000000000000000000000000000000000000000000000002", Default::default()), Request::Invalid); - assert_eq!(parse_request(&HttpMethod::POST, "/admin/servers_set_change/xxx/yyy", - &r#"["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91", - "0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]"#.as_bytes()), - Request::Invalid); - assert_eq!(parse_request(&HttpMethod::POST, "/admin/servers_set_change/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01/a199fb39e11eefb61c78a4074a53c0d4424600a3e74aad4fb9d93a26c30d067e1d4d29936de0c73f19827394a1dd049480a0d581aee7ae7546968da7d3d1c2fd01", "".as_bytes()), - Request::Invalid); - } -} diff --git a/secret-store/src/listener/mod.rs b/secret-store/src/listener/mod.rs deleted file mode 100644 index 1911a4d59dd..00000000000 --- a/secret-store/src/listener/mod.rs +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -pub mod http_listener; -pub mod service_contract; -pub mod service_contract_aggregate; -pub mod service_contract_listener; -mod tasks_queue; - -use std::collections::BTreeSet; -use std::sync::Arc; -use futures::Future; -use traits::{ServerKeyGenerator, DocumentKeyServer, MessageSigner, AdminSessionsServer, KeyServer}; -use types::{Error, Public, MessageHash, EncryptedMessageSignature, RequestSignature, ServerKeyId, - EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId, Requester}; - -/// Available API mask. -#[derive(Debug, Default)] -pub struct ApiMask { - /// Accept server key generation requests. - pub server_key_generation_requests: bool, - /// Accept server key retrieval requests. - pub server_key_retrieval_requests: bool, - /// Accept document key store requests. - pub document_key_store_requests: bool, - /// Accept document key shadow retrieval requests. - pub document_key_shadow_retrieval_requests: bool, -} - -/// Combined HTTP + service contract listener. -pub struct Listener { - key_server: Arc, - _http: Option, - _contract: Option>, -} - -impl ApiMask { - /// Create mask that accepts all requests. - pub fn all() -> Self { - ApiMask { - server_key_generation_requests: true, - server_key_retrieval_requests: true, - document_key_store_requests: true, - document_key_shadow_retrieval_requests: true, - } - } -} - -impl Listener { - /// Create new listener. - pub fn new(key_server: Arc, http: Option, contract: Option>) -> Self { - Self { - key_server: key_server, - _http: http, - _contract: contract, - } - } -} - -impl KeyServer for Listener {} - -impl ServerKeyGenerator for Listener { - fn generate_key( - &self, - key_id: ServerKeyId, - author: Requester, - threshold: usize, - ) -> Box + Send> { - self.key_server.generate_key(key_id, author, threshold) - } - - fn restore_key_public( - &self, - key_id: ServerKeyId, - author: Requester, - ) -> Box + Send> { - self.key_server.restore_key_public(key_id, author) - } -} - -impl DocumentKeyServer for Listener { - fn store_document_key( - &self, - key_id: ServerKeyId, - author: Requester, - common_point: Public, - encrypted_document_key: Public, - ) -> Box + Send> { - self.key_server.store_document_key(key_id, author, common_point, encrypted_document_key) - } - - fn generate_document_key( - &self, - key_id: ServerKeyId, - author: Requester, - threshold: usize, - ) -> Box + Send> { - self.key_server.generate_document_key(key_id, author, threshold) - } - - fn restore_document_key( - &self, - key_id: ServerKeyId, - requester: Requester, - ) -> Box + Send> { - self.key_server.restore_document_key(key_id, requester) - } - - fn restore_document_key_shadow( - &self, - key_id: ServerKeyId, - requester: Requester, - ) -> Box + Send> { - self.key_server.restore_document_key_shadow(key_id, requester) - } -} - -impl MessageSigner for Listener { - fn sign_message_schnorr( - &self, - key_id: ServerKeyId, - requester: Requester, - message: MessageHash, - ) -> Box + Send> { - self.key_server.sign_message_schnorr(key_id, requester, message) - } - - fn sign_message_ecdsa( - &self, - key_id: ServerKeyId, - requester: Requester, - message: MessageHash, - ) -> Box + Send> { - self.key_server.sign_message_ecdsa(key_id, requester, message) - } -} - -impl AdminSessionsServer for Listener { - fn change_servers_set( - &self, - old_set_signature: RequestSignature, - new_set_signature: RequestSignature, - new_servers_set: BTreeSet, - ) -> Box + Send> { - self.key_server.change_servers_set(old_set_signature, new_set_signature, new_servers_set) - } -} diff --git a/secret-store/src/listener/service_contract.rs b/secret-store/src/listener/service_contract.rs deleted file mode 100644 index 6ebe1796a94..00000000000 --- a/secret-store/src/listener/service_contract.rs +++ /dev/null @@ -1,807 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use parking_lot::RwLock; -use ethabi::RawLog; -use ethabi::FunctionOutputDecoder; -use crypto::publickey::{Public, public_to_address}; -use hash::keccak; -use bytes::Bytes; -use ethereum_types::{H256, U256, Address, H512}; -use listener::ApiMask; -use listener::service_contract_listener::ServiceTask; -use blockchain::{SecretStoreChain, Filter, SigningKeyPair, ContractAddress, BlockId}; -use ServerKeyId; - -use_contract!(service, "res/service.json"); - -/// Name of the general SecretStore contract in the registry. -pub const SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service"; -/// Name of the server key generation SecretStore contract in the registry. -pub const SRV_KEY_GEN_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_srv_gen"; -/// Name of the server key retrieval SecretStore contract in the registry. -pub const SRV_KEY_RETR_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_srv_retr"; -/// Name of the document key store SecretStore contract in the registry. -pub const DOC_KEY_STORE_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_doc_store"; -/// Name of the document key retrieval SecretStore contract in the registry. -pub const DOC_KEY_SRETR_SERVICE_CONTRACT_REGISTRY_NAME: &'static str = "secretstore_service_doc_sretr"; - -/// Server key generation has been requested. -const SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME: &'static [u8] = &*b"ServerKeyGenerationRequested(bytes32,address,uint8)"; -/// Server key retrieval has been requested. -const SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME: &'static [u8] = &*b"ServerKeyRetrievalRequested(bytes32)"; -/// Document key store has been requested. -const DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME: &'static [u8] = &*b"DocumentKeyStoreRequested(bytes32,address,bytes,bytes)"; -/// Document key common part retrieval has been requested. -const DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME: &'static [u8] = &*b"DocumentKeyCommonRetrievalRequested(bytes32,address)"; -/// Document key personal part retrieval has been requested. -const DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME: &'static [u8] = &*b"DocumentKeyPersonalRetrievalRequested(bytes32,bytes)"; - -lazy_static! { - pub static ref SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH: H256 = keccak(SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME); - pub static ref SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH: H256 = keccak(SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME); - pub static ref DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH: H256 = keccak(DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME); - pub static ref DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH: H256 = keccak(DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME); - pub static ref DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH: H256 = keccak(DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME); -} - -/// Service contract trait. -pub trait ServiceContract: Send + Sync { - /// Update contract when new blocks are enacted. Returns true if contract is installed && up-to-date (i.e. chain is synced). - fn update(&self) -> bool; - /// Read recent contract logs. Returns topics of every entry. - fn read_logs(&self) -> Box>; - /// Publish generated key. - fn read_pending_requests(&self) -> Box>; - /// Publish generated server key. - fn publish_generated_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String>; - /// Publish server key generation error. - fn publish_server_key_generation_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>; - /// Publish retrieved server key. - fn publish_retrieved_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String>; - /// Publish server key retrieval error. - fn publish_server_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>; - /// Publish stored document key. - fn publish_stored_document_key(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>; - /// Publish document key store error. - fn publish_document_key_store_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String>; - /// Publish retrieved document key common. - fn publish_retrieved_document_key_common(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String>; - /// Publish retrieved document key personal. - fn publish_retrieved_document_key_personal(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String>; - /// Publish document key store error. - fn publish_document_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String>; -} - -/// On-chain service contract. -pub struct OnChainServiceContract { - /// Requests mask. - mask: ApiMask, - /// Blockchain client. - client: Arc, - /// This node key pair. - self_key_pair: Arc, - /// Contract registry name (if any). - name: String, - /// Contract address source. - address_source: ContractAddress, - /// Contract. - data: RwLock, -} - -/// On-chain service contract data. -struct ServiceData { - /// Current contract address. - pub contract_address: Option
, - /// Last block we have read logs from. - pub last_log_block: Option, -} - -/// Pending requests iterator. -struct PendingRequestsIterator Option<(bool, ServiceTask)>> { - /// Pending request read function. - read_request: F, - /// Current request index. - index: U256, - /// Requests length. - length: U256, -} - -/// Server key generation related functions. -struct ServerKeyGenerationService; -/// Server key retrieval related functions. -struct ServerKeyRetrievalService; -/// Document key store related functions. -struct DocumentKeyStoreService; -/// Document key shadow retrievalrelated functions. -struct DocumentKeyShadowRetrievalService; - -impl OnChainServiceContract { - /// Create new on-chain service contract. - pub fn new(mask: ApiMask, client: Arc, name: String, address_source: ContractAddress, self_key_pair: Arc) -> Self { - let contract = OnChainServiceContract { - mask: mask, - client: client, - self_key_pair: self_key_pair, - name: name, - address_source: address_source, - data: RwLock::new(ServiceData { - contract_address: None, - last_log_block: None, - }), - }; - - contract.update_contract_address(); - contract - } - - /// Send transaction to the service contract. - fn send_contract_transaction(&self, tx_name: &str, origin: &Address, server_key_id: &ServerKeyId, is_response_required: C, prepare_tx: P) -> Result<(), String> - where C: FnOnce(&dyn SecretStoreChain, &Address, &ServerKeyId, &Address) -> bool, - P: FnOnce(&dyn SecretStoreChain, &Address) -> Result { - // only publish if contract address is set && client is online - if !self.client.is_trusted() { - return Err("trusted client is required to publish key".into()) - } - - // only publish key if contract waits for publication - // failing is ok here - it could be that enough confirmations have been recevied - // or key has been requested using HTTP API - let self_address = public_to_address(self.self_key_pair.public()); - if !is_response_required(&*self.client, origin, server_key_id, &self_address) { - return Ok(()); - } - - // prepare transaction data - let transaction_data = prepare_tx(&*self.client, origin)?; - - // send transaction - self.client.transact_contract( - origin.clone(), - transaction_data - ).map_err(|e| format!("{}", e))?; - - trace!(target: "secretstore", "{}: transaction {} sent to service contract", - self.self_key_pair.public(), tx_name); - - Ok(()) - } - - /// Create task-specific pending requests iterator. - fn create_pending_requests_iterator< - C: 'static + Fn(&dyn SecretStoreChain, &Address, &BlockId) -> Result, - R: 'static + Fn(&dyn SigningKeyPair, &dyn SecretStoreChain, &Address, &BlockId, U256) -> Result<(bool, ServiceTask), String> - >(&self, client: Arc, contract_address: &Address, block: &BlockId, get_count: C, read_item: R) -> Box> { - get_count(&*client, contract_address, block) - .map(|count| { - let client = client.clone(); - let self_key_pair = self.self_key_pair.clone(); - let contract_address = contract_address.clone(); - let block = block.clone(); - Box::new(PendingRequestsIterator { - read_request: move |index| read_item(&*self_key_pair, &*client, &contract_address, &block, index) - .map_err(|error| { - warn!(target: "secretstore", "{}: reading pending request failed: {}", - self_key_pair.public(), error); - error - }) - .ok(), - index: 0.into(), - length: count, - }) as Box> - }) - .map_err(|error| { - warn!(target: "secretstore", "{}: creating pending requests iterator failed: {}", - self.self_key_pair.public(), error); - error - }) - .ok() - .unwrap_or_else(|| Box::new(::std::iter::empty())) - } - - /// Update service contract address. - fn update_contract_address(&self) -> bool { - let contract_address = self.client.read_contract_address(&self.name, &self.address_source); - let mut data = self.data.write(); - if contract_address != data.contract_address { - trace!(target: "secretstore", "{}: installing {} service contract from address {:?}", - self.self_key_pair.public(), self.name, contract_address); - - data.contract_address = contract_address; - } - - data.contract_address.is_some() - } -} - -impl ServiceContract for OnChainServiceContract { - fn update(&self) -> bool { - self.update_contract_address() && self.client.is_trusted() - } - - fn read_logs(&self) -> Box> { - if !self.client.is_trusted() { - warn!(target: "secretstore", "{}: client is offline during read_logs call", - self.self_key_pair.public()); - return Box::new(::std::iter::empty()); - } - - let address = match self.data.read().contract_address { - Some(address) => address, - None => return Box::new(::std::iter::empty()), // no contract installed - }; - let confirmed_block = match self.client.get_confirmed_block_hash() { - Some(confirmed_block) => confirmed_block, - None => return Box::new(::std::iter::empty()), // no block with enough confirmations - }; - - let request_logs = self.client.retrieve_last_logs(Filter { - from_block: BlockId::Hash(self.data.read().last_log_block.unwrap_or_else(|| confirmed_block)), - address: Some(vec![address]), - topics: vec![Some(mask_topics(&self.mask))], - }).unwrap_or_default(); - - let mut data = self.data.write(); - data.last_log_block = Some(confirmed_block.clone()); - - Box::new(request_logs.into_iter() - .filter_map(|log| { - if log.topics[0] == *SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH { - ServerKeyGenerationService::parse_log(&address, log) - } else if log.topics[0] == *SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { - ServerKeyRetrievalService::parse_log(&address, log) - } else if log.topics[0] == *DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH { - DocumentKeyStoreService::parse_log(&address, log) - } else if log.topics[0] == *DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { - DocumentKeyShadowRetrievalService::parse_common_request_log(&address, log) - } else if log.topics[0] == *DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH { - DocumentKeyShadowRetrievalService::parse_personal_request_log(&address, log) - } else { - Err("unknown type of log entry".into()) - } - .map_err(|error| { - warn!(target: "secretstore", "{}: error parsing log entry from service contract: {}", - self.self_key_pair.public(), error); - error - }) - .ok() - }).collect::>().into_iter()) - } - - fn read_pending_requests(&self) -> Box> { - if !self.client.is_trusted() { - return Box::new(::std::iter::empty()) - } - - // we only need requests that are here from the last confirm block - let data = self.data.read(); - match data.contract_address { - None => Box::new(::std::iter::empty()), - Some(contract_address) => self.client.get_confirmed_block_hash() - .map(|b| { - let block = BlockId::Hash(b); - let iter = match self.mask.server_key_generation_requests { - true => Box::new(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, - &ServerKeyGenerationService::read_pending_requests_count, - &ServerKeyGenerationService::read_pending_request)) as Box>, - false => Box::new(::std::iter::empty()), - }; - let iter = match self.mask.server_key_retrieval_requests { - true => Box::new(iter.chain(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, - &ServerKeyRetrievalService::read_pending_requests_count, - &ServerKeyRetrievalService::read_pending_request))), - false => iter, - }; - let iter = match self.mask.document_key_store_requests { - true => Box::new(iter.chain(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, - &DocumentKeyStoreService::read_pending_requests_count, - &DocumentKeyStoreService::read_pending_request))), - false => iter, - }; - let iter = match self.mask.document_key_shadow_retrieval_requests { - true => Box::new(iter.chain(self.create_pending_requests_iterator(self.client.clone(), &contract_address, &block, - &DocumentKeyShadowRetrievalService::read_pending_requests_count, - &DocumentKeyShadowRetrievalService::read_pending_request))), - false => iter - }; - - iter - }) - .unwrap_or_else(|| Box::new(::std::iter::empty())) - } - } - - fn publish_generated_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String> { - self.send_contract_transaction("publish_generated_server_key", origin, server_key_id, ServerKeyGenerationService::is_response_required, - |_, _| Ok(ServerKeyGenerationService::prepare_pubish_tx_data(server_key_id, &server_key))) - } - - fn publish_server_key_generation_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.send_contract_transaction("publish_server_key_generation_error", origin, server_key_id, ServerKeyGenerationService::is_response_required, - |_, _| Ok(ServerKeyGenerationService::prepare_error_tx_data(server_key_id))) - } - - fn publish_retrieved_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String> { - let threshold = serialize_threshold(threshold)?; - self.send_contract_transaction("publish_retrieved_server_key", origin, server_key_id, ServerKeyRetrievalService::is_response_required, - |_, _| Ok(ServerKeyRetrievalService::prepare_pubish_tx_data(server_key_id, server_key, threshold))) - } - - fn publish_server_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.send_contract_transaction("publish_server_key_retrieval_error", origin, server_key_id, ServerKeyRetrievalService::is_response_required, - |_, _| Ok(ServerKeyRetrievalService::prepare_error_tx_data(server_key_id))) - } - - fn publish_stored_document_key(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.send_contract_transaction("publish_stored_document_key", origin, server_key_id, DocumentKeyStoreService::is_response_required, - |_, _| Ok(DocumentKeyStoreService::prepare_pubish_tx_data(server_key_id))) - } - - fn publish_document_key_store_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.send_contract_transaction("publish_document_key_store_error", origin, server_key_id, DocumentKeyStoreService::is_response_required, - |_, _| Ok(DocumentKeyStoreService::prepare_error_tx_data(server_key_id))) - } - - fn publish_retrieved_document_key_common(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String> { - let threshold = serialize_threshold(threshold)?; - self.send_contract_transaction("publish_retrieved_document_key_common", origin, server_key_id, - |client, contract_address, server_key_id, key_server| - DocumentKeyShadowRetrievalService::is_response_required(client, contract_address, server_key_id, requester, key_server), - |_, _| - Ok(DocumentKeyShadowRetrievalService::prepare_pubish_common_tx_data(server_key_id, requester, common_point, threshold)) - ) - } - - fn publish_retrieved_document_key_personal(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String> { - self.send_contract_transaction("publish_retrieved_document_key_personal", origin, server_key_id, |_, _, _, _| true, - move |client, address| - DocumentKeyShadowRetrievalService::prepare_pubish_personal_tx_data(client, address, server_key_id, requester, participants, decrypted_secret, shadow) - ) - } - - fn publish_document_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> { - self.send_contract_transaction("publish_document_key_retrieval_error", origin, server_key_id, - |client, contract_address, server_key_id, key_server| - DocumentKeyShadowRetrievalService::is_response_required(client, contract_address, server_key_id, requester, key_server), - |_, _| - Ok(DocumentKeyShadowRetrievalService::prepare_error_tx_data(server_key_id, requester)) - ) - } -} - -impl Iterator for PendingRequestsIterator where F: Fn(U256) -> Option<(bool, ServiceTask)> { - type Item = (bool, ServiceTask); - - fn next(&mut self) -> Option<(bool, ServiceTask)> { - if self.index >= self.length { - return None; - } - - let index = self.index.clone(); - self.index = self.index + 1; - - (self.read_request)(index) - } -} - -/// Returns vector of logs topics to listen to. -pub fn mask_topics(mask: &ApiMask) -> Vec { - let mut topics = Vec::new(); - if mask.server_key_generation_requests { - topics.push(*SERVER_KEY_GENERATION_REQUESTED_EVENT_NAME_HASH); - } - if mask.server_key_retrieval_requests { - topics.push(*SERVER_KEY_RETRIEVAL_REQUESTED_EVENT_NAME_HASH); - } - if mask.document_key_store_requests { - topics.push(*DOCUMENT_KEY_STORE_REQUESTED_EVENT_NAME_HASH); - } - if mask.document_key_shadow_retrieval_requests { - topics.push(*DOCUMENT_KEY_COMMON_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH); - topics.push(*DOCUMENT_KEY_PERSONAL_PART_RETRIEVAL_REQUESTED_EVENT_NAME_HASH); - } - topics -} - -impl ServerKeyGenerationService { - /// Parse request log entry. - pub fn parse_log(origin: &Address, raw_log: RawLog) -> Result { - match service::events::server_key_generation_requested::parse_log(raw_log) { - Ok(l) => Ok(ServiceTask::GenerateServerKey(origin.clone(), l.server_key_id, l.author, parse_threshold(l.threshold)?)), - Err(e) => Err(format!("{}", e)), - } - } - - /// Check if response from key server is required. - pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { - // we're checking confirmation in Latest block, because we're interested in latest contract state here - let (encoded, decoder) = service::functions::is_server_key_generation_response_required::call(*server_key_id, *key_server); - match client.call_contract(BlockId::Latest, *contract_address, encoded) { - Err(_) => true, - Ok(data) => decoder.decode(&data).unwrap_or(true) - } - } - - /// Prepare publish key transaction data. - pub fn prepare_pubish_tx_data(server_key_id: &ServerKeyId, server_key_public: &Public) -> Bytes { - service::functions::server_key_generated::encode_input(*server_key_id, server_key_public.as_bytes().to_vec()) - } - - /// Prepare error transaction data. - pub fn prepare_error_tx_data(server_key_id: &ServerKeyId) -> Bytes { - service::functions::server_key_generation_error::encode_input(*server_key_id) - } - - /// Read pending requests count. - fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { - let (encoded, decoder) = service::functions::server_key_generation_requests_count::call(); - decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string()) - } - - /// Read pending request. - fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { - let self_address = public_to_address(self_key_pair.public()); - - let (encoded, decoder) = service::functions::get_server_key_generation_request::call(index); - let (server_key_id, author, threshold) = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - let threshold = parse_threshold(threshold)?; - - let (encoded, decoder) = service::functions::is_server_key_generation_response_required::call(server_key_id, self_address); - let not_confirmed = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let task = ServiceTask::GenerateServerKey( - contract_address.clone(), - server_key_id, - author, - threshold, - ); - - Ok((not_confirmed, task)) - } -} - -impl ServerKeyRetrievalService { - /// Parse request log entry. - pub fn parse_log(origin: &Address, raw_log: RawLog) -> Result { - match service::events::server_key_retrieval_requested::parse_log(raw_log) { - Ok(l) => Ok(ServiceTask::RetrieveServerKey(*origin, l.server_key_id)), - Err(e) => Err(e.to_string()) - } - } - - /// Check if response from key server is required. - pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { - // we're checking confirmation in Latest block, because we're interested in latest contract state here - let (encoded, decoder) = service::functions::is_server_key_retrieval_response_required::call(*server_key_id, *key_server); - match client.call_contract(BlockId::Latest, *contract_address, encoded) { - Err(_) => true, - Ok(data) => decoder.decode(&data).unwrap_or(true) - } - } - - /// Prepare publish key transaction data. - pub fn prepare_pubish_tx_data(server_key_id: &ServerKeyId, server_key_public: Public, threshold: U256) -> Bytes { - service::functions::server_key_retrieved::encode_input(*server_key_id, server_key_public.as_bytes().to_vec(), threshold) - } - - /// Prepare error transaction data. - pub fn prepare_error_tx_data(server_key_id: &ServerKeyId) -> Bytes { - service::functions::server_key_retrieval_error::encode_input(*server_key_id) - } - - /// Read pending requests count. - fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { - let (encoded, decoder) = service::functions::server_key_retrieval_requests_count::call(); - decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string()) - } - - /// Read pending request. - fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { - let self_address = public_to_address(self_key_pair.public()); - - let (encoded, decoder) = service::functions::get_server_key_retrieval_request::call(index); - let server_key_id = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let (encoded, decoder) = service::functions::is_server_key_retrieval_response_required::call(server_key_id, self_address); - let not_confirmed = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let task = ServiceTask::RetrieveServerKey( - *contract_address, - server_key_id, - ); - - Ok((not_confirmed, task)) - } -} - -impl DocumentKeyStoreService { - /// Parse request log entry. - pub fn parse_log(origin: &Address, raw_log: RawLog) -> Result { - match service::events::document_key_store_requested::parse_log(raw_log) { - Ok(l) => Ok(ServiceTask::StoreDocumentKey( - origin.clone(), - l.server_key_id, - l.author, - H512::from_slice(&*l.common_point), - H512::from_slice(&*l.encrypted_point), - )), - Err(e) => Err(format!("{}", e)), - } - } - - /// Check if response from key server is required. - pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, key_server: &Address) -> bool { - // we're checking confirmation in Latest block, because we're interested in latest contract state here - let (encoded, decoder) = service::functions::is_document_key_store_response_required::call(*server_key_id, *key_server); - match client.call_contract(BlockId::Latest, *contract_address, encoded) { - Err(_) => true, - Ok(data) => decoder.decode(&data).unwrap_or(true) - } - } - - /// Prepare publish key transaction data. - pub fn prepare_pubish_tx_data(server_key_id: &ServerKeyId) -> Bytes { - service::functions::document_key_stored::encode_input(*server_key_id) - } - - /// Prepare error transaction data. - pub fn prepare_error_tx_data(server_key_id: &ServerKeyId) -> Bytes { - service::functions::document_key_store_error::encode_input(*server_key_id) - } - - /// Read pending requests count. - fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { - let (encoded, decoder) = service::functions::document_key_store_requests_count::call(); - decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string()) - } - - /// Read pending request. - fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { - let self_address = public_to_address(self_key_pair.public()); - let (encoded, decoder) = service::functions::get_document_key_store_request::call(index); - let (server_key_id, author, common_point, encrypted_point) = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let (encoded, decoder) = service::functions::is_document_key_store_response_required::call(server_key_id, self_address); - let not_confirmed = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let task = ServiceTask::StoreDocumentKey( - *contract_address, - server_key_id, - author, - Public::from_slice(&common_point), - Public::from_slice(&encrypted_point), - ); - - Ok((not_confirmed, task)) - } -} - -impl DocumentKeyShadowRetrievalService { - /// Parse common request log entry. - pub fn parse_common_request_log(origin: &Address, raw_log: RawLog) -> Result { - match service::events::document_key_common_retrieval_requested::parse_log(raw_log) { - Ok(l) => Ok(ServiceTask::RetrieveShadowDocumentKeyCommon(origin.clone(), l.server_key_id, l.requester)), - Err(e) => Err(e.to_string()) - } - } - - /// Parse personal request log entry. - pub fn parse_personal_request_log(origin: &Address, raw_log: RawLog) -> Result { - match service::events::document_key_personal_retrieval_requested::parse_log(raw_log) { - Ok(l) => Ok(ServiceTask::RetrieveShadowDocumentKeyPersonal(origin.clone(), l.server_key_id, H512::from_slice(&*l.requester_public))), - Err(e) => Err(e.to_string()) - } - } - - /// Check if response from key server is required. - pub fn is_response_required(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, requester: &Address, key_server: &Address) -> bool { - // we're checking confirmation in Latest block, because we're interested in latest contract state here - let (encoded, decoder) = service::functions::is_document_key_shadow_retrieval_response_required::call(*server_key_id, *requester, *key_server); - match client.call_contract(BlockId::Latest, *contract_address, encoded) { - Err(_) => true, - Ok(data) => decoder.decode(&data).unwrap_or(true) - } - } - - /// Prepare publish common key transaction data. - pub fn prepare_pubish_common_tx_data(server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: U256) -> Bytes { - service::functions::document_key_common_retrieved::encode_input(*server_key_id, *requester, common_point.as_bytes().to_vec(), threshold) - } - - /// Prepare publish personal key transaction data. - pub fn prepare_pubish_personal_tx_data(client: &dyn SecretStoreChain, contract_address: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result { - let mut participants_mask = U256::default(); - for participant in participants { - let participant_index = Self::map_key_server_address(client, contract_address, participant.clone()) - .map_err(|e| format!("Error searching for {} participant: {}", participant, e))?; - participants_mask = participants_mask | (U256::one() << participant_index); - } - Ok(service::functions::document_key_personal_retrieved::encode_input( - *server_key_id, *requester, participants_mask, decrypted_secret.as_bytes().to_vec(), shadow - )) - } - - /// Prepare error transaction data. - pub fn prepare_error_tx_data(server_key_id: &ServerKeyId, requester: &Address) -> Bytes { - service::functions::document_key_shadow_retrieval_error::encode_input(*server_key_id, *requester) - } - - /// Read pending requests count. - fn read_pending_requests_count(client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId) -> Result { - let (encoded, decoder) = service::functions::document_key_shadow_retrieval_requests_count::call(); - decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string()) - } - - /// Read pending request. - fn read_pending_request(self_key_pair: &dyn SigningKeyPair, client: &dyn SecretStoreChain, contract_address: &Address, block: &BlockId, index: U256) -> Result<(bool, ServiceTask), String> { - let self_address = public_to_address(self_key_pair.public()); - - let (encoded, decoder) = service::functions::get_document_key_shadow_retrieval_request::call(index); - let (server_key_id, requester, is_common_retrieval_completed) = - decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let requester = Public::from_slice(&requester); - let (encoded, decoder) = service::functions::is_document_key_shadow_retrieval_response_required::call(server_key_id, public_to_address(&requester), self_address); - let not_confirmed = decoder.decode(&client.call_contract(*block, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - let task = match is_common_retrieval_completed { - true => ServiceTask::RetrieveShadowDocumentKeyPersonal( - *contract_address, - server_key_id, - requester, - ), - false => ServiceTask::RetrieveShadowDocumentKeyCommon( - *contract_address, - server_key_id, - public_to_address(&requester), - ), - }; - - Ok((not_confirmed, task)) - } - - /// Map from key server address to key server index. - fn map_key_server_address(client: &dyn SecretStoreChain, contract_address: &Address, key_server: Address) -> Result { - // we're checking confirmation in Latest block, because tx ,ust be appended to the latest state - let (encoded, decoder) = service::functions::require_key_server::call(key_server); - let index = decoder.decode(&client.call_contract(BlockId::Latest, *contract_address, encoded)?) - .map_err(|e| e.to_string())?; - - if index > u8::max_value().into() { - Err(format!("key server index is too big: {}", index)) - } else { - let index: u32 = index.low_u32(); - Ok(index as u8) - } - } -} - -/// Parse threshold (we only supposrt 256 KS at max). -fn parse_threshold(threshold: U256) -> Result { - let threshold_num = threshold.low_u64(); - if threshold != threshold_num.into() || threshold_num >= ::std::u8::MAX as u64 { - return Err(format!("invalid threshold to use in service contract: {}", threshold)); - } - - Ok(threshold_num as usize) -} - -/// Serialize threshold (we only support 256 KS at max). -fn serialize_threshold(threshold: usize) -> Result { - if threshold > ::std::u8::MAX as usize { - return Err(format!("invalid threshold to use in service contract: {}", threshold)); - } - Ok(threshold.into()) -} - -#[cfg(test)] -pub mod tests { - use parking_lot::Mutex; - use bytes::Bytes; - use crypto::publickey::Public; - use ethereum_types::Address; - use listener::service_contract_listener::ServiceTask; - use {ServerKeyId}; - use super::ServiceContract; - - #[derive(Default)] - pub struct DummyServiceContract { - pub is_actual: bool, - pub logs: Vec, - pub pending_requests: Vec<(bool, ServiceTask)>, - pub generated_server_keys: Mutex>, - pub server_keys_generation_failures: Mutex>, - pub retrieved_server_keys: Mutex>, - pub server_keys_retrieval_failures: Mutex>, - pub stored_document_keys: Mutex>, - pub document_keys_store_failures: Mutex>, - pub common_shadow_retrieved_document_keys: Mutex>, - pub personal_shadow_retrieved_document_keys: Mutex, Public, Bytes)>>, - pub document_keys_shadow_retrieval_failures: Mutex>, - } - - impl ServiceContract for DummyServiceContract { - fn update(&self) -> bool { - true - } - - fn read_logs(&self) -> Box> { - Box::new(self.logs.clone().into_iter()) - } - - fn read_pending_requests(&self) -> Box> { - Box::new(self.pending_requests.clone().into_iter()) - } - - fn publish_generated_server_key(&self, _origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String> { - self.generated_server_keys.lock().push((server_key_id.clone(), server_key.clone())); - Ok(()) - } - - fn publish_server_key_generation_error(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.server_keys_generation_failures.lock().push(server_key_id.clone()); - Ok(()) - } - - fn publish_retrieved_server_key(&self, _origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String> { - self.retrieved_server_keys.lock().push((server_key_id.clone(), server_key.clone(), threshold)); - Ok(()) - } - - fn publish_server_key_retrieval_error(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.server_keys_retrieval_failures.lock().push(server_key_id.clone()); - Ok(()) - } - - fn publish_stored_document_key(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.stored_document_keys.lock().push(server_key_id.clone()); - Ok(()) - } - - fn publish_document_key_store_error(&self, _origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.document_keys_store_failures.lock().push(server_key_id.clone()); - Ok(()) - } - - fn publish_retrieved_document_key_common(&self, _origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String> { - self.common_shadow_retrieved_document_keys.lock().push((server_key_id.clone(), requester.clone(), common_point.clone(), threshold)); - Ok(()) - } - - fn publish_retrieved_document_key_personal(&self, _origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String> { - self.personal_shadow_retrieved_document_keys.lock().push((server_key_id.clone(), requester.clone(), participants.iter().cloned().collect(), decrypted_secret, shadow)); - Ok(()) - } - - fn publish_document_key_retrieval_error(&self, _origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> { - self.document_keys_shadow_retrieval_failures.lock().push((server_key_id.clone(), requester.clone())); - Ok(()) - } - } -} diff --git a/secret-store/src/listener/service_contract_aggregate.rs b/secret-store/src/listener/service_contract_aggregate.rs deleted file mode 100644 index 9b2fb0e54b2..00000000000 --- a/secret-store/src/listener/service_contract_aggregate.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::sync::Arc; -use bytes::Bytes; -use ethereum_types::Address; -use crypto::publickey::Public; -use listener::service_contract::ServiceContract; -use listener::service_contract_listener::ServiceTask; -use {ServerKeyId}; - -/// Aggregated on-chain service contract. -pub struct OnChainServiceContractAggregate { - /// All hosted service contracts. - contracts: Vec>, -} - -impl OnChainServiceContractAggregate { - /// Create new aggregated service contract listener. - pub fn new(contracts: Vec>) -> Self { - debug_assert!(contracts.len() > 1); - OnChainServiceContractAggregate { - contracts: contracts, - } - } -} - -impl ServiceContract for OnChainServiceContractAggregate { - fn update(&self) -> bool { - let mut result = false; - for contract in &self.contracts { - result = contract.update() || result; - } - result - } - - fn read_logs(&self) -> Box> { - self.contracts.iter() - .fold(Box::new(::std::iter::empty()) as Box>, |i, c| - Box::new(i.chain(c.read_logs()))) - } - - fn read_pending_requests(&self) -> Box> { - self.contracts.iter() - .fold(Box::new(::std::iter::empty()) as Box>, |i, c| - Box::new(i.chain(c.read_pending_requests()))) - } - - // in current implementation all publish methods are independent of actual contract adddress - // (tx is sent to origin) => we do not care which contract to use for publish data in methods below - - fn publish_generated_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public) -> Result<(), String> { - self.contracts[0].publish_generated_server_key(origin, server_key_id, server_key) - } - - fn publish_server_key_generation_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.contracts[0].publish_server_key_generation_error(origin, server_key_id) - } - - fn publish_retrieved_server_key(&self, origin: &Address, server_key_id: &ServerKeyId, server_key: Public, threshold: usize) -> Result<(), String> { - self.contracts[0].publish_retrieved_server_key(origin, server_key_id, server_key, threshold) - } - - fn publish_server_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.contracts[0].publish_server_key_retrieval_error(origin, server_key_id) - } - - fn publish_stored_document_key(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.contracts[0].publish_stored_document_key(origin, server_key_id) - } - - fn publish_document_key_store_error(&self, origin: &Address, server_key_id: &ServerKeyId) -> Result<(), String> { - self.contracts[0].publish_document_key_store_error(origin, server_key_id) - } - - fn publish_retrieved_document_key_common(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, common_point: Public, threshold: usize) -> Result<(), String> { - self.contracts[0].publish_retrieved_document_key_common(origin, server_key_id, requester, common_point, threshold) - } - - fn publish_retrieved_document_key_personal(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address, participants: &[Address], decrypted_secret: Public, shadow: Bytes) -> Result<(), String> { - self.contracts[0].publish_retrieved_document_key_personal(origin, server_key_id, requester, participants, decrypted_secret, shadow) - } - - fn publish_document_key_retrieval_error(&self, origin: &Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> { - self.contracts[0].publish_document_key_retrieval_error(origin, server_key_id, requester) - } -} diff --git a/secret-store/src/listener/service_contract_listener.rs b/secret-store/src/listener/service_contract_listener.rs deleted file mode 100644 index 8e1ffa66d16..00000000000 --- a/secret-store/src/listener/service_contract_listener.rs +++ /dev/null @@ -1,1054 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::HashSet; -use std::sync::Arc; -use std::sync::atomic::{AtomicUsize, Ordering}; -use std::thread; -use bytes::Bytes; -use crypto::publickey::{Public, public_to_address}; -use ethereum_types::{H256, U256, Address, BigEndianHash as _}; -use key_server_set::KeyServerSet; -use key_server_cluster::{NodeId, ClusterClient, ClusterSessionsListener, ClusterSession}; -use key_server_cluster::math; -use key_server_cluster::generation_session::SessionImpl as GenerationSession; -use key_server_cluster::encryption_session::{check_encrypted_data, update_encrypted_data}; -use key_server_cluster::decryption_session::SessionImpl as DecryptionSession; -use key_server_cluster::key_version_negotiation_session::{SessionImpl as KeyVersionNegotiationSession, - IsolatedSessionTransport as KeyVersionNegotiationTransport, FailedContinueAction}; -use key_storage::KeyStorage; -use parking_lot::Mutex; -use acl_storage::AclStorage; -use listener::service_contract::ServiceContract; -use listener::tasks_queue::TasksQueue; -use {ServerKeyId, Error}; -use blockchain::{NewBlocksNotify, SigningKeyPair}; - -/// Retry interval (in blocks). Every RETRY_INTERVAL_BLOCKS blocks each KeyServer reads pending requests from -/// service contract && tries to re-execute. The reason to have this mechanism is primarily because keys -/// servers set change takes a lot of time + there could be some races, when blocks are coming to different -/// KS at different times. This isn't intended to fix && respond to general session errors! -const RETRY_INTERVAL_BLOCKS: usize = 30; - -/// Max failed retry requests (in single retry interval). The reason behind this constant is that if several -/// pending requests have failed, then most probably other will fail too. -const MAX_FAILED_RETRY_REQUESTS: usize = 1; - -/// SecretStore <-> Authority connector responsible for: -/// 1. listening for new requests on SecretStore contract -/// 2. redirecting requests to key server -/// 3. publishing response on SecretStore contract -pub struct ServiceContractListener { - /// Service contract listener data. - data: Arc, - /// Service thread handle. - service_handle: Option>, -} - -/// Service contract listener parameters. -pub struct ServiceContractListenerParams { - /// Service contract. - pub contract: Arc, - /// This node key pair. - pub self_key_pair: Arc, - /// Key servers set. - pub key_server_set: Arc, - /// ACL storage reference. - pub acl_storage: Arc, - /// Cluster reference. - pub cluster: Arc, - /// Key storage reference. - pub key_storage: Arc, -} - -/// Service contract listener data. -struct ServiceContractListenerData { - /// Blocks since last retry. - pub last_retry: AtomicUsize, - /// Retry-related data. - pub retry_data: Mutex, - /// Service tasks queue. - pub tasks_queue: Arc>, - /// Service contract. - pub contract: Arc, - /// ACL storage reference. - pub acl_storage: Arc, - /// Cluster client reference. - pub cluster: Arc, - /// This node key pair. - pub self_key_pair: Arc, - /// Key servers set. - pub key_server_set: Arc, - /// Key storage reference. - pub key_storage: Arc, - -} - -/// Retry-related data. -#[derive(Default)] -struct ServiceContractRetryData { - /// Server keys, which we have 'touched' since last retry. - pub affected_server_keys: HashSet, - /// Document keys + requesters, which we have 'touched' since last retry. - pub affected_document_keys: HashSet<(ServerKeyId, Address)>, -} - -/// Service task. -#[derive(Debug, Clone, PartialEq)] -pub enum ServiceTask { - /// Retry all 'stalled' tasks. - Retry, - /// Generate server key (origin, server_key_id, author, threshold). - GenerateServerKey(Address, ServerKeyId, Address, usize), - /// Retrieve server key (origin, server_key_id). - RetrieveServerKey(Address, ServerKeyId), - /// Store document key (origin, server_key_id, author, common_point, encrypted_point). - StoreDocumentKey(Address, ServerKeyId, Address, Public, Public), - /// Retrieve common data of document key (origin, server_key_id, requester). - RetrieveShadowDocumentKeyCommon(Address, ServerKeyId, Address), - /// Retrieve personal data of document key (origin, server_key_id, requester). - RetrieveShadowDocumentKeyPersonal(Address, ServerKeyId, Public), - /// Shutdown listener. - Shutdown, -} - -impl ServiceContractListener { - /// Create new service contract listener. - pub fn new(params: ServiceContractListenerParams) -> Result, Error> { - let data = Arc::new(ServiceContractListenerData { - last_retry: AtomicUsize::new(0), - retry_data: Default::default(), - tasks_queue: Arc::new(TasksQueue::new()), - contract: params.contract, - acl_storage: params.acl_storage, - cluster: params.cluster, - self_key_pair: params.self_key_pair, - key_server_set: params.key_server_set, - key_storage: params.key_storage, - }); - - // we are not starting thread when in test mode - let service_handle = if cfg!(test) { - None - } else { - let service_thread_data = data.clone(); - Some(thread::Builder::new().name("ServiceContractListener".into()).spawn(move || - Self::run_service_thread(service_thread_data)).map_err(|e| Error::Internal(format!("{}", e)))?) - }; - let contract = Arc::new(ServiceContractListener { - data: data, - service_handle: service_handle, - }); - contract.data.cluster.add_generation_listener(contract.clone()); - contract.data.cluster.add_decryption_listener(contract.clone()); - contract.data.cluster.add_key_version_negotiation_listener(contract.clone()); - Ok(contract) - } - - /// Process incoming events of service contract. - fn process_service_contract_events(&self) { - // shortcut: do not process events if we're isolated from the cluster - if self.data.key_server_set.is_isolated() { - return; - } - - self.data.tasks_queue.push_many(self.data.contract.read_logs() - .filter_map(|task| Self::filter_task(&self.data, task))); - } - - /// Filter service task. Only returns Some if task must be executed by this server. - fn filter_task(data: &Arc, task: ServiceTask) -> Option { - match task { - // when this node should be master of this server key generation session - ServiceTask::GenerateServerKey(origin, server_key_id, author, threshold) if is_processed_by_this_key_server( - &*data.key_server_set, data.self_key_pair.public(), &server_key_id) => - Some(ServiceTask::GenerateServerKey(origin, server_key_id, author, threshold)), - // when server key is not yet generated and generation must be initiated by other node - ServiceTask::GenerateServerKey(_, _, _, _) => None, - - // when server key retrieval is requested - ServiceTask::RetrieveServerKey(origin, server_key_id) => - Some(ServiceTask::RetrieveServerKey(origin, server_key_id)), - - // when document key store is requested - ServiceTask::StoreDocumentKey(origin, server_key_id, author, common_point, encrypted_point) => - Some(ServiceTask::StoreDocumentKey(origin, server_key_id, author, common_point, encrypted_point)), - - // when common document key data retrieval is requested - ServiceTask::RetrieveShadowDocumentKeyCommon(origin, server_key_id, requester) => - Some(ServiceTask::RetrieveShadowDocumentKeyCommon(origin, server_key_id, requester)), - - // when this node should be master of this document key decryption session - ServiceTask::RetrieveShadowDocumentKeyPersonal(origin, server_key_id, requester) if is_processed_by_this_key_server( - &*data.key_server_set, data.self_key_pair.public(), &server_key_id) => - Some(ServiceTask::RetrieveShadowDocumentKeyPersonal(origin, server_key_id, requester)), - // when server key is not yet generated and generation must be initiated by other node - ServiceTask::RetrieveShadowDocumentKeyPersonal(_, _, _) => None, - - ServiceTask::Retry | ServiceTask::Shutdown => unreachable!("must be filtered outside"), - } - } - - /// Service thread procedure. - fn run_service_thread(data: Arc) { - loop { - let task = data.tasks_queue.wait(); - trace!(target: "secretstore", "{}: processing {:?} task", data.self_key_pair.public(), task); - - match task { - ServiceTask::Shutdown => break, - task => { - // the only possible reaction to an error is a tx+trace && it is already happened - let _ = Self::process_service_task(&data, task); - }, - }; - } - - trace!(target: "secretstore", "{}: ServiceContractListener thread stopped", data.self_key_pair.public()); - } - - /// Process single service task. - fn process_service_task(data: &Arc, task: ServiceTask) -> Result<(), String> { - match &task { - &ServiceTask::GenerateServerKey(origin, server_key_id, author, threshold) => { - data.retry_data.lock().affected_server_keys.insert(server_key_id.clone()); - log_service_task_result(&task, data.self_key_pair.public(), - Self::generate_server_key(&data, origin, &server_key_id, author, threshold)) - }, - &ServiceTask::RetrieveServerKey(origin, server_key_id) => { - data.retry_data.lock().affected_server_keys.insert(server_key_id.clone()); - log_service_task_result(&task, data.self_key_pair.public(), - Self::retrieve_server_key(&data, origin, &server_key_id)) - }, - &ServiceTask::StoreDocumentKey(origin, server_key_id, author, common_point, encrypted_point) => { - data.retry_data.lock().affected_document_keys.insert((server_key_id.clone(), author.clone())); - log_service_task_result(&task, data.self_key_pair.public(), - Self::store_document_key(&data, origin, &server_key_id, &author, &common_point, &encrypted_point)) - }, - &ServiceTask::RetrieveShadowDocumentKeyCommon(origin, server_key_id, requester) => { - data.retry_data.lock().affected_document_keys.insert((server_key_id.clone(), requester.clone())); - log_service_task_result(&task, data.self_key_pair.public(), - Self::retrieve_document_key_common(&data, origin, &server_key_id, &requester)) - }, - &ServiceTask::RetrieveShadowDocumentKeyPersonal(origin, server_key_id, requester) => { - data.retry_data.lock().affected_server_keys.insert(server_key_id.clone()); - log_service_task_result(&task, data.self_key_pair.public(), - Self::retrieve_document_key_personal(&data, origin, &server_key_id, requester)) - }, - &ServiceTask::Retry => { - Self::retry_pending_requests(&data) - .map(|processed_requests| { - if processed_requests != 0 { - trace!(target: "secretstore", "{}: successfully retried {} pending requests", - data.self_key_pair.public(), processed_requests); - } - () - }) - .map_err(|error| { - warn!(target: "secretstore", "{}: retrying pending requests has failed with: {}", - data.self_key_pair.public(), error); - error - }) - }, - &ServiceTask::Shutdown => unreachable!("must be filtered outside"), - } - } - - /// Retry processing pending requests. - fn retry_pending_requests(data: &Arc) -> Result { - let mut failed_requests = 0; - let mut processed_requests = 0; - let retry_data = ::std::mem::replace(&mut *data.retry_data.lock(), Default::default()); - let pending_tasks = data.contract.read_pending_requests() - .filter_map(|(is_confirmed, task)| Self::filter_task(data, task) - .map(|t| (is_confirmed, t))); - for (is_response_required, task) in pending_tasks { - // only process requests, which we haven't confirmed yet - if !is_response_required { - continue; - } - - match task { - ServiceTask::GenerateServerKey(_, ref key, _, _) | ServiceTask::RetrieveServerKey(_, ref key) - if retry_data.affected_server_keys.contains(key) => continue, - ServiceTask::StoreDocumentKey(_, ref key, ref author, _, _) | - ServiceTask::RetrieveShadowDocumentKeyCommon(_, ref key, ref author) - if retry_data.affected_document_keys.contains(&(key.clone(), author.clone())) => continue, - ServiceTask::RetrieveShadowDocumentKeyPersonal(_, ref key, ref requester) - if retry_data.affected_document_keys.contains(&(key.clone(), public_to_address(requester))) => continue, - _ => (), - } - - // process request result - let request_result = Self::process_service_task(data, task); - match request_result { - Ok(_) => processed_requests += 1, - Err(_) => { - failed_requests += 1; - if failed_requests > MAX_FAILED_RETRY_REQUESTS { - return Err("too many failed requests".into()); - } - }, - } - } - - Ok(processed_requests) - } - - /// Generate server key (start generation session). - fn generate_server_key(data: &Arc, origin: Address, server_key_id: &ServerKeyId, author: Address, threshold: usize) -> Result<(), String> { - Self::process_server_key_generation_result(data, origin, server_key_id, data.cluster.new_generation_session( - server_key_id.clone(), Some(origin), author, threshold).map(|_| None).map_err(Into::into)) - } - - /// Process server key generation result. - fn process_server_key_generation_result(data: &Arc, origin: Address, server_key_id: &ServerKeyId, result: Result, Error>) -> Result<(), String> { - match result { - Ok(None) => Ok(()), - Ok(Some(server_key)) => { - data.contract.publish_generated_server_key(&origin, server_key_id, server_key) - }, - Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), - Err(ref error) => { - // ignore error as we're already processing an error - let _ = data.contract.publish_server_key_generation_error(&origin, server_key_id) - .map_err(|error| warn!(target: "secretstore", "{}: failed to publish GenerateServerKey({}) error: {}", - data.self_key_pair.public(), server_key_id, error)); - Err(format!("{}", error)) - } - } - } - - /// Retrieve server key. - fn retrieve_server_key(data: &Arc, origin: Address, server_key_id: &ServerKeyId) -> Result<(), String> { - match data.key_storage.get(server_key_id) { - Ok(Some(server_key_share)) => { - data.contract.publish_retrieved_server_key(&origin, server_key_id, server_key_share.public, server_key_share.threshold) - }, - Ok(None) => { - data.contract.publish_server_key_retrieval_error(&origin, server_key_id) - } - Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), - Err(ref error) => { - // ignore error as we're already processing an error - let _ = data.contract.publish_server_key_retrieval_error(&origin, server_key_id) - .map_err(|error| warn!(target: "secretstore", "{}: failed to publish RetrieveServerKey({}) error: {}", - data.self_key_pair.public(), server_key_id, error)); - Err(format!("{}", error)) - } - } - } - - /// Store document key. - fn store_document_key(data: &Arc, origin: Address, server_key_id: &ServerKeyId, author: &Address, common_point: &Public, encrypted_point: &Public) -> Result<(), String> { - let store_result = data.key_storage.get(server_key_id) - .and_then(|key_share| key_share.ok_or(Error::ServerKeyIsNotFound)) - .and_then(|key_share| check_encrypted_data(Some(&key_share)).map(|_| key_share).map_err(Into::into)) - .and_then(|key_share| update_encrypted_data(&data.key_storage, server_key_id.clone(), key_share, - author.clone(), common_point.clone(), encrypted_point.clone()).map_err(Into::into)); - match store_result { - Ok(()) => { - data.contract.publish_stored_document_key(&origin, server_key_id) - }, - Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), - Err(ref error) => { - // ignore error as we're already processing an error - let _ = data.contract.publish_document_key_store_error(&origin, server_key_id) - .map_err(|error| warn!(target: "secretstore", "{}: failed to publish StoreDocumentKey({}) error: {}", - data.self_key_pair.public(), server_key_id, error)); - Err(format!("{}", error)) - }, - } - } - - /// Retrieve common part of document key. - fn retrieve_document_key_common(data: &Arc, origin: Address, server_key_id: &ServerKeyId, requester: &Address) -> Result<(), String> { - let retrieval_result = data.acl_storage.check(requester.clone(), server_key_id) - .and_then(|is_allowed| if !is_allowed { Err(Error::AccessDenied) } else { Ok(()) }) - .and_then(|_| data.key_storage.get(server_key_id).and_then(|key_share| key_share.ok_or(Error::ServerKeyIsNotFound))) - .and_then(|key_share| key_share.common_point - .ok_or(Error::DocumentKeyIsNotFound) - .and_then(|common_point| math::make_common_shadow_point(key_share.threshold, common_point)) - .map(|common_point| (common_point, key_share.threshold))); - match retrieval_result { - Ok((common_point, threshold)) => { - data.contract.publish_retrieved_document_key_common(&origin, server_key_id, requester, common_point, threshold) - }, - Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), - Err(ref error) => { - // ignore error as we're already processing an error - let _ = data.contract.publish_document_key_retrieval_error(&origin, server_key_id, requester) - .map_err(|error| warn!(target: "secretstore", "{}: failed to publish RetrieveDocumentKey({}) error: {}", - data.self_key_pair.public(), server_key_id, error)); - Err(format!("{}", error)) - }, - } - } - - /// Retrieve personal part of document key (start decryption session). - fn retrieve_document_key_personal(data: &Arc, origin: Address, server_key_id: &ServerKeyId, requester: Public) -> Result<(), String> { - Self::process_document_key_retrieval_result(data, origin, server_key_id, &public_to_address(&requester), data.cluster.new_decryption_session( - server_key_id.clone(), Some(origin), requester.clone().into(), None, true, true).map(|_| None).map_err(Into::into)) - } - - /// Process document key retrieval result. - fn process_document_key_retrieval_result(data: &Arc, origin: Address, server_key_id: &ServerKeyId, requester: &Address, result: Result, Public, Bytes)>, Error>) -> Result<(), String> { - match result { - Ok(None) => Ok(()), - Ok(Some((participants, decrypted_secret, shadow))) => { - data.contract.publish_retrieved_document_key_personal(&origin, server_key_id, &requester, &participants, decrypted_secret, shadow) - }, - Err(ref error) if error.is_non_fatal() => Err(format!("{}", error)), - Err(ref error) => { - // ignore error as we're already processing an error - let _ = data.contract.publish_document_key_retrieval_error(&origin, server_key_id, &requester) - .map_err(|error| warn!(target: "secretstore", "{}: failed to publish RetrieveDocumentKey({}) error: {}", - data.self_key_pair.public(), server_key_id, error)); - Err(format!("{}", error)) - } - } - } -} - -impl Drop for ServiceContractListener { - fn drop(&mut self) { - if let Some(service_handle) = self.service_handle.take() { - self.data.tasks_queue.push_front(ServiceTask::Shutdown); - // ignore error as we are already closing - let _ = service_handle.join(); - } - } -} - -impl NewBlocksNotify for ServiceContractListener { - fn new_blocks(&self, new_enacted_len: usize) { - if !self.data.contract.update() { - return; - } - - self.process_service_contract_events(); - - // schedule retry if received enough blocks since last retry - // it maybe inaccurate when switching syncing/synced states, but that's ok - if self.data.last_retry.fetch_add(new_enacted_len, Ordering::Relaxed) >= RETRY_INTERVAL_BLOCKS { - // shortcut: do not retry if we're isolated from the cluster - if !self.data.key_server_set.is_isolated() { - self.data.tasks_queue.push(ServiceTask::Retry); - self.data.last_retry.store(0, Ordering::Relaxed); - } - } - } -} - -impl ClusterSessionsListener for ServiceContractListener { - fn on_session_removed(&self, session: Arc) { - // by this time sesion must already be completed - either successfully, or not - assert!(session.is_finished()); - - // ignore result - the only thing that we can do is to log the error - let server_key_id = session.id(); - if let Some(origin) = session.origin() { - if let Some(generation_result) = session.result() { - let generation_result = generation_result.map(Some).map_err(Into::into); - let _ = Self::process_server_key_generation_result(&self.data, origin, &server_key_id, generation_result); - } - } - } -} - -impl ClusterSessionsListener for ServiceContractListener { - fn on_session_removed(&self, session: Arc) { - // by this time sesion must already be completed - either successfully, or not - assert!(session.is_finished()); - - // ignore result - the only thing that we can do is to log the error - let session_id = session.id(); - let server_key_id = session_id.id; - if let (Some(requester), Some(origin)) = (session.requester().and_then(|r| r.address(&server_key_id).ok()), session.origin()) { - if let Some(retrieval_result) = session.result() { - let retrieval_result = retrieval_result.map(|key_shadow| - session.broadcast_shadows() - .and_then(|broadcast_shadows| - broadcast_shadows.get(self.data.self_key_pair.public()) - .map(|self_shadow| ( - broadcast_shadows.keys().map(public_to_address).collect(), - key_shadow.decrypted_secret, - self_shadow.clone() - ))) - ).map_err(Into::into); - let _ = Self::process_document_key_retrieval_result(&self.data, origin, &server_key_id, &requester, retrieval_result); - } - } - } -} - -impl ClusterSessionsListener> for ServiceContractListener { - fn on_session_removed(&self, session: Arc>) { - // by this time sesion must already be completed - either successfully, or not - assert!(session.is_finished()); - - // we're interested in: - // 1) sessions failed with fatal error - // 2) with decryption continue action - let error = match session.result() { - Some(Err(ref error)) if !error.is_non_fatal() => error.clone(), - _ => return, - }; - - let (origin, requester) = match session.take_failed_continue_action() { - Some(FailedContinueAction::Decrypt(Some(origin), requester)) => (origin, requester), - _ => return, - }; - - // check if master node is responsible for processing key requests - let meta = session.meta(); - if !is_processed_by_this_key_server(&*self.data.key_server_set, &meta.master_node_id, &meta.id) { - return; - } - - // ignore result as we're already processing an error - let _ = Self::process_document_key_retrieval_result(&self.data, origin, &meta.id, &requester, Err(error)); - } -} - -impl ::std::fmt::Display for ServiceTask { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match *self { - ServiceTask::Retry => write!(f, "Retry"), - ServiceTask::GenerateServerKey(_, ref server_key_id, ref author, ref threshold) => - write!(f, "GenerateServerKey({}, {}, {})", server_key_id, author, threshold), - ServiceTask::RetrieveServerKey(_, ref server_key_id) => - write!(f, "RetrieveServerKey({})", server_key_id), - ServiceTask::StoreDocumentKey(_, ref server_key_id, ref author, _, _) => - write!(f, "StoreDocumentKey({}, {})", server_key_id, author), - ServiceTask::RetrieveShadowDocumentKeyCommon(_, ref server_key_id, ref requester) => - write!(f, "RetrieveShadowDocumentKeyCommon({}, {})", server_key_id, requester), - ServiceTask::RetrieveShadowDocumentKeyPersonal(_, ref server_key_id, ref requester) => - write!(f, "RetrieveShadowDocumentKeyPersonal({}, {})", server_key_id, public_to_address(requester)), - ServiceTask::Shutdown => write!(f, "Shutdown"), - } - } -} - -/// Log service task result. -fn log_service_task_result(task: &ServiceTask, self_id: &Public, result: Result<(), String>) -> Result<(), String> { - match result { - Ok(_) => trace!(target: "secretstore", "{}: processed {} request", self_id, task), - Err(ref error) => warn!(target: "secretstore", "{}: failed to process {} request with: {}", self_id, task, error), - } - - result -} - -/// Returns true when session, related to `server_key_id` must be started on `node`. -fn is_processed_by_this_key_server(key_server_set: &dyn KeyServerSet, node: &NodeId, server_key_id: &H256) -> bool { - let servers = key_server_set.snapshot().current_set; - let total_servers_count = servers.len(); - match total_servers_count { - 0 => return false, - 1 => return true, - _ => (), - } - - let this_server_index = match servers.keys().enumerate().find(|&(_, s)| s == node) { - Some((index, _)) => index, - None => return false, - }; - - let server_key_id_value: U256 = server_key_id.into_uint(); - let range_interval = U256::max_value() / total_servers_count; - let range_begin = (range_interval + 1) * this_server_index as u32; - let range_end = range_begin.saturating_add(range_interval); - - server_key_id_value >= range_begin && server_key_id_value <= range_end -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::sync::atomic::Ordering; - use crypto::publickey::{Random, Generator, KeyPair}; - use listener::service_contract::ServiceContract; - use listener::service_contract::tests::DummyServiceContract; - use key_server_cluster::DummyClusterClient; - use acl_storage::{AclStorage, DummyAclStorage}; - use key_storage::{KeyStorage, DocumentKeyShare}; - use key_storage::tests::DummyKeyStorage; - use key_server_set::KeyServerSet; - use key_server_set::tests::MapKeyServerSet; - use blockchain::SigningKeyPair; - use {PlainNodeKeyPair, ServerKeyId}; - use super::{ServiceTask, ServiceContractListener, ServiceContractListenerParams, is_processed_by_this_key_server}; - use ethereum_types::Address; - - fn create_non_empty_key_storage(has_doc_key: bool) -> Arc { - let key_storage = Arc::new(DummyKeyStorage::default()); - let mut key_share = DocumentKeyShare::default(); - key_share.public = KeyPair::from_secret("0000000000000000000000000000000000000000000000000000000000000001" - .parse().unwrap()).unwrap().public().clone(); - if has_doc_key { - key_share.common_point = Some(Default::default()); - key_share.encrypted_point = Some(Default::default()); - } - key_storage.insert(Default::default(), key_share.clone()).unwrap(); - key_storage - } - - fn make_servers_set(is_isolated: bool) -> Arc { - Arc::new(MapKeyServerSet::new(is_isolated, vec![ - ("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - ("c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - ("f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - ].into_iter().collect())) - } - - fn make_service_contract_listener(contract: Option>, cluster: Option>, key_storage: Option>, acl_storage: Option>, servers_set: Option>) -> Arc { - let contract = contract.unwrap_or_else(|| Arc::new(DummyServiceContract::default())); - let cluster = cluster.unwrap_or_else(|| Arc::new(DummyClusterClient::default())); - let key_storage = key_storage.unwrap_or_else(|| Arc::new(DummyKeyStorage::default())); - let acl_storage = acl_storage.unwrap_or_else(|| Arc::new(DummyAclStorage::default())); - let servers_set = servers_set.unwrap_or_else(|| make_servers_set(false)); - let self_key_pair = Arc::new(PlainNodeKeyPair::new(KeyPair::from_secret("0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap()).unwrap())); - ServiceContractListener::new(ServiceContractListenerParams { - contract: contract, - self_key_pair: self_key_pair, - key_server_set: servers_set, - acl_storage: acl_storage, - cluster: cluster, - key_storage: key_storage, - }).unwrap() - } - - #[test] - fn is_not_processed_by_this_key_server_with_zero_servers() { - assert_eq!(is_processed_by_this_key_server( - &MapKeyServerSet::default(), - Random.generate().unwrap().public(), - &Default::default()), false); - } - - #[test] - fn is_processed_by_this_key_server_with_single_server() { - let self_key_pair = Random.generate().unwrap(); - assert_eq!(is_processed_by_this_key_server( - &MapKeyServerSet::new(false, vec![ - (self_key_pair.public().clone(), "127.0.0.1:8080".parse().unwrap()) - ].into_iter().collect()), - self_key_pair.public(), - &Default::default()), true); - } - - #[test] - fn is_not_processed_by_this_key_server_when_not_a_part_of_servers_set() { - assert!(is_processed_by_this_key_server( - &MapKeyServerSet::new(false, vec![ - (Random.generate().unwrap().public().clone(), "127.0.0.1:8080".parse().unwrap()) - ].into_iter().collect()), - Random.generate().unwrap().public(), - &Default::default())); - } - - #[test] - fn is_processed_by_this_key_server_in_set_of_3() { - // servers set is ordered && server range depends on index of this server - let servers_set = MapKeyServerSet::new(false, vec![ - // secret: 0000000000000000000000000000000000000000000000000000000000000001 - ("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - // secret: 0000000000000000000000000000000000000000000000000000000000000002 - ("c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - // secret: 0000000000000000000000000000000000000000000000000000000000000003 - ("f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - ].into_iter().collect()); - - // 1st server: process hashes [0x0; 0x555...555] - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"0000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"3000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"5555555555555555555555555555555555555555555555555555555555555555".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"5555555555555555555555555555555555555555555555555555555555555556".parse().unwrap()), false); - - // 2nd server: process hashes from 0x555...556 to 0xaaa...aab - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000002".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"5555555555555555555555555555555555555555555555555555555555555555".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"5555555555555555555555555555555555555555555555555555555555555556".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"7555555555555555555555555555555555555555555555555555555555555555".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac".parse().unwrap()), false); - - // 3rd server: process hashes from 0x800...000 to 0xbff...ff - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000003".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); - } - - #[test] - fn is_processed_by_this_key_server_in_set_of_4() { - // servers set is ordered && server range depends on index of this server - let servers_set = MapKeyServerSet::new(false, vec![ - // secret: 0000000000000000000000000000000000000000000000000000000000000001 - ("79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - // secret: 0000000000000000000000000000000000000000000000000000000000000002 - ("c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - // secret: 0000000000000000000000000000000000000000000000000000000000000004 - ("e493dbf1c10d80f3581e4904930b1404cc6c13900ee0758474fa94abe8c4cd1351ed993ea0d455b75642e2098ea51448d967ae33bfbdfe40cfe97bdc47739922".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - // secret: 0000000000000000000000000000000000000000000000000000000000000003 - ("f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9388f7b0f632de8140fe337e62a37f3566500a99934c2231b6cb9fd7584b8e672".parse().unwrap(), - "127.0.0.1:8080".parse().unwrap()), - ].into_iter().collect()); - - // 1st server: process hashes [0x0; 0x3ff...ff] - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"0000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"2000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"4000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), false); - - // 2nd server: process hashes from 0x400...000 to 0x7ff...ff - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000002".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"3fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"4000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"6000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"8000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), false); - - // 3rd server: process hashes from 0x800...000 to 0xbff...ff - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000004".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"8000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"a000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"bfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"c000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), false); - - // 4th server: process hashes from 0xc00...000 to 0xfff...ff - let key_pair = PlainNodeKeyPair::new(KeyPair::from_secret( - "0000000000000000000000000000000000000000000000000000000000000003".parse().unwrap()).unwrap()); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"bfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), false); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"c000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"e000000000000000000000000000000000000000000000000000000000000000".parse().unwrap()), true); - assert_eq!(is_processed_by_this_key_server(&servers_set, key_pair.public(), - &"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap()), true); - } - - #[test] - fn no_tasks_scheduled_when_no_contract_events() { - let listener = make_service_contract_listener(None, None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - } - - #[test] - fn tasks_are_not_scheduled_on_isolated_node() { - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::GenerateServerKey(Default::default(), Default::default(), Default::default(), 0)); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, Some(make_servers_set(true))); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - } - - // server key generation tests - - #[test] - fn server_key_generation_is_scheduled_when_requested() { - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::GenerateServerKey(Default::default(), Default::default(), Default::default(), 0)); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::GenerateServerKey( - Default::default(), Default::default(), Default::default(), 0))); - } - - #[test] - fn no_new_tasks_scheduled_when_server_key_generation_requested_and_request_belongs_to_other_key_server() { - let server_key_id = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap(); - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::GenerateServerKey(Default::default(), server_key_id, Default::default(), 0)); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - } - - #[test] - fn generation_session_is_created_when_processing_generate_server_key_task() { - let cluster = Arc::new(DummyClusterClient::default()); - let listener = make_service_contract_listener(None, Some(cluster.clone()), None, None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::GenerateServerKey( - Default::default(), Default::default(), Default::default(), Default::default())).unwrap_err(); - assert_eq!(cluster.generation_requests_count.load(Ordering::Relaxed), 1); - } - - #[test] - fn server_key_generation_is_not_retried_if_tried_in_the_same_cycle() { - let mut contract = DummyServiceContract::default(); - contract.pending_requests.push((false, ServiceTask::GenerateServerKey(Default::default(), - Default::default(), Default::default(), Default::default()))); - let cluster = Arc::new(DummyClusterClient::default()); - let listener = make_service_contract_listener(Some(Arc::new(contract)), Some(cluster.clone()), None, None, None); - listener.data.retry_data.lock().affected_server_keys.insert(Default::default()); - ServiceContractListener::retry_pending_requests(&listener.data).unwrap(); - assert_eq!(cluster.generation_requests_count.load(Ordering::Relaxed), 0); - } - - // server key retrieval tests - - #[test] - fn server_key_retrieval_is_scheduled_when_requested() { - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::RetrieveServerKey(Default::default(), Default::default())); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::RetrieveServerKey( - Default::default(), Default::default()))); - } - - #[test] - fn server_key_retrieval_is_scheduled_when_requested_and_request_belongs_to_other_key_server() { - let server_key_id: ServerKeyId = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap(); - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::RetrieveServerKey(Default::default(), server_key_id.clone())); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::RetrieveServerKey( - Default::default(), server_key_id))); - } - - #[test] - fn server_key_is_retrieved_when_processing_retrieve_server_key_task() { - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(false); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage), None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveServerKey( - Default::default(), Default::default())).unwrap(); - assert_eq!(*contract.retrieved_server_keys.lock(), vec![(Default::default(), - KeyPair::from_secret("0000000000000000000000000000000000000000000000000000000000000001".parse().unwrap()).unwrap().public().clone(), 0)]); - } - - #[test] - fn server_key_retrieval_failure_is_reported_when_processing_retrieve_server_key_task_and_key_is_unknown() { - let contract = Arc::new(DummyServiceContract::default()); - let listener = make_service_contract_listener(Some(contract.clone()), None, None, None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveServerKey( - Default::default(), Default::default())).unwrap(); - assert_eq!(*contract.server_keys_retrieval_failures.lock(), vec![Default::default()]); - } - - #[test] - fn server_key_retrieval_is_not_retried_if_tried_in_the_same_cycle() { - let mut contract = DummyServiceContract::default(); - contract.pending_requests.push((false, ServiceTask::RetrieveServerKey(Default::default(), Default::default()))); - let cluster = Arc::new(DummyClusterClient::default()); - let listener = make_service_contract_listener(Some(Arc::new(contract)), Some(cluster.clone()), None, None, None); - listener.data.retry_data.lock().affected_server_keys.insert(Default::default()); - ServiceContractListener::retry_pending_requests(&listener.data).unwrap(); - assert_eq!(cluster.generation_requests_count.load(Ordering::Relaxed), 0); - } - - // document key store tests - - #[test] - fn document_key_store_is_scheduled_when_requested() { - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::StoreDocumentKey(Default::default(), Default::default(), - Default::default(), Default::default(), Default::default())); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::StoreDocumentKey( - Default::default(), Default::default(), Default::default(), Default::default(), Default::default()))); - } - - #[test] - fn document_key_store_is_scheduled_when_requested_and_request_belongs_to_other_key_server() { - let server_key_id: ServerKeyId = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap(); - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::StoreDocumentKey(Default::default(), server_key_id.clone(), - Default::default(), Default::default(), Default::default())); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::StoreDocumentKey( - Default::default(), server_key_id, Default::default(), Default::default(), Default::default()))); - } - - #[test] - fn document_key_is_stored_when_processing_store_document_key_task() { - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(false); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage.clone()), None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::StoreDocumentKey( - Default::default(), Default::default(), Default::default(), Default::default(), Default::default())).unwrap(); - assert_eq!(*contract.stored_document_keys.lock(), vec![Default::default()]); - - let key_share = key_storage.get(&Default::default()).unwrap().unwrap(); - assert_eq!(key_share.common_point, Some(Default::default())); - assert_eq!(key_share.encrypted_point, Some(Default::default())); - } - - #[test] - fn document_key_store_failure_reported_when_no_server_key() { - let contract = Arc::new(DummyServiceContract::default()); - let listener = make_service_contract_listener(Some(contract.clone()), None, None, None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::StoreDocumentKey( - Default::default(), Default::default(), Default::default(), Default::default(), Default::default())).unwrap_err(); - assert_eq!(*contract.document_keys_store_failures.lock(), vec![Default::default()]); - } - - #[test] - fn document_key_store_failure_reported_when_document_key_already_set() { - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(true); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage), None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::StoreDocumentKey( - Default::default(), Default::default(), Default::default(), Default::default(), Default::default())).unwrap_err(); - assert_eq!(*contract.document_keys_store_failures.lock(), vec![Default::default()]); - } - - #[test] - fn document_key_store_failure_reported_when_author_differs() { - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(false); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage), None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::StoreDocumentKey( - Default::default(), Default::default(), Address::from_low_u64_be(1), Default::default(), Default::default())).unwrap_err(); - assert_eq!(*contract.document_keys_store_failures.lock(), vec![Default::default()]); - } - - // document key shadow common retrieval tests - - #[test] - fn document_key_shadow_common_retrieval_is_scheduled_when_requested() { - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::RetrieveShadowDocumentKeyCommon(Default::default(), Default::default(), Default::default())); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::RetrieveShadowDocumentKeyCommon( - Default::default(), Default::default(), Default::default()))); - } - - #[test] - fn document_key_shadow_common_retrieval_is_scheduled_when_requested_and_request_belongs_to_other_key_server() { - let server_key_id: ServerKeyId = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff".parse().unwrap(); - let mut contract = DummyServiceContract::default(); - contract.logs.push(ServiceTask::RetrieveShadowDocumentKeyCommon(Default::default(), server_key_id.clone(), Default::default())); - let listener = make_service_contract_listener(Some(Arc::new(contract)), None, None, None, None); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 0); - listener.process_service_contract_events(); - assert_eq!(listener.data.tasks_queue.snapshot().len(), 1); - assert_eq!(listener.data.tasks_queue.snapshot().pop_back(), Some(ServiceTask::RetrieveShadowDocumentKeyCommon( - Default::default(), server_key_id, Default::default()))); - } - - #[test] - fn document_key_shadow_common_is_retrieved_when_processing_document_key_shadow_common_retrieval_task() { - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(true); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage.clone()), None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveShadowDocumentKeyCommon( - Default::default(), Default::default(), Default::default())).unwrap(); - assert_eq!(*contract.common_shadow_retrieved_document_keys.lock(), vec![(Default::default(), Default::default(), - Default::default(), 0)]); - } - - #[test] - fn document_key_shadow_common_retrieval_failure_reported_when_access_denied() { - let acl_storage = DummyAclStorage::default(); - acl_storage.prohibit(Default::default(), Default::default()); - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(true); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage.clone()), Some(Arc::new(acl_storage)), None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveShadowDocumentKeyCommon( - Default::default(), Default::default(), Default::default())).unwrap_err(); - assert_eq!(*contract.document_keys_shadow_retrieval_failures.lock(), vec![(Default::default(), Default::default())]); - } - - #[test] - fn document_key_shadow_common_retrieval_failure_reported_when_no_server_key() { - let contract = Arc::new(DummyServiceContract::default()); - let listener = make_service_contract_listener(Some(contract.clone()), None, None, None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveShadowDocumentKeyCommon( - Default::default(), Default::default(), Default::default())).unwrap_err(); - assert_eq!(*contract.document_keys_shadow_retrieval_failures.lock(), vec![(Default::default(), Default::default())]); - } - - #[test] - fn document_key_shadow_common_retrieval_failure_reported_when_no_document_key() { - let contract = Arc::new(DummyServiceContract::default()); - let key_storage = create_non_empty_key_storage(false); - let listener = make_service_contract_listener(Some(contract.clone()), None, Some(key_storage.clone()), None, None); - ServiceContractListener::process_service_task(&listener.data, ServiceTask::RetrieveShadowDocumentKeyCommon( - Default::default(), Default::default(), Default::default())).unwrap_err(); - assert_eq!(*contract.document_keys_shadow_retrieval_failures.lock(), vec![(Default::default(), Default::default())]); - } -} diff --git a/secret-store/src/listener/tasks_queue.rs b/secret-store/src/listener/tasks_queue.rs deleted file mode 100644 index 08a4a316dea..00000000000 --- a/secret-store/src/listener/tasks_queue.rs +++ /dev/null @@ -1,78 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::VecDeque; -use parking_lot::{Mutex, Condvar}; - -#[derive(Default)] -/// General deque-based tasks queue. -pub struct TasksQueue { - /// Service event. - service_event: Condvar, - /// Service tasks queue. - service_tasks: Mutex>, -} - -impl TasksQueue where Task: Clone { - /// Create new tasks queue. - pub fn new() -> Self { - TasksQueue { - service_event: Condvar::new(), - service_tasks: Mutex::new(VecDeque::new()), - } - } - - #[cfg(test)] - /// Get current tasks snapshot. - pub fn snapshot(&self) -> VecDeque { - self.service_tasks.lock().clone() - } - - /// Push task to the front of queue. - pub fn push_front(&self, task: Task) { - let mut service_tasks = self.service_tasks.lock(); - service_tasks.push_front(task); - self.service_event.notify_all(); - } - - /// Push task to the back of queue. - pub fn push(&self, task: Task) { - let mut service_tasks = self.service_tasks.lock(); - service_tasks.push_back(task); - self.service_event.notify_all(); - } - - /// Push task to the back of queue. - pub fn push_many>(&self, tasks: I) { - let mut service_tasks = self.service_tasks.lock(); - let previous_len = service_tasks.len(); - service_tasks.extend(tasks); - if service_tasks.len() != previous_len { - self.service_event.notify_all(); - } - } - - /// Wait for new task (task is removed from the front of queue). - pub fn wait(&self) -> Task { - let mut service_tasks = self.service_tasks.lock(); - if service_tasks.is_empty() { - self.service_event.wait(&mut service_tasks); - } - - service_tasks.pop_front() - .expect("service_event is only fired when there are new tasks; qed") - } -} diff --git a/secret-store/src/migration.rs b/secret-store/src/migration.rs deleted file mode 100644 index 44ac5197651..00000000000 --- a/secret-store/src/migration.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Secret Store DB migration module. - - -use std::fmt::{Display, Error as FmtError, Formatter}; -use std::fs; -use std::io::{Error as IoError, ErrorKind as IoErrorKind, Read as _}; -use std::path::PathBuf; - -/// Current db version. -const CURRENT_VERSION: u8 = 4; -/// Database is assumed to be at the default version, when no version file is found. -const DEFAULT_VERSION: u8 = 3; -/// Version file name. -const VERSION_FILE_NAME: &str = "db_version"; - -/// Migration related errors. -#[derive(Debug)] -pub enum Error { - /// Returned when current version cannot be read or guessed. - UnknownDatabaseVersion, - /// Existing DB is newer than the known one. - FutureDBVersion, - /// Migration using parity-ethereum 2.6.7 is required. - MigrationWithLegacyVersionRequired, - /// Migration was completed successfully, - /// but there was a problem with io. - Io(IoError), -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { - let out = match *self { - Error::UnknownDatabaseVersion => - "Current Secret Store database version cannot be read".into(), - Error::FutureDBVersion => - "Secret Store database was created with newer client version.\ - Upgrade your client or delete DB and resync.".into(), - Error::MigrationWithLegacyVersionRequired => - "Secret Store database was created with an older client version.\ - To migrate, use parity-ethereum v2.6.7, then retry using the latest.".into(), - Error::Io(ref err) => - format!("Unexpected io error on Secret Store database migration: {}.", err), - }; - write!(f, "{}", out) - } -} - -impl From for Error { - fn from(err: IoError) -> Self { - Error::Io(err) - } -} - -/// Apply all migrations if possible. -pub fn upgrade_db(db_path: &str) -> Result<(), Error> { - match current_version(db_path)? { - old_version if old_version < CURRENT_VERSION => { - Err(Error::MigrationWithLegacyVersionRequired) - }, - CURRENT_VERSION => Ok(()), - _ => Err(Error::FutureDBVersion), - } -} - -/// Returns the version file path. -fn version_file_path(path: &str) -> PathBuf { - let mut file_path = PathBuf::from(path); - file_path.push(VERSION_FILE_NAME); - file_path -} - -/// Reads current database version from the file at given path. -/// If the file does not exist returns `DEFAULT_VERSION`. -fn current_version(path: &str) -> Result { - match fs::File::open(version_file_path(path)) { - Err(ref err) if err.kind() == IoErrorKind::NotFound => Ok(DEFAULT_VERSION), - Err(err) => Err(err.into()), - Ok(mut file) => { - let mut s = String::new(); - file.read_to_string(&mut s)?; - u8::from_str_radix(&s, 10).map_err(|_| Error::UnknownDatabaseVersion) - }, - } -} - diff --git a/secret-store/src/node_key_pair.rs b/secret-store/src/node_key_pair.rs deleted file mode 100644 index 3b953a95f01..00000000000 --- a/secret-store/src/node_key_pair.rs +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use crypto::publickey::{KeyPair, Public, Signature, Error as EthKeyError, sign, public_to_address}; -use ethereum_types::{H256, Address}; -use blockchain::SigningKeyPair; - -pub struct PlainNodeKeyPair { - key_pair: KeyPair, -} - -impl PlainNodeKeyPair { - pub fn new(key_pair: KeyPair) -> Self { - PlainNodeKeyPair { - key_pair: key_pair, - } - } - - #[cfg(test)] - pub fn key_pair(&self) -> &KeyPair { - &self.key_pair - } -} - -impl SigningKeyPair for PlainNodeKeyPair { - fn public(&self) -> &Public { - self.key_pair.public() - } - - fn address(&self) -> Address { - public_to_address(self.key_pair.public()) - } - - fn sign(&self, data: &H256) -> Result { - sign(self.key_pair.secret(), data) - } -} diff --git a/secret-store/src/serialization.rs b/secret-store/src/serialization.rs deleted file mode 100644 index 90fe9678129..00000000000 --- a/secret-store/src/serialization.rs +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::fmt; -use std::ops::Deref; -use rustc_hex::{self, FromHex}; -use serde::{Serialize, Deserialize, Serializer, Deserializer}; -use serde::de::{Visitor, Error as SerdeError}; -use crypto::publickey::{Public, Secret, Signature}; -use ethereum_types::{H160, H256}; -use bytes::Bytes; -use types::Requester; - -trait ToHex { - fn to_hex(&self) -> String; -} - -impl ToHex for Bytes { - fn to_hex(&self) -> String { - format!("0x{}", rustc_hex::ToHex::to_hex(&self[..])) - } -} - -impl ToHex for Signature { - fn to_hex(&self) -> String { - format!("0x{}", self) - } -} - -impl ToHex for Secret { - fn to_hex(&self) -> String { - format!("0x{}", self.to_hex()) - } -} - -macro_rules! impl_to_hex { - ($name: ident) => ( - impl ToHex for $name { - fn to_hex(&self) -> String { - format!("{:#x}", self) - } - } - ); -} - -macro_rules! impl_bytes_deserialize { - ($name: ident, $value: expr, true) => { - $value[2..].from_hex().map($name).map_err(SerdeError::custom) - }; - ($name: ident, $value: expr, false) => { - $value[2..].parse().map($name).map_err(SerdeError::custom) - } -} - -macro_rules! impl_bytes { - ($name: ident, $other: ident, $from_hex: ident, ($($trait: ident),*)) => { - #[derive(Clone, Debug, PartialEq, Eq, $($trait,)*)] - pub struct $name(pub $other); - - impl From for $name where $other: From { - fn from(s: T) -> $name { - $name(s.into()) - } - } - - impl Into<$other> for $name { - fn into(self) -> $other { - self.0 - } - } - - impl Deref for $name { - type Target = $other; - - fn deref(&self) -> &$other { - &self.0 - } - } - - impl Serialize for $name { - fn serialize(&self, serializer: S) -> Result where S: Serializer { - serializer.serialize_str(<$other as ToHex>::to_hex(&self.0).as_ref()) - } - } - - impl<'a> Deserialize<'a> for $name { - fn deserialize(deserializer: D) -> Result where D: Deserializer<'a> { - struct HexBytesVisitor; - - impl<'b> Visitor<'b> for HexBytesVisitor { - type Value = $name; - - fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { - write!(formatter, "a hex-encoded bytes string") - } - - fn visit_str(self, value: &str) -> Result where E: SerdeError { - if value.len() >= 2 && &value[0..2] == "0x" && value.len() & 1 == 0 { - impl_bytes_deserialize!($name, value, $from_hex) - } else { - Err(SerdeError::custom("invalid format")) - } - } - - fn visit_string(self, value: String) -> Result where E: SerdeError { - self.visit_str(value.as_ref()) - } - } - - deserializer.deserialize_any(HexBytesVisitor) - } - } - } -} - -/// Serializable message hash. -pub type SerializableMessageHash = SerializableH256; -/// Serializable address; -pub type SerializableAddress = SerializableH160; - -impl_to_hex!(H256); -impl_to_hex!(H160); -impl_to_hex!(Public); - -impl_bytes!(SerializableBytes, Bytes, true, (Default)); -impl_bytes!(SerializableH256, H256, false, (Default, PartialOrd, Ord)); -impl_bytes!(SerializableH160, H160, false, (Default)); -impl_bytes!(SerializablePublic, Public, false, (Default, PartialOrd, Ord)); -impl_bytes!(SerializableSecret, Secret, false, ()); -impl_bytes!(SerializableSignature, Signature, false, ()); - -/// Serializable shadow decryption result. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub struct SerializableEncryptedDocumentKeyShadow { - /// Decrypted secret point. It is partially decrypted if shadow decryption was requested. - pub decrypted_secret: SerializablePublic, - /// Shared common point. - pub common_point: SerializablePublic, - /// If shadow decryption was requested: shadow decryption coefficients, encrypted with requestor public. - pub decrypt_shadows: Vec, -} - -/// Serializable requester identification data. -#[derive(Clone, Debug, Serialize, Deserialize)] -pub enum SerializableRequester { - /// Requested with server key id signature. - Signature(SerializableSignature), - /// Requested with public key. - Public(SerializablePublic), - /// Requested with verified address. - Address(SerializableAddress), -} - -impl From for Requester { - fn from(requester: SerializableRequester) -> Requester { - match requester { - SerializableRequester::Signature(signature) => Requester::Signature(signature.into()), - SerializableRequester::Public(public) => Requester::Public(public.into()), - SerializableRequester::Address(address) => Requester::Address(address.into()), - } - } -} - -impl From for SerializableRequester { - fn from(requester: Requester) -> SerializableRequester { - match requester { - Requester::Signature(signature) => SerializableRequester::Signature(signature.into()), - Requester::Public(public) => SerializableRequester::Public(public.into()), - Requester::Address(address) => SerializableRequester::Address(address.into()), - } - } -} - -#[cfg(test)] -mod tests { - use serde_json; - use super::*; - use std::str::FromStr; - - macro_rules! do_test { - ($value: expr, $expected: expr, $expected_type: ident) => ( - let serialized = serde_json::to_string(&$value).unwrap(); - assert_eq!(serialized, $expected); - let deserialized: $expected_type = serde_json::from_str(&serialized).unwrap(); - assert_eq!(deserialized, $value); - ); - } - - #[test] - fn serialize_and_deserialize_bytes() { - do_test!(SerializableBytes(vec![1, 2, 3, 4]), "\"0x01020304\"".to_owned(), SerializableBytes); - } - - #[test] - fn serialize_and_deserialize_h256() { - let s = "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"; - let h256 = SerializableH256(H256::from_str(s).unwrap()); - do_test!(h256, format!("\"0x{}\"", s), SerializableH256); - } - - #[test] - fn serialize_and_deserialize_h160() { - let s = "c6d9d2cd449a754c494264e1809c50e34d64562b"; - let h160 = SerializableH160(H160::from_str(s).unwrap()); - do_test!(h160, format!("\"0x{}\"", s), SerializableH160); - } - - #[test] - fn serialize_and_deserialize_public() { - let s = "cac6c205eb06c8308d65156ff6c862c62b000b8ead121a4455a8ddeff7248128d895692136f240d5d1614dc7cc4147b1bd584bd617e30560bb872064d09ea325"; - let public = SerializablePublic(s.parse().unwrap()); - do_test!(public, format!("\"0x{}\"", s), SerializablePublic); - } - - #[test] - fn serialize_and_deserialize_secret() { - let s = "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"; - let secret = SerializableSecret(Secret::from_str(s).unwrap()); - do_test!(secret, format!("\"0x{}\"", s), SerializableSecret); - } - - #[test] - fn serialize_and_deserialize_signature() { - let raw_r = "afafafafafafafafafafafbcbcbcbcbcbcbcbcbcbeeeeeeeeeeeeedddddddddd"; - let raw_s = "5a39ed1020c04d4d84539975b893a4e7c53eab6c2965db8bc3468093a31bc5ae"; - let r = H256::from_str(raw_r).unwrap(); - let s = H256::from_str(raw_s).unwrap(); - let v = 42u8; - let public = SerializableSignature(Signature::from_rsv(&r, &s, v)); - do_test!(public, format!("\"0x{}{}{:x}\"", raw_r, raw_s, v), SerializableSignature); - } -} diff --git a/secret-store/src/traits.rs b/secret-store/src/traits.rs deleted file mode 100644 index 441bd25a778..00000000000 --- a/secret-store/src/traits.rs +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeSet; -use futures::Future; -use types::{Error, Public, ServerKeyId, MessageHash, EncryptedMessageSignature, RequestSignature, Requester, - EncryptedDocumentKey, EncryptedDocumentKeyShadow, NodeId}; - -/// Server key (SK) generator. -pub trait ServerKeyGenerator { - /// Generate new SK. - /// `key_id` is the caller-provided identifier of generated SK. - /// `author` is the author of key entry. - /// `threshold + 1` is the minimal number of nodes, required to restore private key. - /// Result is a public portion of SK. - fn generate_key( - &self, - key_id: ServerKeyId, - author: Requester, - threshold: usize, - ) -> Box + Send>; - /// Retrieve public portion of previously generated SK. - /// `key_id` is identifier of previously generated SK. - /// `author` is the same author, that has created the server key. - fn restore_key_public( - &self, - key_id: ServerKeyId, - author: Requester, - ) -> Box + Send>; -} - -/// Document key (DK) server. -pub trait DocumentKeyServer: ServerKeyGenerator { - /// Store externally generated DK. - /// `key_id` is identifier of previously generated SK. - /// `author` is the same author, that has created the server key. - /// `common_point` is a result of `k * T` expression, where `T` is generation point and `k` is random scalar in EC field. - /// `encrypted_document_key` is a result of `M + k * y` expression, where `M` is unencrypted document key (point on EC), - /// `k` is the same scalar used in `common_point` calculation and `y` is previously generated public part of SK. - fn store_document_key( - &self, - key_id: ServerKeyId, - author: Requester, - common_point: Public, - encrypted_document_key: Public, - ) -> Box + Send>; - /// Generate and store both SK and DK. This is a shortcut for consequent calls of `generate_key` and `store_document_key`. - /// The only difference is that DK is generated by DocumentKeyServer (which might be considered unsafe). - /// `key_id` is the caller-provided identifier of generated SK. - /// `author` is the author of server && document key entry. - /// `threshold + 1` is the minimal number of nodes, required to restore private key. - /// Result is a DK, encrypted with caller public key. - fn generate_document_key( - &self, - key_id: ServerKeyId, - author: Requester, - threshold: usize, - ) -> Box + Send>; - /// Restore previously stored DK. - /// DK is decrypted on the key server (which might be considered unsafe), and then encrypted with caller public key. - /// `key_id` is identifier of previously generated SK. - /// `requester` is the one who requests access to document key. Caller must be on ACL for this function to succeed. - /// Result is a DK, encrypted with caller public key. - fn restore_document_key( - &self, - key_id: ServerKeyId, - requester: Requester, - ) -> Box + Send>; - /// Restore previously stored DK. - /// To decrypt DK on client: - /// 1) use requestor secret key to decrypt secret coefficients from result.decrypt_shadows - /// 2) calculate decrypt_shadows_sum = sum of all secrets from (1) - /// 3) calculate decrypt_shadow_point: decrypt_shadows_sum * result.common_point - /// 4) calculate decrypted_secret: result.decrypted_secret + decrypt_shadow_point - /// Result is a DK shadow. - fn restore_document_key_shadow( - &self, - key_id: ServerKeyId, - requester: Requester, - ) -> Box + Send>; -} - -/// Message signer. -pub trait MessageSigner: ServerKeyGenerator { - /// Generate Schnorr signature for message with previously generated SK. - /// `key_id` is the caller-provided identifier of generated SK. - /// `requester` is the one who requests access to server key private. - /// `message` is the message to be signed. - /// Result is a signed message, encrypted with caller public key. - fn sign_message_schnorr( - &self, - key_id: ServerKeyId, - requester: Requester, - message: MessageHash, - ) -> Box + Send>; - /// Generate ECDSA signature for message with previously generated SK. - /// WARNING: only possible when SK was generated using t <= 2 * N. - /// `key_id` is the caller-provided identifier of generated SK. - /// `signature` is `key_id`, signed with caller public key. - /// `message` is the message to be signed. - /// Result is a signed message, encrypted with caller public key. - fn sign_message_ecdsa( - &self, - key_id: ServerKeyId, - signature: Requester, - message: MessageHash, - ) -> Box + Send>; -} - -/// Administrative sessions server. -pub trait AdminSessionsServer { - /// Change servers set so that nodes in new_servers_set became owners of shares for all keys. - /// And old nodes (i.e. cluster nodes except new_servers_set) have clear databases. - /// WARNING: newly generated keys will be distributed among all cluster nodes. So this session - /// must be followed with cluster nodes change (either via contract, or config files). - fn change_servers_set( - &self, - old_set_signature: RequestSignature, - new_set_signature: RequestSignature, - new_servers_set: BTreeSet, - ) -> Box + Send>; -} - -/// Key server. -pub trait KeyServer: AdminSessionsServer + DocumentKeyServer + MessageSigner + Send + Sync { -} diff --git a/secret-store/src/types/all.rs b/secret-store/src/types/all.rs deleted file mode 100644 index e85285b2668..00000000000 --- a/secret-store/src/types/all.rs +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::collections::BTreeMap; - -use blockchain::ContractAddress; -use {bytes, ethereum_types}; - -/// Node id. -pub type NodeId = crypto::publickey::Public; -/// Server key id. When key is used to encrypt document, it could be document contents hash. -pub type ServerKeyId = ethereum_types::H256; -/// Encrypted document key type. -pub type EncryptedDocumentKey = bytes::Bytes; -/// Message hash. -pub type MessageHash = ethereum_types::H256; -/// Message signature. -pub type EncryptedMessageSignature = bytes::Bytes; -/// Request signature type. -pub type RequestSignature = crypto::publickey::Signature; -/// Public key type. -pub use crypto::publickey::Public; - -/// Secret store configuration -#[derive(Debug, Clone)] -pub struct NodeAddress { - /// IP address. - pub address: String, - /// IP port. - pub port: u16, -} - -/// Secret store configuration -#[derive(Debug)] -pub struct ServiceConfiguration { - /// HTTP listener address. If None, HTTP API is disabled. - pub listener_address: Option, - /// Service contract address. - pub service_contract_address: Option, - /// Server key generation service contract address. - pub service_contract_srv_gen_address: Option, - /// Server key retrieval service contract address. - pub service_contract_srv_retr_address: Option, - /// Document key store service contract address. - pub service_contract_doc_store_address: Option, - /// Document key shadow retrieval service contract address. - pub service_contract_doc_sretr_address: Option, - /// ACL check contract address. If None, everyone has access to all keys. Useful for tests only. - pub acl_check_contract_address: Option, - /// Cluster configuration. - pub cluster_config: ClusterConfiguration, - // Allowed CORS domains - pub cors: Option>, -} - -/// Key server cluster configuration -#[derive(Debug)] -pub struct ClusterConfiguration { - /// This node address. - pub listener_address: NodeAddress, - /// All cluster nodes addresses. - pub nodes: BTreeMap, - /// Key Server Set contract address. If None, servers from 'nodes' map are used. - pub key_server_set_contract_address: Option, - /// Allow outbound connections to 'higher' nodes. - /// This is useful for tests, but slower a bit for production. - pub allow_connecting_to_higher_nodes: bool, - /// Administrator public key. - pub admin_public: Option, - /// Should key servers set change session should be started when servers set changes. - /// This will only work when servers set is configured using KeyServerSet contract. - pub auto_migrate_enabled: bool, -} - -/// Shadow decryption result. -#[derive(Clone, Debug, PartialEq)] -pub struct EncryptedDocumentKeyShadow { - /// Decrypted secret point. It is partially decrypted if shadow decryption was requested. - pub decrypted_secret: crypto::publickey::Public, - /// Shared common point. - pub common_point: Option, - /// If shadow decryption was requested: shadow decryption coefficients, encrypted with requestor public. - pub decrypt_shadows: Option>>, -} - -/// Requester identification data. -#[derive(Debug, Clone)] -pub enum Requester { - /// Requested with server key id signature. - Signature(crypto::publickey::Signature), - /// Requested with public key. - Public(crypto::publickey::Public), - /// Requested with verified address. - Address(ethereum_types::Address), -} - -impl Default for Requester { - fn default() -> Self { - Requester::Signature(Default::default()) - } -} - -impl Requester { - pub fn public(&self, server_key_id: &ServerKeyId) -> Result { - match *self { - Requester::Signature(ref signature) => crypto::publickey::recover(signature, server_key_id) - .map_err(|e| format!("bad signature: {}", e)), - Requester::Public(ref public) => Ok(public.clone()), - Requester::Address(_) => Err("cannot recover public from address".into()), - } - } - - pub fn address(&self, server_key_id: &ServerKeyId) -> Result { - self.public(server_key_id) - .map(|p| crypto::publickey::public_to_address(&p)) - } -} - -impl From for Requester { - fn from(signature: crypto::publickey::Signature) -> Requester { - Requester::Signature(signature) - } -} - -impl From for Requester { - fn from(public: ethereum_types::Public) -> Requester { - Requester::Public(public) - } -} - -impl From for Requester { - fn from(address: ethereum_types::Address) -> Requester { - Requester::Address(address) - } -} diff --git a/secret-store/src/types/error.rs b/secret-store/src/types/error.rs deleted file mode 100644 index 29a9262e6d3..00000000000 --- a/secret-store/src/types/error.rs +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use std::fmt; -use std::net; -use std::io::Error as IoError; - -use crypto; - -/// Secret store error. -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum Error { - /// Invalid node address has been passed. - InvalidNodeAddress, - /// Invalid node id has been passed. - InvalidNodeId, - /// Session with the given id already exists. - DuplicateSessionId, - /// No active session with given id. - NoActiveSessionWithId, - /// Invalid threshold value has been passed. - /// Threshold value must be in [0; n - 1], where n is a number of nodes participating in the encryption. - NotEnoughNodesForThreshold, - /// Current state of encryption/decryption session does not allow to proceed request. - /// Reschedule this request for later processing. - TooEarlyForRequest, - /// Current state of encryption/decryption session does not allow to proceed request. - /// This means that either there is some comm-failure or node is misbehaving/cheating. - InvalidStateForRequest, - /// Request cannot be sent/received from this node. - InvalidNodeForRequest, - /// Message or some data in the message was recognized as invalid. - /// This means that node is misbehaving/cheating. - InvalidMessage, - /// Message version is not supported. - InvalidMessageVersion, - /// Message is invalid because of replay-attack protection. - ReplayProtection, - /// Connection to node, required for this session is not established. - NodeDisconnected, - /// Server key with this ID is already generated. - ServerKeyAlreadyGenerated, - /// Server key with this ID is not yet generated. - ServerKeyIsNotFound, - /// Document key with this ID is already stored. - DocumentKeyAlreadyStored, - /// Document key with this ID is not yet stored. - DocumentKeyIsNotFound, - /// Consensus is temporary unreachable. Means that something is currently blocking us from either forming - /// consensus group (like disconnecting from too many nodes, which are AGREE to participate in consensus) - /// or from rejecting request (disconnecting from AccessDenied-nodes). - ConsensusTemporaryUnreachable, - /// Consensus is unreachable. It doesn't mean that it will ALWAYS remain unreachable, but right NOW we have - /// enough nodes confirmed that they do not want to be a part of consensus. Example: we're connected to 10 - /// of 100 nodes. Key threshold is 6 (i.e. 7 nodes are required for consensus). 4 nodes are responding with - /// reject => consensus is considered unreachable, even though another 90 nodes still can respond with OK. - ConsensusUnreachable, - /// Acl storage error. - AccessDenied, - /// Can't start session, because exclusive session is active. - ExclusiveSessionActive, - /// Can't start exclusive session, because there are other active sessions. - HasActiveSessions, - /// Insufficient requester data. - InsufficientRequesterData(String), - /// Cryptographic error. - EthKey(String), - /// I/O error has occurred. - Io(String), - /// Deserialization error has occurred. - Serde(String), - /// Hyper error. - Hyper(String), - /// Database-related error. - Database(String), - /// Internal error. - Internal(String), -} - -impl Error { - /// Is this a fatal error? Non-fatal means that it is possible to replay the same request with a non-zero - /// chance to success. I.e. the error is not about request itself (or current environment factors that - /// are affecting request processing), but about current SecretStore state. - pub fn is_non_fatal(&self) -> bool { - match *self { - // non-fatal errors: - - // session start errors => restarting session is a solution - Error::DuplicateSessionId | Error::NoActiveSessionWithId | - // unexpected message errors => restarting session/excluding node is a solution - Error::TooEarlyForRequest | Error::InvalidStateForRequest | Error::InvalidNodeForRequest | - // invalid message errors => restarting/updating/excluding node is a solution - Error::InvalidMessage | Error::InvalidMessageVersion | Error::ReplayProtection | - // connectivity problems => waiting for reconnect && restarting session is a solution - Error::NodeDisconnected | - // temporary (?) consensus problems, related to other non-fatal errors => restarting is probably (!) a solution - Error::ConsensusTemporaryUnreachable | - // exclusive session errors => waiting && restarting is a solution - Error::ExclusiveSessionActive | Error::HasActiveSessions => true, - - // fatal errors: - - // config-related errors - Error::InvalidNodeAddress | Error::InvalidNodeId | - // wrong session input params errors - Error::NotEnoughNodesForThreshold | Error::ServerKeyAlreadyGenerated | Error::ServerKeyIsNotFound | - Error::DocumentKeyAlreadyStored | Error::DocumentKeyIsNotFound | Error::InsufficientRequesterData(_) | - // access denied/consensus error - Error::AccessDenied | Error::ConsensusUnreachable | - // indeterminate internal errors, which could be either fatal (db failure, invalid request), or not (network error), - // but we still consider these errors as fatal - Error::EthKey(_) | Error::Serde(_) | Error::Hyper(_) | Error::Database(_) | Error::Internal(_) | Error::Io(_) => false, - } - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { - match *self { - Error::InvalidNodeAddress => write!(f, "invalid node address has been passed"), - Error::InvalidNodeId => write!(f, "invalid node id has been passed"), - Error::DuplicateSessionId => write!(f, "session with the same id is already registered"), - Error::NoActiveSessionWithId => write!(f, "no active session with given id"), - Error::NotEnoughNodesForThreshold => write!(f, "not enough nodes for passed threshold"), - Error::TooEarlyForRequest => write!(f, "session is not yet ready to process this request"), - Error::InvalidStateForRequest => write!(f, "session is in invalid state for processing this request"), - Error::InvalidNodeForRequest => write!(f, "invalid node for this request"), - Error::InvalidMessage => write!(f, "invalid message is received"), - Error::InvalidMessageVersion => write!(f, "unsupported message is received"), - Error::ReplayProtection => write!(f, "replay message is received"), - Error::NodeDisconnected => write!(f, "node required for this operation is currently disconnected"), - Error::ServerKeyAlreadyGenerated => write!(f, "Server key with this ID is already generated"), - Error::ServerKeyIsNotFound => write!(f, "Server key with this ID is not found"), - Error::DocumentKeyAlreadyStored => write!(f, "Document key with this ID is already stored"), - Error::DocumentKeyIsNotFound => write!(f, "Document key with this ID is not found"), - Error::ConsensusUnreachable => write!(f, "Consensus unreachable"), - Error::ConsensusTemporaryUnreachable => write!(f, "Consensus temporary unreachable"), - Error::AccessDenied => write!(f, "Access denied"), - Error::ExclusiveSessionActive => write!(f, "Exclusive session active"), - Error::HasActiveSessions => write!(f, "Unable to start exclusive session"), - Error::InsufficientRequesterData(ref e) => write!(f, "Insufficient requester data: {}", e), - Error::EthKey(ref e) => write!(f, "cryptographic error {}", e), - Error::Hyper(ref msg) => write!(f, "Hyper error: {}", msg), - Error::Serde(ref msg) => write!(f, "Serialization error: {}", msg), - Error::Database(ref msg) => write!(f, "Database error: {}", msg), - Error::Internal(ref msg) => write!(f, "Internal error: {}", msg), - Error::Io(ref msg) => write!(f, "IO error: {}", msg), - } - } -} - -impl From for Error { - fn from(err: crypto::publickey::Error) -> Self { - Error::EthKey(err.into()) - } -} - -impl From for Error { - fn from(err: crypto::Error) -> Self { - Error::EthKey(err.to_string()) - } -} - -impl From for Error { - fn from(err: IoError) -> Self { - Error::Io(err.to_string()) - } -} - -impl Into for Error { - fn into(self) -> String { - format!("{}", self) - } -} - -impl From for Error { - fn from(err: net::AddrParseError) -> Error { - Error::Internal(err.to_string()) - } -} diff --git a/secret-store/src/types/mod.rs b/secret-store/src/types/mod.rs deleted file mode 100644 index 3a8511d233e..00000000000 --- a/secret-store/src/types/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Types used in the public api - -mod all; -mod error; - -pub use self::all::*; -pub use self::error::*; diff --git a/updater/hash-fetch/Cargo.toml b/updater/hash-fetch/Cargo.toml index 0f39d61c9ad..c3ea793a5f1 100644 --- a/updater/hash-fetch/Cargo.toml +++ b/updater/hash-fetch/Cargo.toml @@ -13,11 +13,11 @@ log = "0.4" mime = "0.3" mime_guess = "2.0.1" rand = "0.7" -rustc-hex = "1.0" +rustc-hex = "2.1.0" fetch = { path = "../../util/fetch" } parity-bytes = "0.1" ethereum-types = "0.8.0" -parity-runtime = { path = "../../util/runtime" } +parity-runtime = "0.1.1" keccak-hash = "0.4.0" registrar = { path = "../../util/registrar" } types = { path = "../../ethcore/types", package = "common-types" } diff --git a/updater/hash-fetch/src/client.rs b/updater/hash-fetch/src/client.rs index 09a70fee0de..45bccc2fd33 100644 --- a/updater/hash-fetch/src/client.rs +++ b/updater/hash-fetch/src/client.rs @@ -203,8 +203,8 @@ mod tests { fn registrar() -> FakeRegistrar { let mut registrar = FakeRegistrar::new(); registrar.responses = Mutex::new(vec![ - Ok(format!("000000000000000000000000{}", URLHINT).from_hex().unwrap()), - Ok("00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000deadcafebeefbeefcafedeaddeedfeedffffffff000000000000000000000000000000000000000000000000000000000000003c68747470733a2f2f7061726974792e696f2f6173736574732f696d616765732f657468636f72652d626c61636b2d686f72697a6f6e74616c2e706e6700000000".from_hex().unwrap()), + Ok(format!("000000000000000000000000{}", URLHINT).from_hex::>().unwrap()), + Ok("00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000000000000000000000000000000deadcafebeefbeefcafedeaddeedfeedffffffff000000000000000000000000000000000000000000000000000000000000003c68747470733a2f2f7061726974792e696f2f6173736574732f696d616765732f657468636f72652d626c61636b2d686f72697a6f6e74616c2e706e6700000000".from_hex::>().unwrap()), ]); registrar } diff --git a/updater/hash-fetch/src/urlhint.rs b/updater/hash-fetch/src/urlhint.rs index dcb1347abc1..708ecf19d67 100644 --- a/updater/hash-fetch/src/urlhint.rs +++ b/updater/hash-fetch/src/urlhint.rs @@ -52,7 +52,7 @@ impl GithubApp { pub fn url(&self) -> String { // Since https fetcher doesn't support redirections we use direct link // format!("https://github.com/{}/{}/archive/{}.zip", self.account, self.repo, self.commit.to_hex()) - format!("https://codeload.github.com/{}/{}/zip/{}", self.account, self.repo, self.commit.to_hex()) + format!("https://codeload.github.com/{}/{}/zip/{}", self.account, self.repo, self.commit.to_hex::()) } fn commit(bytes: &[u8]) -> Option<[u8;COMMIT_LEN]> { @@ -313,11 +313,13 @@ pub mod tests { // when let res = urlhint.resolve(h256_from_short_str("test")).unwrap(); + let c: Vec = "ec4c1fe06c808fe3739858c347109b1f5f1ed4b5".from_hex().unwrap(); + // then assert_eq!(res, Some(URLHintResult::Dapp(GithubApp { account: "ethcore".into(), repo: "dao.claim".into(), - commit: GithubApp::commit(&"ec4c1fe06c808fe3739858c347109b1f5f1ed4b5".from_hex().unwrap()).unwrap(), + commit: GithubApp::commit(&c).unwrap(), owner: Address::from_str("deadcafebeefbeefcafedeaddeedfeedffffffff").unwrap(), }))) } diff --git a/util/EIP-2124/Cargo.toml b/util/EIP-2124/Cargo.toml deleted file mode 100644 index 09b0956256e..00000000000 --- a/util/EIP-2124/Cargo.toml +++ /dev/null @@ -1,21 +0,0 @@ -[package] -name = "eip-2124" -version = "0.1.0" -authors = ["Parity Technologies "] -repository = "https://github.com/paritytech/parity-ethereum" -documentation = "https://docs.rs/eip-2124" -readme = "README.md" -description = "EIP-2124 Fork ID implementation" -keywords = ["eip-2124", "eip"] -license = "GPL-3.0" -edition = "2018" - -[dependencies] -crc = "1" -ethereum-types = "0.8.0" -maplit = "1" -rlp = "0.4" -rlp_derive = { path = "../rlp-derive" } - -[dev-dependencies] -hex-literal = "0.2" diff --git a/util/EIP-2124/src/lib.rs b/util/EIP-2124/src/lib.rs deleted file mode 100644 index 612465814b6..00000000000 --- a/util/EIP-2124/src/lib.rs +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright 2015-2019 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! EIP-2124 implementation based on . - -#![deny(missing_docs)] - -#![warn( - clippy::all, - clippy::pedantic, - clippy::nursery, -)] - -use crc::crc32; -use ethereum_types::H256; -use maplit::btreemap; -use rlp::{DecoderError, Rlp, RlpStream}; -use rlp_derive::{RlpDecodable, RlpEncodable}; -use std::collections::{BTreeMap, BTreeSet}; - -/// Block number. -pub type BlockNumber = u64; - -/// `CRC32` hash of all previous forks starting from genesis block. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct ForkHash(pub u32); - -impl rlp::Encodable for ForkHash { - fn rlp_append(&self, s: &mut RlpStream) { - s.encoder().encode_value(&self.0.to_be_bytes()); - } -} - -impl rlp::Decodable for ForkHash { - fn decode(rlp: &Rlp) -> Result { - rlp.decoder().decode_value(|b| { - if b.len() != 4 { - return Err(DecoderError::RlpInvalidLength); - } - - let mut blob = [0; 4]; - blob.copy_from_slice(&b[..]); - - Ok(Self(u32::from_be_bytes(blob))) - }) - } -} - -impl From for ForkHash { - fn from(genesis: H256) -> Self { - Self(crc32::checksum_ieee(&genesis[..])) - } -} - -impl std::ops::AddAssign for ForkHash { - fn add_assign(&mut self, height: BlockNumber) { - let blob = height.to_be_bytes(); - self.0 = crc32::update(self.0, &crc32::IEEE_TABLE, &blob) - } -} - -impl std::ops::Add for ForkHash { - type Output = Self; - fn add(mut self, height: BlockNumber) -> Self { - self += height; - self - } -} - -/// A fork identifier as defined by EIP-2124. -/// Serves as the chain compatibility identifier. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, RlpEncodable, RlpDecodable)] -pub struct ForkId { - /// CRC32 checksum of the all fork blocks from genesis. - pub hash: ForkHash, - /// Next upcoming fork block number, 0 if not yet known. - pub next: BlockNumber -} - -/// Reason for rejecting provided `ForkId`. -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub enum RejectReason { - /// Remote node is outdated and needs a software update. - RemoteStale, - /// Local node is on an incompatible chain or needs a sofwtare update. - LocalIncompatibleOrStale, -} - -/// Filter that describes the state of blockchain and can be used to check incoming `ForkId`s for compatibility. -#[derive(Clone, Debug)] -pub struct ForkFilter { - /// Blockchain head - pub head: BlockNumber, - past_forks: BTreeMap, - next_forks: BTreeSet, -} - -impl ForkFilter { - /// Create the filter from provided head, genesis block hash, past forks and expected future forks. - pub fn new(head: BlockNumber, genesis: H256, past_forks: PF, next_forks: NF) -> Self - where - PF: IntoIterator, - NF: IntoIterator, - { - let genesis_fork_hash = ForkHash::from(genesis); - Self { - head, - past_forks: past_forks.into_iter().fold((btreemap! { 0 => genesis_fork_hash }, genesis_fork_hash), |(mut acc, base_hash), block| { - let fork_hash = base_hash + block; - acc.insert(block, fork_hash); - (acc, fork_hash) - }).0, - next_forks: next_forks.into_iter().collect(), - } - } - - fn current_fork_hash(&self) -> ForkHash { - *self.past_forks.values().next_back().expect("there is always at least one - genesis - fork hash; qed") - } - - fn future_fork_hashes(&self) -> Vec { - self.next_forks.iter().fold((Vec::new(), self.current_fork_hash()), |(mut acc, hash), fork| { - let next = hash + *fork; - acc.push(next); - (acc, next) - }).0 - } - - /// Insert a new past fork - pub fn insert_past_fork(&mut self, height: BlockNumber) { - self.past_forks.insert(height, self.current_fork_hash() + height); - } - - /// Insert a new upcoming fork - pub fn insert_next_fork(&mut self, height: BlockNumber) { - self.next_forks.insert(height); - } - - /// Mark an upcoming fork as already happened and immutable. - /// Returns `false` if no such fork existed and the call was a no-op. - pub fn promote_next_fork(&mut self, height: BlockNumber) -> bool { - let promoted = self.next_forks.remove(&height); - if promoted { - self.insert_past_fork(height); - } - promoted - } - - /// Check whether the provided `ForkId` is compatible based on the validation rules in `EIP-2124`. - /// - /// # Errors - /// Returns a `RejectReason` if the `ForkId` is not compatible. - pub fn is_valid(&self, fork_id: ForkId) -> Result<(), RejectReason> { - // 1) If local and remote FORK_HASH matches... - if self.current_fork_hash() == fork_id.hash { - if fork_id.next == 0 { - // 1b) No remotely announced fork, connect. - return Ok(()) - } - - //... compare local head to FORK_NEXT. - if self.head >= fork_id.next { - // 1a) A remotely announced but remotely not passed block is already passed locally, disconnect, - // since the chains are incompatible. - return Err(RejectReason::LocalIncompatibleOrStale) - } else { - // 1b) Remotely announced fork not yet passed locally, connect. - return Ok(()) - } - } - - // 2) If the remote FORK_HASH is a subset of the local past forks... - let mut it = self.past_forks.iter(); - while let Some((_, hash)) = it.next() { - if *hash == fork_id.hash { - // ...and the remote FORK_NEXT matches with the locally following fork block number, connect. - if let Some((actual_fork_block, _)) = it.next() { - if *actual_fork_block == fork_id.next { - return Ok(()) - } else { - return Err(RejectReason::RemoteStale); - } - } - - break; - } - } - - // 3) If the remote FORK_HASH is a superset of the local past forks and can be completed with locally known future forks, connect. - for future_fork_hash in self.future_fork_hashes() { - if future_fork_hash == fork_id.hash { - return Ok(()) - } - } - - // 4) Reject in all other cases. - Err(RejectReason::LocalIncompatibleOrStale) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use hex_literal::hex; - - const GENESIS_HASH: &str = "d4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3"; - const BYZANTIUM_FORK_HEIGHT: BlockNumber = 4370000; - const PETERSBURG_FORK_HEIGHT: BlockNumber = 7280000; - - // EIP test vectors. - - #[test] - fn test_forkhash() { - let mut fork_hash = ForkHash::from(GENESIS_HASH.parse::().unwrap()); - assert_eq!(fork_hash.0, 0xfc64ec04); - - fork_hash += 1150000; - assert_eq!(fork_hash.0, 0x97c2c34c); - - fork_hash += 1920000; - assert_eq!(fork_hash.0, 0x91d1f948); - } - - #[test] - fn test_compatibility_check() { - let spurious_filter = ForkFilter::new( - 4369999, - GENESIS_HASH.parse().unwrap(), - vec![1150000, 1920000, 2463000, 2675000], - vec![BYZANTIUM_FORK_HEIGHT] - ); - let mut byzantium_filter = spurious_filter.clone(); - byzantium_filter.promote_next_fork(BYZANTIUM_FORK_HEIGHT); - byzantium_filter.insert_next_fork(PETERSBURG_FORK_HEIGHT); - byzantium_filter.head = 7279999; - - let mut petersburg_filter = byzantium_filter.clone(); - petersburg_filter.promote_next_fork(PETERSBURG_FORK_HEIGHT); - petersburg_filter.head = 7987396; - - // Local is mainnet Petersburg, remote announces the same. No future fork is announced. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0x668db0af), next: 0 }), Ok(())); - - // Local is mainnet Petersburg, remote announces the same. Remote also announces a next fork - // at block 0xffffffff, but that is uncertain. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0x668db0af), next: BlockNumber::max_value() }), Ok(())); - - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg),remote announces - // also Byzantium, but it's not yet aware of Petersburg (e.g. non updated node before the fork). - // In this case we don't know if Petersburg passed yet or not. - assert_eq!(byzantium_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: 0 }), Ok(())); - - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces - // also Byzantium, and it's also aware of Petersburg (e.g. updated node before the fork). We - // don't know if Petersburg passed yet (will pass) or not. - assert_eq!(byzantium_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: PETERSBURG_FORK_HEIGHT }), Ok(())); - - // Local is mainnet currently in Byzantium only (so it's aware of Petersburg), remote announces - // also Byzantium, and it's also aware of some random fork (e.g. misconfigured Petersburg). As - // neither forks passed at neither nodes, they may mismatch, but we still connect for now. - assert_eq!(byzantium_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: BlockNumber::max_value() }), Ok(())); - - // Local is mainnet Petersburg, remote announces Byzantium + knowledge about Petersburg. Remote is simply out of sync, accept. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: PETERSBURG_FORK_HEIGHT }), Ok(())); - - // Local is mainnet Petersburg, remote announces Spurious + knowledge about Byzantium. Remote - // is definitely out of sync. It may or may not need the Petersburg update, we don't know yet. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0x3edd5b10), next: 4370000 }), Ok(())); - - // Local is mainnet Byzantium, remote announces Petersburg. Local is out of sync, accept. - assert_eq!(byzantium_filter.is_valid(ForkId { hash: ForkHash(0x668db0af), next: 0 }), Ok(())); - - // Local is mainnet Spurious, remote announces Byzantium, but is not aware of Petersburg. Local - // out of sync. Local also knows about a future fork, but that is uncertain yet. - assert_eq!(spurious_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: 0 }), Ok(())); - - // Local is mainnet Petersburg. remote announces Byzantium but is not aware of further forks. - // Remote needs software update. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: 0 }), Err(RejectReason::RemoteStale)); - - // Local is mainnet Petersburg, and isn't aware of more forks. Remote announces Petersburg + - // 0xffffffff. Local needs software update, reject. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0x5cddc0e1), next: 0 }), Err(RejectReason::LocalIncompatibleOrStale)); - - // Local is mainnet Byzantium, and is aware of Petersburg. Remote announces Petersburg + - // 0xffffffff. Local needs software update, reject. - assert_eq!(byzantium_filter.is_valid(ForkId { hash: ForkHash(0x5cddc0e1), next: 0 }), Err(RejectReason::LocalIncompatibleOrStale)); - - // Local is mainnet Petersburg, remote is Rinkeby Petersburg. - assert_eq!(petersburg_filter.is_valid(ForkId { hash: ForkHash(0xafec6b27), next: 0 }), Err(RejectReason::LocalIncompatibleOrStale)); - - // Local is mainnet Petersburg, far in the future. Remote announces Gopherium (non existing fork) - // at some future block 88888888, for itself, but past block for local. Local is incompatible. - // - // This case detects non-upgraded nodes with majority hash power (typical Ropsten mess). - let mut far_away_petersburg = petersburg_filter.clone(); - far_away_petersburg.head = 88888888; - assert_eq!(far_away_petersburg.is_valid(ForkId { hash: ForkHash(0x668db0af), next: 88888888 }), Err(RejectReason::LocalIncompatibleOrStale)); - - // Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing - // fork) at block 7279999, before Petersburg. Local is incompatible. - assert_eq!(byzantium_filter.is_valid(ForkId { hash: ForkHash(0xa00bc324), next: 7279999 }), Err(RejectReason::LocalIncompatibleOrStale)); - } - - #[test] - fn test_forkid_serialization() { - assert_eq!(rlp::encode(&ForkId { hash: ForkHash(0), next: 0 }), hex!("c6840000000080")); - assert_eq!(rlp::encode(&ForkId { hash: ForkHash(0xdeadbeef), next: 0xBADDCAFE }), hex!("ca84deadbeef84baddcafe")); - assert_eq!(rlp::encode(&ForkId { hash: ForkHash(u32::max_value()), next: u64::max_value() }), hex!("ce84ffffffff88ffffffffffffffff")); - - assert_eq!(rlp::decode::(&hex!("c6840000000080")).unwrap(), ForkId { hash: ForkHash(0), next: 0 }); - assert_eq!(rlp::decode::(&hex!("ca84deadbeef84baddcafe")).unwrap(), ForkId { hash: ForkHash(0xdeadbeef), next: 0xBADDCAFE }); - assert_eq!(rlp::decode::(&hex!("ce84ffffffff88ffffffffffffffff")).unwrap(), ForkId { hash: ForkHash(u32::max_value()), next: u64::max_value() }); - } -} diff --git a/util/bloom/src/lib.rs b/util/bloom/src/lib.rs index 0c67944e983..c7f3f17372f 100644 --- a/util/bloom/src/lib.rs +++ b/util/bloom/src/lib.rs @@ -84,9 +84,9 @@ impl Bloom { let k_num = Bloom::optimal_k_num(bitmap_bits, items_count); let bitmap = BitVecJournal::new(bitmap_bits as usize); Bloom { - bitmap: bitmap, - bitmap_bits: bitmap_bits, - k_num: k_num, + bitmap, + bitmap_bits, + k_num, } } @@ -96,9 +96,9 @@ impl Bloom { let bitmap_bits = (bitmap_size as u64) * 8u64; let bitmap = BitVecJournal::from_parts(parts); Bloom { - bitmap: bitmap, - bitmap_bits: bitmap_bits, - k_num: k_num, + bitmap, + bitmap_bits, + k_num, } } @@ -169,15 +169,14 @@ impl Bloom { { let mut sip = SipHasher::new(); item.hash(&mut sip); - let hash = sip.finish(); - hash + sip.finish() } fn bloom_hash(base_hash: u64, k_i: u32) -> u64 { if k_i < 2 { base_hash } else { - base_hash.wrapping_add((k_i as u64).wrapping_mul(base_hash) % 0xffffffffffffffc5) + base_hash.wrapping_add((k_i as u64).wrapping_mul(base_hash) % 0xffff_ffff_ffff_ffc5) } } diff --git a/util/len-caching-lock/Cargo.toml b/util/len-caching-lock/Cargo.toml index a354c71fcb7..3e02dc1a299 100644 --- a/util/len-caching-lock/Cargo.toml +++ b/util/len-caching-lock/Cargo.toml @@ -5,6 +5,7 @@ license = "GPL-3.0" name = "len-caching-lock" version = "0.1.1" authors = ["Parity Technologies "] +edition = "2018" [dependencies] parking_lot = "0.10.0" diff --git a/util/len-caching-lock/src/lib.rs b/util/len-caching-lock/src/lib.rs index 18118937f90..f43e7f9955a 100644 --- a/util/len-caching-lock/src/lib.rs +++ b/util/len-caching-lock/src/lib.rs @@ -25,19 +25,15 @@ //! ## Example //! //! ```rust -//! extern crate len_caching_lock; //! use len_caching_lock::LenCachingMutex; //! -//! fn main() { -//! let vec: Vec = Vec::new(); -//! let len_caching_mutex = LenCachingMutex::new(vec); -//! assert_eq!(len_caching_mutex.lock().len(), len_caching_mutex.load_len()); -//! len_caching_mutex.lock().push(0); -//! assert_eq!(1, len_caching_mutex.load_len()); -//! } -//! ``` - -extern crate parking_lot; +//! let vec: Vec = Vec::new(); +//! let len_caching_mutex = LenCachingMutex::new(vec); +//! assert_eq!(len_caching_mutex.lock().len(), len_caching_mutex.load_len()); +//! len_caching_mutex.lock().push(0); +//! assert_eq!(1, len_caching_mutex.load_len()); +//! ``` + use std::collections::{VecDeque, LinkedList, HashMap, BTreeMap, HashSet, BTreeSet, BinaryHeap}; use std::hash::Hash; @@ -66,7 +62,7 @@ impl Len for LinkedList { fn len(&self) -> usize { LinkedList::len(self) } } -impl Len for HashMap { +impl Len for HashMap { fn len(&self) -> usize { HashMap::len(self) } } @@ -74,7 +70,7 @@ impl Len for BTreeMap { fn len(&self) -> usize { BTreeMap::len(self) } } -impl Len for HashSet { +impl Len for HashSet { fn len(&self) -> usize { HashSet::len(self) } } diff --git a/util/len-caching-lock/src/mutex.rs b/util/len-caching-lock/src/mutex.rs index 2d54b48dbce..8f401823c14 100644 --- a/util/len-caching-lock/src/mutex.rs +++ b/util/len-caching-lock/src/mutex.rs @@ -20,7 +20,7 @@ use std::sync::atomic::Ordering; use parking_lot::{Mutex, MutexGuard}; -use Len; +use crate::Len; /// Can be used in place of a [`Mutex`](../../lock_api/struct.Mutex.html) where reading `T`'s `len()` without /// needing to lock, is advantageous. diff --git a/util/len-caching-lock/src/rwlock.rs b/util/len-caching-lock/src/rwlock.rs index 6a4487d48d7..661b4ae1a92 100644 --- a/util/len-caching-lock/src/rwlock.rs +++ b/util/len-caching-lock/src/rwlock.rs @@ -20,7 +20,7 @@ use std::sync::atomic::Ordering; use parking_lot::{RwLock, RwLockReadGuard, RwLockWriteGuard}; -use Len; +use crate::Len; /// Can be used in place of a [`RwLock`](../../lock_api/struct.RwLock.html) where /// reading `T`'s `len()` without needing to lock, is advantageous. diff --git a/util/macros/Cargo.toml b/util/macros/Cargo.toml deleted file mode 100644 index e0732b0b918..00000000000 --- a/util/macros/Cargo.toml +++ /dev/null @@ -1,5 +0,0 @@ -[package] -name = "macros" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" diff --git a/util/macros/src/lib.rs b/util/macros/src/lib.rs deleted file mode 100644 index 471ec1d6b58..00000000000 --- a/util/macros/src/lib.rs +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Utils common types and macros global reexport. - -use std::io; - -#[macro_export] -macro_rules! vec_into { - ( $( $x:expr ),* ) => { - vec![ $( $x.into() ),* ] - } -} - -#[macro_export] -macro_rules! slice_into { - ( $( $x:expr ),* ) => { - &[ $( $x.into() ),* ] - } -} - -#[macro_export] -macro_rules! hash_map { - () => { HashMap::new() }; - ( $( $x:expr => $y:expr ),* ) => {{ - let mut x = HashMap::new(); - $( - x.insert($x, $y); - )* - x - }} -} - -#[macro_export] -macro_rules! map { - () => { BTreeMap::new() }; - ( $( $x:expr => $y:expr ),* ) => {{ - let mut x = BTreeMap::new(); - $( - x.insert($x, $y); - )* - x - }} -} - -#[macro_export] -macro_rules! flush { - ($arg:expr) => ($crate::flush($arg.into())); - ($($arg:tt)*) => ($crate::flush(format!("{}", format_args!($($arg)*)))); -} - -#[macro_export] -macro_rules! flushln { - ($fmt:expr) => (flush!(concat!($fmt, "\n"))); - ($fmt:expr, $($arg:tt)*) => (flush!(concat!($fmt, "\n"), $($arg)*)); -} - -#[doc(hidden)] -pub fn flush(s: String) { - let _ = io::Write::write(&mut io::stdout(), s.as_bytes()); - let _ = io::Write::flush(&mut io::stdout()); -} - -#[test] -fn test_flush() { - flushln!("hello_world {:?}", 1); -} diff --git a/util/migration-rocksdb/Cargo.toml b/util/migration-rocksdb/Cargo.toml index 6ef6bd4bb0f..1748b4b1586 100644 --- a/util/migration-rocksdb/Cargo.toml +++ b/util/migration-rocksdb/Cargo.toml @@ -2,12 +2,14 @@ name = "migration-rocksdb" version = "0.1.0" authors = ["Parity Technologies "] +edition = "2018" +license = "GPL-3.0" [dependencies] log = "0.4" -macros = { path = "../macros" } kvdb = "0.4.0" kvdb-rocksdb = "0.5.0" [dev-dependencies] tempdir = "0.3" +maplit = "1.0.2" diff --git a/util/migration-rocksdb/src/lib.rs b/util/migration-rocksdb/src/lib.rs index 384909c426f..169732a79af 100644 --- a/util/migration-rocksdb/src/lib.rs +++ b/util/migration-rocksdb/src/lib.rs @@ -16,19 +16,12 @@ //! DB Migration module. -#[macro_use] -extern crate log; -#[macro_use] -extern crate macros; - -extern crate kvdb; -extern crate kvdb_rocksdb; - use std::collections::BTreeMap; use std::path::{Path, PathBuf}; use std::sync::Arc; use std::{fs, io, error}; +use log::trace; use kvdb::DBTransaction; use kvdb_rocksdb::{CompactionProfile, Database, DatabaseConfig}; @@ -309,29 +302,3 @@ impl Manager { self.migrations.iter_mut().filter(|m| m.version() > version).collect() } } - -/// Prints a dot every `max` ticks -pub struct Progress { - current: usize, - max: usize, -} - -impl Default for Progress { - fn default() -> Self { - Progress { - current: 0, - max: 100_000, - } - } -} - -impl Progress { - /// Tick progress meter. - pub fn tick(&mut self) { - self.current += 1; - if self.current == self.max { - self.current = 0; - flush!("."); - } - } -} diff --git a/util/migration-rocksdb/tests/tests.rs b/util/migration-rocksdb/tests/tests.rs index 335082de0b9..4d3b33283a2 100644 --- a/util/migration-rocksdb/tests/tests.rs +++ b/util/migration-rocksdb/tests/tests.rs @@ -18,19 +18,15 @@ //! A random temp directory is created. A database is created within it, and migrations //! are performed in temp sub-directories. -#[macro_use] -extern crate macros; -extern crate tempdir; -extern crate kvdb_rocksdb; -extern crate migration_rocksdb as migration; - use std::collections::BTreeMap; use std::io; use std::path::{Path, PathBuf}; use std::sync::Arc; -use tempdir::TempDir; + use kvdb_rocksdb::{Database, DatabaseConfig}; -use migration::{Batch, Config, SimpleMigration, Migration, Manager, ChangeColumns}; +use maplit::btreemap; +use migration_rocksdb::{Batch, Config, SimpleMigration, Migration, Manager, ChangeColumns}; +use tempdir::TempDir; #[inline] fn db_path(path: &Path) -> PathBuf { @@ -114,8 +110,8 @@ fn one_simple_migration() { let tempdir = TempDir::new("").unwrap(); let db_path = db_path(tempdir.path()); let mut manager = Manager::new(Config::default()); - make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]); - let expected = map![vec![0x11] => vec![0x22], vec![1, 0x11] => vec![1, 0x22]]; + make_db(&db_path, btreemap![vec![] => vec![], vec![1] => vec![1]]); + let expected = btreemap![vec![0x11] => vec![0x22], vec![1, 0x11] => vec![1, 0x22]]; manager.add_migration(Migration0).unwrap(); let end_path = manager.execute(&db_path, 0).unwrap(); @@ -129,7 +125,7 @@ fn no_migration_needed() { let tempdir = TempDir::new("").unwrap(); let db_path = db_path(tempdir.path()); let mut manager = Manager::new(Config::default()); - make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]); + make_db(&db_path, btreemap![vec![] => vec![], vec![1] => vec![1]]); manager.add_migration(Migration0).unwrap(); manager.execute(&db_path, 1).unwrap(); @@ -141,7 +137,7 @@ fn wrong_adding_order() { let tempdir = TempDir::new("").unwrap(); let db_path = db_path(tempdir.path()); let mut manager = Manager::new(Config::default()); - make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]); + make_db(&db_path, btreemap![vec![] => vec![], vec![1] => vec![1]]); manager.add_migration(Migration1).unwrap(); manager.add_migration(Migration0).unwrap(); @@ -152,8 +148,8 @@ fn multiple_migrations() { let tempdir = TempDir::new("").unwrap(); let db_path = db_path(tempdir.path()); let mut manager = Manager::new(Config::default()); - make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]); - let expected = map![vec![0x11] => vec![], vec![1, 0x11] => vec![]]; + make_db(&db_path, btreemap![vec![] => vec![], vec![1] => vec![1]]); + let expected = btreemap![vec![0x11] => vec![], vec![1, 0x11] => vec![]]; manager.add_migration(Migration0).unwrap(); manager.add_migration(Migration1).unwrap(); @@ -167,8 +163,8 @@ fn second_migration() { let tempdir = TempDir::new("").unwrap(); let db_path = db_path(tempdir.path()); let mut manager = Manager::new(Config::default()); - make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]); - let expected = map![vec![] => vec![], vec![1] => vec![]]; + make_db(&db_path, btreemap![vec![] => vec![], vec![1] => vec![1]]); + let expected = btreemap![vec![] => vec![], vec![1] => vec![]]; manager.add_migration(Migration0).unwrap(); manager.add_migration(Migration1).unwrap(); @@ -182,8 +178,8 @@ fn first_and_noop_migration() { let tempdir = TempDir::new("").unwrap(); let db_path = db_path(tempdir.path()); let mut manager = Manager::new(Config::default()); - make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]); - let expected = map![vec![0x11] => vec![0x22], vec![1, 0x11] => vec![1, 0x22]]; + make_db(&db_path, btreemap![vec![] => vec![], vec![1] => vec![1]]); + let expected = btreemap![vec![0x11] => vec![0x22], vec![1, 0x11] => vec![1, 0x22]]; manager.add_migration(Migration0).expect("Migration0 can be added"); let end_path = manager.execute(&db_path, 0).expect("Migration0 runs clean"); @@ -196,8 +192,8 @@ fn noop_and_second_migration() { let tempdir = TempDir::new("").unwrap(); let db_path = db_path(tempdir.path()); let mut manager = Manager::new(Config::default()); - make_db(&db_path, map![vec![] => vec![], vec![1] => vec![1]]); - let expected = map![vec![] => vec![], vec![1] => vec![]]; + make_db(&db_path, btreemap![vec![] => vec![], vec![1] => vec![1]]); + let expected = btreemap![vec![] => vec![], vec![1] => vec![]]; manager.add_migration(Migration1).unwrap(); let end_path = manager.execute(&db_path, 0).unwrap(); diff --git a/util/network-devp2p/Cargo.toml b/util/network-devp2p/Cargo.toml index df8fe1d9130..541dc026578 100644 --- a/util/network-devp2p/Cargo.toml +++ b/util/network-devp2p/Cargo.toml @@ -8,36 +8,36 @@ authors = ["Parity Technologies "] edition = "2018" [dependencies] -log = "0.4" -mio = "0.6.8" -bytes = "0.4" -rand = "0.7" -tiny-keccak = "1.4" -slab = "0.2" -igd = "0.10.0" -libc = "0.2.7" -parking_lot = "0.10.0" ansi_term = "0.11" -rustc-hex = "1.0" +bytes = "0.4" ethcore-io = { path = "../io", features = ["mio"] } -parity-bytes = "0.1" -parity-crypto = { version = "0.4.2", features = ["publickey"] } -network = { package = "ethcore-network", path = "../network" } ethereum-types = "0.8.0" -rlp = "0.4.0" -parity-path = "0.1" +igd = "0.10.0" ipnetwork = "0.12.6" keccak-hash = "0.4.0" +libc = "0.2.7" +log = "0.4" +lru-cache = "0.1" +mio = "0.6.8" +natpmp = "0.2" +network = { package = "ethcore-network", path = "../network" } +parity-bytes = "0.1" +parity-crypto = { version = "0.5.0", features = ["publickey"] } +parity-path = "0.1" parity-snappy = "0.1" +parking_lot = "0.10.0" +rand = "0.7" +rlp = "0.4.0" serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" -lru-cache = "0.1" -natpmp = "0.2" +slab = "0.2" +tiny-keccak = "1.4" [dev-dependencies] +assert_matches = "1.2" env_logger = "0.5" +hex-literal = "0.2.1" tempdir = "0.3" -assert_matches = "1.2" [features] default = [] diff --git a/util/network-devp2p/src/discovery.rs b/util/network-devp2p/src/discovery.rs index 7df03673005..a468b04fc3e 100644 --- a/util/network-devp2p/src/discovery.rs +++ b/util/network-devp2p/src/discovery.rs @@ -899,12 +899,10 @@ mod tests { use std::net::{IpAddr, Ipv4Addr}; use std::str::FromStr; - use rustc_hex::FromHex; - + use hex_literal::hex; use parity_crypto::publickey::{Generator, Random}; use crate::node_table::{Node, NodeEndpoint, NodeId}; - use super::*; #[test] @@ -926,7 +924,7 @@ mod tests { #[test] fn ping_queue() { - let key = Random.generate().unwrap(); + let key = Random.generate(); let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40445").unwrap(), udp_port: 40445 }; let mut discovery = Discovery::new(&key, ep.clone(), IpFilter::default()); @@ -947,7 +945,7 @@ mod tests { #[test] fn discovery() { let mut discovery_handlers = (0..5).map(|i| { - let key = Random.generate().unwrap(); + let key = Random.generate(); let ep = NodeEndpoint { address: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 41000 + i), udp_port: 41000 + i, @@ -997,7 +995,7 @@ mod tests { #[test] fn removes_expired() { - let key = Random.generate().unwrap(); + let key = Random.generate(); let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40446").unwrap(), udp_port: 40447 }; let discovery = Discovery::new(&key, ep.clone(), IpFilter::default()); @@ -1042,7 +1040,7 @@ mod tests { let from = SocketAddr::from_str("99.99.99.99:40445").unwrap(); // FIND_NODE times out because it doesn't receive k results. - let key = Random.generate().unwrap(); + let key = Random.generate(); discovery.send_find_node(&node_entries[100], key.public()).unwrap(); for payload in Discovery::prepare_neighbours_packets(&node_entries[101..116]) { let packet = assemble_packet(PACKET_NEIGHBOURS, &payload, &key.secret()).unwrap(); @@ -1089,7 +1087,7 @@ mod tests { fn find_nearest_saturated() { use super::*; - let key = Random.generate().unwrap(); + let key = Random.generate(); let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40447").unwrap(), udp_port: 40447 }; let mut discovery = Discovery::new(&key, ep.clone(), IpFilter::default()); @@ -1191,74 +1189,74 @@ mod tests { #[test] fn packets() { - let key = Random.generate().unwrap(); + let key = Random.generate(); let ep = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40449").unwrap(), udp_port: 40449 }; let mut discovery = Discovery::new(&key, ep.clone(), IpFilter::default()); discovery.check_timestamps = false; let from = SocketAddr::from_str("99.99.99.99:40445").unwrap(); - let packet = "\ - e9614ccfd9fc3e74360018522d30e1419a143407ffcce748de3e22116b7e8dc92ff74788c0b6663a\ - aa3d67d641936511c8f8d6ad8698b820a7cf9e1be7155e9a241f556658c55428ec0563514365799a\ - 4be2be5a685a80971ddcfa80cb422cdd0101ec04cb847f000001820cfa8215a8d790000000000000\ - 000000000000000000018208ae820d058443b9a3550102\ - ".from_hex().unwrap(); + let packet = hex!(" + e9614ccfd9fc3e74360018522d30e1419a143407ffcce748de3e22116b7e8dc92ff74788c0b6663a + aa3d67d641936511c8f8d6ad8698b820a7cf9e1be7155e9a241f556658c55428ec0563514365799a + 4be2be5a685a80971ddcfa80cb422cdd0101ec04cb847f000001820cfa8215a8d790000000000000 + 000000000000000000018208ae820d058443b9a3550102 + ").to_vec(); let _ = discovery.on_packet(&packet, from.clone()).expect("packet to be ok"); - let packet = "\ - 577be4349c4dd26768081f58de4c6f375a7a22f3f7adda654d1428637412c3d7fe917cadc56d4e5e\ - 7ffae1dbe3efffb9849feb71b262de37977e7c7a44e677295680e9e38ab26bee2fcbae207fba3ff3\ - d74069a50b902a82c9903ed37cc993c50001f83e82022bd79020010db83c4d001500000000abcdef\ - 12820cfa8215a8d79020010db885a308d313198a2e037073488208ae82823a8443b9a355c5010203\ - 040531b9019afde696e582a78fa8d95ea13ce3297d4afb8ba6433e4154caa5ac6431af1b80ba7602\ - 3fa4090c408f6b4bc3701562c031041d4702971d102c9ab7fa5eed4cd6bab8f7af956f7d565ee191\ - 7084a95398b6a21eac920fe3dd1345ec0a7ef39367ee69ddf092cbfe5b93e5e568ebc491983c09c7\ - 6d922dc3\ - ".from_hex().unwrap(); + let packet = hex!(" + 577be4349c4dd26768081f58de4c6f375a7a22f3f7adda654d1428637412c3d7fe917cadc56d4e5e + 7ffae1dbe3efffb9849feb71b262de37977e7c7a44e677295680e9e38ab26bee2fcbae207fba3ff3 + d74069a50b902a82c9903ed37cc993c50001f83e82022bd79020010db83c4d001500000000abcdef + 12820cfa8215a8d79020010db885a308d313198a2e037073488208ae82823a8443b9a355c5010203 + 040531b9019afde696e582a78fa8d95ea13ce3297d4afb8ba6433e4154caa5ac6431af1b80ba7602 + 3fa4090c408f6b4bc3701562c031041d4702971d102c9ab7fa5eed4cd6bab8f7af956f7d565ee191 + 7084a95398b6a21eac920fe3dd1345ec0a7ef39367ee69ddf092cbfe5b93e5e568ebc491983c09c7 + 6d922dc3 + ").to_vec(); let _ = discovery.on_packet(&packet, from.clone()).expect("packet to be ok"); - let packet = "\ - 09b2428d83348d27cdf7064ad9024f526cebc19e4958f0fdad87c15eb598dd61d08423e0bf66b206\ - 9869e1724125f820d851c136684082774f870e614d95a2855d000f05d1648b2d5945470bc187c2d2\ - 216fbe870f43ed0909009882e176a46b0102f846d79020010db885a308d313198a2e037073488208\ - ae82823aa0fbc914b16819237dcd8801d7e53f69e9719adecb3cc0e790c57e91ca4461c9548443b9\ + let packet = hex!(" + 09b2428d83348d27cdf7064ad9024f526cebc19e4958f0fdad87c15eb598dd61d08423e0bf66b206 + 9869e1724125f820d851c136684082774f870e614d95a2855d000f05d1648b2d5945470bc187c2d2 + 216fbe870f43ed0909009882e176a46b0102f846d79020010db885a308d313198a2e037073488208 + ae82823aa0fbc914b16819237dcd8801d7e53f69e9719adecb3cc0e790c57e91ca4461c9548443b9 a355c6010203c2040506a0c969a58f6f9095004c0177a6b47f451530cab38966a25cca5cb58f0555 - 42124e\ - ".from_hex().unwrap(); + 42124e + ").to_vec(); let _ = discovery.on_packet(&packet, from.clone()).expect("packet to be ok"); - let packet = "\ - c7c44041b9f7c7e41934417ebac9a8e1a4c6298f74553f2fcfdcae6ed6fe53163eb3d2b52e39fe91\ - 831b8a927bf4fc222c3902202027e5e9eb812195f95d20061ef5cd31d502e47ecb61183f74a504fe\ - 04c51e73df81f25c4d506b26db4517490103f84eb840ca634cae0d49acb401d8a4c6b6fe8c55b70d\ - 115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be0081290476\ - 7bf5ccd1fc7f8443b9a35582999983999999280dc62cc8255c73471e0a61da0c89acdc0e035e260a\ - dd7fc0c04ad9ebf3919644c91cb247affc82b69bd2ca235c71eab8e49737c937a2c396\ - ".from_hex().unwrap(); + let packet = hex!(" + c7c44041b9f7c7e41934417ebac9a8e1a4c6298f74553f2fcfdcae6ed6fe53163eb3d2b52e39fe91 + 831b8a927bf4fc222c3902202027e5e9eb812195f95d20061ef5cd31d502e47ecb61183f74a504fe + 04c51e73df81f25c4d506b26db4517490103f84eb840ca634cae0d49acb401d8a4c6b6fe8c55b70d + 115bf400769cc1400f3258cd31387574077f301b421bc84df7266c44e9e6d569fc56be0081290476 + 7bf5ccd1fc7f8443b9a35582999983999999280dc62cc8255c73471e0a61da0c89acdc0e035e260a + dd7fc0c04ad9ebf3919644c91cb247affc82b69bd2ca235c71eab8e49737c937a2c396 + ").to_vec(); let _ = discovery.on_packet(&packet, from.clone()).expect("packet to be ok"); - let packet = "\ - c679fc8fe0b8b12f06577f2e802d34f6fa257e6137a995f6f4cbfc9ee50ed3710faf6e66f932c4c8\ - d81d64343f429651328758b47d3dbc02c4042f0fff6946a50f4a49037a72bb550f3a7872363a83e1\ - b9ee6469856c24eb4ef80b7535bcf99c0004f9015bf90150f84d846321163782115c82115db84031\ - 55e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa8291\ - 15d224c523596b401065a97f74010610fce76382c0bf32f84984010203040101b840312c55512422\ - cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e82\ - 9f04c2d314fc2d4e255e0d3bc08792b069dbf8599020010db83c4d001500000000abcdef12820d05\ - 820d05b84038643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2\ - d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aacf8599020010db885a308d3\ - 13198a2e037073488203e78203e8b8408dcab8618c3253b558d459da53bd8fa68935a719aff8b811\ - 197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73\ - 8443b9a355010203b525a138aa34383fec3d2719a0\ - ".from_hex().unwrap(); + let packet = hex!(" + c679fc8fe0b8b12f06577f2e802d34f6fa257e6137a995f6f4cbfc9ee50ed3710faf6e66f932c4c8 + d81d64343f429651328758b47d3dbc02c4042f0fff6946a50f4a49037a72bb550f3a7872363a83e1 + b9ee6469856c24eb4ef80b7535bcf99c0004f9015bf90150f84d846321163782115c82115db84031 + 55e1427f85f10a5c9a7755877748041af1bcd8d474ec065eb33df57a97babf54bfd2103575fa8291 + 15d224c523596b401065a97f74010610fce76382c0bf32f84984010203040101b840312c55512422 + cf9b8a4097e9a6ad79402e87a15ae909a4bfefa22398f03d20951933beea1e4dfa6f968212385e82 + 9f04c2d314fc2d4e255e0d3bc08792b069dbf8599020010db83c4d001500000000abcdef12820d05 + 820d05b84038643200b172dcfef857492156971f0e6aa2c538d8b74010f8e140811d53b98c765dd2 + d96126051913f44582e8c199ad7c6d6819e9a56483f637feaac9448aacf8599020010db885a308d3 + 13198a2e037073488203e78203e8b8408dcab8618c3253b558d459da53bd8fa68935a719aff8b811 + 197101a4b2b47dd2d47295286fc00cc081bb542d760717d1bdd6bec2c37cd72eca367d6dd3b9df73 + 8443b9a355010203b525a138aa34383fec3d2719a0 + ").to_vec(); let _ = discovery.on_packet(&packet, from.clone()).expect("packet to be ok"); } #[test] fn test_ping() { - let key1 = Random.generate().unwrap(); - let key2 = Random.generate().unwrap(); - let key3 = Random.generate().unwrap(); + let key1 = Random.generate(); + let key2 = Random.generate(); + let key3 = Random.generate(); let ep1 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40344").unwrap(), udp_port: 40344 }; let ep2 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40345").unwrap(), udp_port: 40345 }; let ep3 = NodeEndpoint { address: SocketAddr::from_str("127.0.0.1:40346").unwrap(), udp_port: 40345 }; diff --git a/util/network-devp2p/src/handshake.rs b/util/network-devp2p/src/handshake.rs index 2b7ce03e878..b869db41dd5 100644 --- a/util/network-devp2p/src/handshake.rs +++ b/util/network-devp2p/src/handshake.rs @@ -83,20 +83,20 @@ const ECIES_OVERHEAD: usize = 113; impl Handshake { /// Create a new handshake object - pub fn new(token: StreamToken, id: Option<&NodeId>, socket: TcpStream, nonce: &H256) -> Result { - Ok(Handshake { + pub fn new(token: StreamToken, id: Option<&NodeId>, socket: TcpStream, nonce: &H256) -> Handshake { + Handshake { id: if let Some(id) = id { *id } else { NodeId::default() }, connection: Connection::new(token, socket), originated: false, state: HandshakeState::New, - ecdhe: Random.generate()?, + ecdhe: Random.generate(), nonce: *nonce, remote_ephemeral: Public::default(), remote_nonce: H256::zero(), remote_version: PROTOCOL_VERSION, auth_cipher: Bytes::new(), ack_cipher: Bytes::new(), - }) + } } /// Start a handshake @@ -323,11 +323,10 @@ impl Handshake { mod test { use std::str::FromStr; + use ethcore_io::*; use ethereum_types::{H256, H512}; + use hex_literal::hex; use mio::tcp::TcpStream; - use rustc_hex::FromHex; - - use ethcore_io::*; use parity_crypto::publickey::Public; use super::*; @@ -358,7 +357,7 @@ mod test { let addr = "127.0.0.1:50556".parse().unwrap(); let socket = TcpStream::connect(&addr).unwrap(); let nonce = H256::zero(); - Handshake::new(0, to, socket, &nonce).unwrap() + Handshake::new(0, to, socket, &nonce) } fn test_io() -> IoContext { @@ -370,16 +369,16 @@ mod test { let mut h = create_handshake(None); let secret = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291".parse().unwrap(); let auth = - "\ - 048ca79ad18e4b0659fab4853fe5bc58eb83992980f4c9cc147d2aa31532efd29a3d3dc6a3d89eaf\ - 913150cfc777ce0ce4af2758bf4810235f6e6ceccfee1acc6b22c005e9e3a49d6448610a58e98744\ - ba3ac0399e82692d67c1f58849050b3024e21a52c9d3b01d871ff5f210817912773e610443a9ef14\ - 2e91cdba0bd77b5fdf0769b05671fc35f83d83e4d3b0b000c6b2a1b1bba89e0fc51bf4e460df3105\ - c444f14be226458940d6061c296350937ffd5e3acaceeaaefd3c6f74be8e23e0f45163cc7ebd7622\ - 0f0128410fd05250273156d548a414444ae2f7dea4dfca2d43c057adb701a715bf59f6fb66b2d1d2\ - 0f2c703f851cbf5ac47396d9ca65b6260bd141ac4d53e2de585a73d1750780db4c9ee4cd4d225173\ - a4592ee77e2bd94d0be3691f3b406f9bba9b591fc63facc016bfa8\ - ".from_hex().unwrap(); + hex!(" + 048ca79ad18e4b0659fab4853fe5bc58eb83992980f4c9cc147d2aa31532efd29a3d3dc6a3d89eaf + 913150cfc777ce0ce4af2758bf4810235f6e6ceccfee1acc6b22c005e9e3a49d6448610a58e98744 + ba3ac0399e82692d67c1f58849050b3024e21a52c9d3b01d871ff5f210817912773e610443a9ef14 + 2e91cdba0bd77b5fdf0769b05671fc35f83d83e4d3b0b000c6b2a1b1bba89e0fc51bf4e460df3105 + c444f14be226458940d6061c296350937ffd5e3acaceeaaefd3c6f74be8e23e0f45163cc7ebd7622 + 0f0128410fd05250273156d548a414444ae2f7dea4dfca2d43c057adb701a715bf59f6fb66b2d1d2 + 0f2c703f851cbf5ac47396d9ca65b6260bd141ac4d53e2de585a73d1750780db4c9ee4cd4d225173 + a4592ee77e2bd94d0be3691f3b406f9bba9b591fc63facc016bfa8 + ").to_vec(); h.read_auth(&test_io(), &secret, &auth).unwrap(); assert_eq!(h.state, super::HandshakeState::StartSession); @@ -391,19 +390,19 @@ mod test { let mut h = create_handshake(None); let secret = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291".parse().unwrap(); let auth = - "\ - 01b304ab7578555167be8154d5cc456f567d5ba302662433674222360f08d5f1534499d3678b513b\ - 0fca474f3a514b18e75683032eb63fccb16c156dc6eb2c0b1593f0d84ac74f6e475f1b8d56116b84\ - 9634a8c458705bf83a626ea0384d4d7341aae591fae42ce6bd5c850bfe0b999a694a49bbbaf3ef6c\ - da61110601d3b4c02ab6c30437257a6e0117792631a4b47c1d52fc0f8f89caadeb7d02770bf999cc\ - 147d2df3b62e1ffb2c9d8c125a3984865356266bca11ce7d3a688663a51d82defaa8aad69da39ab6\ - d5470e81ec5f2a7a47fb865ff7cca21516f9299a07b1bc63ba56c7a1a892112841ca44b6e0034dee\ - 70c9adabc15d76a54f443593fafdc3b27af8059703f88928e199cb122362a4b35f62386da7caad09\ - c001edaeb5f8a06d2b26fb6cb93c52a9fca51853b68193916982358fe1e5369e249875bb8d0d0ec3\ - 6f917bc5e1eafd5896d46bd61ff23f1a863a8a8dcd54c7b109b771c8e61ec9c8908c733c0263440e\ - 2aa067241aaa433f0bb053c7b31a838504b148f570c0ad62837129e547678c5190341e4f1693956c\ - 3bf7678318e2d5b5340c9e488eefea198576344afbdf66db5f51204a6961a63ce072c8926c\ - ".from_hex().unwrap(); + hex!(" + 01b304ab7578555167be8154d5cc456f567d5ba302662433674222360f08d5f1534499d3678b513b + 0fca474f3a514b18e75683032eb63fccb16c156dc6eb2c0b1593f0d84ac74f6e475f1b8d56116b84 + 9634a8c458705bf83a626ea0384d4d7341aae591fae42ce6bd5c850bfe0b999a694a49bbbaf3ef6c + da61110601d3b4c02ab6c30437257a6e0117792631a4b47c1d52fc0f8f89caadeb7d02770bf999cc + 147d2df3b62e1ffb2c9d8c125a3984865356266bca11ce7d3a688663a51d82defaa8aad69da39ab6 + d5470e81ec5f2a7a47fb865ff7cca21516f9299a07b1bc63ba56c7a1a892112841ca44b6e0034dee + 70c9adabc15d76a54f443593fafdc3b27af8059703f88928e199cb122362a4b35f62386da7caad09 + c001edaeb5f8a06d2b26fb6cb93c52a9fca51853b68193916982358fe1e5369e249875bb8d0d0ec3 + 6f917bc5e1eafd5896d46bd61ff23f1a863a8a8dcd54c7b109b771c8e61ec9c8908c733c0263440e + 2aa067241aaa433f0bb053c7b31a838504b148f570c0ad62837129e547678c5190341e4f1693956c + 3bf7678318e2d5b5340c9e488eefea198576344afbdf66db5f51204a6961a63ce072c8926c + ").to_vec(); h.read_auth(&test_io(), &secret, &auth[0..super::V4_AUTH_PACKET_SIZE]).unwrap(); assert_eq!(h.state, super::HandshakeState::ReadingAuthEip8); @@ -417,20 +416,19 @@ mod test { let mut h = create_handshake(None); let secret = "b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291".parse().unwrap(); let auth = - "\ - 01b8044c6c312173685d1edd268aa95e1d495474c6959bcdd10067ba4c9013df9e40ff45f5bfd6f7\ - 2471f93a91b493f8e00abc4b80f682973de715d77ba3a005a242eb859f9a211d93a347fa64b597bf\ - 280a6b88e26299cf263b01b8dfdb712278464fd1c25840b995e84d367d743f66c0e54a586725b7bb\ - f12acca27170ae3283c1073adda4b6d79f27656993aefccf16e0d0409fe07db2dc398a1b7e8ee93b\ - cd181485fd332f381d6a050fba4c7641a5112ac1b0b61168d20f01b479e19adf7fdbfa0905f63352\ - bfc7e23cf3357657455119d879c78d3cf8c8c06375f3f7d4861aa02a122467e069acaf513025ff19\ - 6641f6d2810ce493f51bee9c966b15c5043505350392b57645385a18c78f14669cc4d960446c1757\ - 1b7c5d725021babbcd786957f3d17089c084907bda22c2b2675b4378b114c601d858802a55345a15\ - 116bc61da4193996187ed70d16730e9ae6b3bb8787ebcaea1871d850997ddc08b4f4ea668fbf3740\ - 7ac044b55be0908ecb94d4ed172ece66fd31bfdadf2b97a8bc690163ee11f5b575a4b44e36e2bfb2\ - f0fce91676fd64c7773bac6a003f481fddd0bae0a1f31aa27504e2a533af4cef3b623f4791b2cca6\ - d490\ - ".from_hex().unwrap(); + hex!(" + 01b8044c6c312173685d1edd268aa95e1d495474c6959bcdd10067ba4c9013df9e40ff45f5bfd6f7 + 2471f93a91b493f8e00abc4b80f682973de715d77ba3a005a242eb859f9a211d93a347fa64b597bf + 280a6b88e26299cf263b01b8dfdb712278464fd1c25840b995e84d367d743f66c0e54a586725b7bb + f12acca27170ae3283c1073adda4b6d79f27656993aefccf16e0d0409fe07db2dc398a1b7e8ee93b + cd181485fd332f381d6a050fba4c7641a5112ac1b0b61168d20f01b479e19adf7fdbfa0905f63352 + bfc7e23cf3357657455119d879c78d3cf8c8c06375f3f7d4861aa02a122467e069acaf513025ff19 + 6641f6d2810ce493f51bee9c966b15c5043505350392b57645385a18c78f14669cc4d960446c1757 + 1b7c5d725021babbcd786957f3d17089c084907bda22c2b2675b4378b114c601d858802a55345a15 + 116bc61da4193996187ed70d16730e9ae6b3bb8787ebcaea1871d850997ddc08b4f4ea668fbf3740 + 7ac044b55be0908ecb94d4ed172ece66fd31bfdadf2b97a8bc690163ee11f5b575a4b44e36e2bfb2 + f0fce91676fd64c7773bac6a003f481fddd0bae0a1f31aa27504e2a533af4cef3b623f4791b2cca6 + d490").to_vec(); h.read_auth(&test_io(), &secret, &auth[0..super::V4_AUTH_PACKET_SIZE]).unwrap(); assert_eq!(h.state, super::HandshakeState::ReadingAuthEip8); @@ -448,14 +446,14 @@ mod test { let mut h = create_handshake(Some(&remote)); let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".parse().unwrap(); let ack = - "\ - 049f8abcfa9c0dc65b982e98af921bc0ba6e4243169348a236abe9df5f93aa69d99cadddaa387662\ - b0ff2c08e9006d5a11a278b1b3331e5aaabf0a32f01281b6f4ede0e09a2d5f585b26513cb794d963\ - 5a57563921c04a9090b4f14ee42be1a5461049af4ea7a7f49bf4c97a352d39c8d02ee4acc416388c\ - 1c66cec761d2bc1c72da6ba143477f049c9d2dde846c252c111b904f630ac98e51609b3b1f58168d\ - dca6505b7196532e5f85b259a20c45e1979491683fee108e9660edbf38f3add489ae73e3dda2c71b\ - d1497113d5c755e942d1\ - ".from_hex().unwrap(); + hex!(" + 049f8abcfa9c0dc65b982e98af921bc0ba6e4243169348a236abe9df5f93aa69d99cadddaa387662 + b0ff2c08e9006d5a11a278b1b3331e5aaabf0a32f01281b6f4ede0e09a2d5f585b26513cb794d963 + 5a57563921c04a9090b4f14ee42be1a5461049af4ea7a7f49bf4c97a352d39c8d02ee4acc416388c + 1c66cec761d2bc1c72da6ba143477f049c9d2dde846c252c111b904f630ac98e51609b3b1f58168d + dca6505b7196532e5f85b259a20c45e1979491683fee108e9660edbf38f3add489ae73e3dda2c71b + d1497113d5c755e942d1 + ").to_vec(); h.read_ack(&secret, &ack).unwrap(); assert_eq!(h.state, super::HandshakeState::StartSession); @@ -468,21 +466,21 @@ mod test { let mut h = create_handshake(Some(&remote)); let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".parse().unwrap(); let ack = - "\ - 01ea0451958701280a56482929d3b0757da8f7fbe5286784beead59d95089c217c9b917788989470\ - b0e330cc6e4fb383c0340ed85fab836ec9fb8a49672712aeabbdfd1e837c1ff4cace34311cd7f4de\ - 05d59279e3524ab26ef753a0095637ac88f2b499b9914b5f64e143eae548a1066e14cd2f4bd7f814\ - c4652f11b254f8a2d0191e2f5546fae6055694aed14d906df79ad3b407d94692694e259191cde171\ - ad542fc588fa2b7333313d82a9f887332f1dfc36cea03f831cb9a23fea05b33deb999e85489e645f\ - 6aab1872475d488d7bd6c7c120caf28dbfc5d6833888155ed69d34dbdc39c1f299be1057810f34fb\ - e754d021bfca14dc989753d61c413d261934e1a9c67ee060a25eefb54e81a4d14baff922180c395d\ - 3f998d70f46f6b58306f969627ae364497e73fc27f6d17ae45a413d322cb8814276be6ddd13b885b\ - 201b943213656cde498fa0e9ddc8e0b8f8a53824fbd82254f3e2c17e8eaea009c38b4aa0a3f306e8\ - 797db43c25d68e86f262e564086f59a2fc60511c42abfb3057c247a8a8fe4fb3ccbadde17514b7ac\ - 8000cdb6a912778426260c47f38919a91f25f4b5ffb455d6aaaf150f7e5529c100ce62d6d92826a7\ - 1778d809bdf60232ae21ce8a437eca8223f45ac37f6487452ce626f549b3b5fdee26afd2072e4bc7\ - 5833c2464c805246155289f4\ - ".from_hex().unwrap(); + hex!(" + 01ea0451958701280a56482929d3b0757da8f7fbe5286784beead59d95089c217c9b917788989470 + b0e330cc6e4fb383c0340ed85fab836ec9fb8a49672712aeabbdfd1e837c1ff4cace34311cd7f4de + 05d59279e3524ab26ef753a0095637ac88f2b499b9914b5f64e143eae548a1066e14cd2f4bd7f814 + c4652f11b254f8a2d0191e2f5546fae6055694aed14d906df79ad3b407d94692694e259191cde171 + ad542fc588fa2b7333313d82a9f887332f1dfc36cea03f831cb9a23fea05b33deb999e85489e645f + 6aab1872475d488d7bd6c7c120caf28dbfc5d6833888155ed69d34dbdc39c1f299be1057810f34fb + e754d021bfca14dc989753d61c413d261934e1a9c67ee060a25eefb54e81a4d14baff922180c395d + 3f998d70f46f6b58306f969627ae364497e73fc27f6d17ae45a413d322cb8814276be6ddd13b885b + 201b943213656cde498fa0e9ddc8e0b8f8a53824fbd82254f3e2c17e8eaea009c38b4aa0a3f306e8 + 797db43c25d68e86f262e564086f59a2fc60511c42abfb3057c247a8a8fe4fb3ccbadde17514b7ac + 8000cdb6a912778426260c47f38919a91f25f4b5ffb455d6aaaf150f7e5529c100ce62d6d92826a7 + 1778d809bdf60232ae21ce8a437eca8223f45ac37f6487452ce626f549b3b5fdee26afd2072e4bc7 + 5833c2464c805246155289f4 + ").to_vec(); h.read_ack(&secret, &ack[0..super::V4_ACK_PACKET_SIZE]).unwrap(); assert_eq!(h.state, super::HandshakeState::ReadingAckEip8); @@ -497,21 +495,21 @@ mod test { let mut h = create_handshake(Some(&remote)); let secret = "49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee".parse().unwrap(); let ack = - "\ - 01f004076e58aae772bb101ab1a8e64e01ee96e64857ce82b1113817c6cdd52c09d26f7b90981cd7\ - ae835aeac72e1573b8a0225dd56d157a010846d888dac7464baf53f2ad4e3d584531fa203658fab0\ - 3a06c9fd5e35737e417bc28c1cbf5e5dfc666de7090f69c3b29754725f84f75382891c561040ea1d\ - dc0d8f381ed1b9d0d4ad2a0ec021421d847820d6fa0ba66eaf58175f1b235e851c7e2124069fbc20\ - 2888ddb3ac4d56bcbd1b9b7eab59e78f2e2d400905050f4a92dec1c4bdf797b3fc9b2f8e84a482f3\ - d800386186712dae00d5c386ec9387a5e9c9a1aca5a573ca91082c7d68421f388e79127a5177d4f8\ - 590237364fd348c9611fa39f78dcdceee3f390f07991b7b47e1daa3ebcb6ccc9607811cb17ce51f1\ - c8c2c5098dbdd28fca547b3f58c01a424ac05f869f49c6a34672ea2cbbc558428aa1fe48bbfd6115\ - 8b1b735a65d99f21e70dbc020bfdface9f724a0d1fb5895db971cc81aa7608baa0920abb0a565c9c\ - 436e2fd13323428296c86385f2384e408a31e104670df0791d93e743a3a5194ee6b076fb6323ca59\ - 3011b7348c16cf58f66b9633906ba54a2ee803187344b394f75dd2e663a57b956cb830dd7a908d4f\ - 39a2336a61ef9fda549180d4ccde21514d117b6c6fd07a9102b5efe710a32af4eeacae2cb3b1dec0\ - 35b9593b48b9d3ca4c13d245d5f04169b0b1\ - ".from_hex().unwrap(); + hex!(" + 01f004076e58aae772bb101ab1a8e64e01ee96e64857ce82b1113817c6cdd52c09d26f7b90981cd7 + ae835aeac72e1573b8a0225dd56d157a010846d888dac7464baf53f2ad4e3d584531fa203658fab0 + 3a06c9fd5e35737e417bc28c1cbf5e5dfc666de7090f69c3b29754725f84f75382891c561040ea1d + dc0d8f381ed1b9d0d4ad2a0ec021421d847820d6fa0ba66eaf58175f1b235e851c7e2124069fbc20 + 2888ddb3ac4d56bcbd1b9b7eab59e78f2e2d400905050f4a92dec1c4bdf797b3fc9b2f8e84a482f3 + d800386186712dae00d5c386ec9387a5e9c9a1aca5a573ca91082c7d68421f388e79127a5177d4f8 + 590237364fd348c9611fa39f78dcdceee3f390f07991b7b47e1daa3ebcb6ccc9607811cb17ce51f1 + c8c2c5098dbdd28fca547b3f58c01a424ac05f869f49c6a34672ea2cbbc558428aa1fe48bbfd6115 + 8b1b735a65d99f21e70dbc020bfdface9f724a0d1fb5895db971cc81aa7608baa0920abb0a565c9c + 436e2fd13323428296c86385f2384e408a31e104670df0791d93e743a3a5194ee6b076fb6323ca59 + 3011b7348c16cf58f66b9633906ba54a2ee803187344b394f75dd2e663a57b956cb830dd7a908d4f + 39a2336a61ef9fda549180d4ccde21514d117b6c6fd07a9102b5efe710a32af4eeacae2cb3b1dec0 + 35b9593b48b9d3ca4c13d245d5f04169b0b1 + ").to_vec(); h.read_ack(&secret, &ack[0..super::V4_ACK_PACKET_SIZE]).unwrap(); assert_eq!(h.state, super::HandshakeState::ReadingAckEip8); diff --git a/util/network-devp2p/src/host.rs b/util/network-devp2p/src/host.rs index e61faa25104..103c76f7a54 100644 --- a/util/network-devp2p/src/host.rs +++ b/util/network-devp2p/src/host.rs @@ -290,7 +290,7 @@ impl Host { } else { config.config_path.clone().and_then(|ref p| load_key(Path::new(&p))) .map_or_else(|| { - let key = Random.generate().expect("Error generating random key pair"); + let key = Random.generate(); if let Some(path) = config.config_path.clone() { save_key(Path::new(&path), key.secret()); } diff --git a/util/network-devp2p/src/ip_utils.rs b/util/network-devp2p/src/ip_utils.rs index 579bf084138..630072b3928 100644 --- a/util/network-devp2p/src/ip_utils.rs +++ b/util/network-devp2p/src/ip_utils.rs @@ -516,10 +516,10 @@ fn ipv4_special_purpose() { #[test] fn ipv4_benchmarking() { - assert!(!Ipv4Addr::new(198, 17, 255, 255).is_benchmarking()); - assert!(Ipv4Addr::new(198, 18, 0, 0).is_benchmarking()); - assert!(Ipv4Addr::new(198, 19, 255, 255).is_benchmarking()); - assert!(!Ipv4Addr::new(198, 20, 0, 0).is_benchmarking()); + assert!(!SocketAddrExt::is_benchmarking(&Ipv4Addr::new(198, 17, 255, 255))); + assert!(SocketAddrExt::is_benchmarking(&Ipv4Addr::new(198, 18, 0, 0))); + assert!(SocketAddrExt::is_benchmarking(&Ipv4Addr::new(198, 19, 255, 255))); + assert!(!SocketAddrExt::is_benchmarking(&Ipv4Addr::new(198, 20, 0, 0))); } #[test] diff --git a/util/network-devp2p/src/session.rs b/util/network-devp2p/src/session.rs index 07259ecf8ec..b6369548dd5 100644 --- a/util/network-devp2p/src/session.rs +++ b/util/network-devp2p/src/session.rs @@ -110,7 +110,7 @@ impl Session { nonce: &H256, host: &HostInfo) -> Result where Message: Send + Clone + Sync + 'static { let originated = id.is_some(); - let mut handshake = Handshake::new(token, id, socket, nonce).expect("Can't create handshake"); + let mut handshake = Handshake::new(token, id, socket, nonce); let local_addr = handshake.connection.local_addr_str(); handshake.start(io, host, originated)?; Ok(Session { diff --git a/util/network-devp2p/tests/tests.rs b/util/network-devp2p/tests/tests.rs index 55ee29d031f..a8bf6309aa1 100644 --- a/util/network-devp2p/tests/tests.rs +++ b/util/network-devp2p/tests/tests.rs @@ -113,7 +113,7 @@ fn net_start_stop() { #[test] fn net_disconnect() { - let key1 = Random.generate().unwrap(); + let key1 = Random.generate(); let mut config1 = NetworkConfiguration::new_local(); config1.use_secret = Some(key1.secret().clone()); config1.boot_nodes = vec![ ]; diff --git a/util/network/Cargo.toml b/util/network/Cargo.toml index fbbcd0c1f76..81995fd908c 100644 --- a/util/network/Cargo.toml +++ b/util/network/Cargo.toml @@ -8,7 +8,7 @@ authors = ["Parity Technologies "] [dependencies] derive_more = "0.99" -parity-crypto = { version = "0.4.2", features = ["publickey"] } +parity-crypto = { version = "0.5.0", features = ["publickey"] } ethcore-io = { path = "../io" } ethereum-types = "0.8.0" ipnetwork = "0.12.6" diff --git a/util/panic-hook/Cargo.toml b/util/panic-hook/Cargo.toml index eb70c19f10c..83b5920d21f 100644 --- a/util/panic-hook/Cargo.toml +++ b/util/panic-hook/Cargo.toml @@ -7,4 +7,4 @@ version = "0.1.0" authors = ["Parity Technologies "] [dependencies] -backtrace = "0.3.2" +backtrace = "0.3.43" diff --git a/util/rlp-compress/src/common.rs b/util/rlp-compress/src/common.rs index b4a52641229..eb199cb3efb 100644 --- a/util/rlp-compress/src/common.rs +++ b/util/rlp-compress/src/common.rs @@ -20,14 +20,14 @@ lazy_static! { pub static ref BLOCKS_SWAPPER: Swapper<'static> = Swapper::new(COMMON_RLPS, INVALID_RLPS); } -static EMPTY_RLPS: &'static [&'static [u8]] = &[ +static EMPTY_RLPS: &[&[u8]] = &[ // RLP of KECCAK_NULL_RLP &[160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33], // RLP of KECCAK_EMPTY &[160, 197, 210, 70, 1, 134, 247, 35, 60, 146, 126, 125, 178, 220, 199, 3, 192, 229, 0, 182, 83, 202, 130, 39, 59, 123, 250, 216, 4, 93, 133, 164, 112] ]; -static COMMON_RLPS: &'static [&'static [u8]] = &[ +static COMMON_RLPS: &[&[u8]] = &[ // RLP of KECCAK_NULL_RLP &[160, 86, 232, 31, 23, 27, 204, 85, 166, 255, 131, 69, 230, 146, 192, 248, 110, 91, 72, 224, 27, 153, 108, 173, 192, 1, 98, 47, 181, 227, 99, 180, 33], // RLP of KECCAK_EMPTY @@ -39,4 +39,4 @@ static COMMON_RLPS: &'static [&'static [u8]] = &[ &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] ]; -static INVALID_RLPS: &'static [&'static [u8]] = &[&[0x81, 0x0], &[0x81, 0x1], &[0x81, 0x2], &[0x81, 0x3], &[0x81, 0x4], &[0x81, 0x5], &[0x81, 0x6], &[0x81, 0x7], &[0x81, 0x8], &[0x81, 0x9], &[0x81, 0xa], &[0x81, 0xb], &[0x81, 0xc], &[0x81, 0xd], &[0x81, 0xe], &[0x81, 0xf], &[0x81, 0x10], &[0x81, 0x11], &[0x81, 0x12], &[0x81, 0x13], &[0x81, 0x14], &[0x81, 0x15], &[0x81, 0x16], &[0x81, 0x17], &[0x81, 0x18], &[0x81, 0x19], &[0x81, 0x1a], &[0x81, 0x1b], &[0x81, 0x1c], &[0x81, 0x1d], &[0x81, 0x1e], &[0x81, 0x1f], &[0x81, 0x20], &[0x81, 0x21], &[0x81, 0x22], &[0x81, 0x23], &[0x81, 0x24], &[0x81, 0x25], &[0x81, 0x26], &[0x81, 0x27], &[0x81, 0x28], &[0x81, 0x29], &[0x81, 0x2a], &[0x81, 0x2b], &[0x81, 0x2c], &[0x81, 0x2d], &[0x81, 0x2e], &[0x81, 0x2f], &[0x81, 0x30], &[0x81, 0x31], &[0x81, 0x32], &[0x81, 0x33], &[0x81, 0x34], &[0x81, 0x35], &[0x81, 0x36], &[0x81, 0x37], &[0x81, 0x38], &[0x81, 0x39], &[0x81, 0x3a], &[0x81, 0x3b], &[0x81, 0x3c], &[0x81, 0x3d], &[0x81, 0x3e], &[0x81, 0x3f], &[0x81, 0x40], &[0x81, 0x41], &[0x81, 0x42], &[0x81, 0x43], &[0x81, 0x44], &[0x81, 0x45], &[0x81, 0x46], &[0x81, 0x47], &[0x81, 0x48], &[0x81, 0x49], &[0x81, 0x4a], &[0x81, 0x4b], &[0x81, 0x4c], &[0x81, 0x4d], &[0x81, 0x4e], &[0x81, 0x4f], &[0x81, 0x50], &[0x81, 0x51], &[0x81, 0x52], &[0x81, 0x53], &[0x81, 0x54], &[0x81, 0x55], &[0x81, 0x56], &[0x81, 0x57], &[0x81, 0x58], &[0x81, 0x59], &[0x81, 0x5a], &[0x81, 0x5b], &[0x81, 0x5c], &[0x81, 0x5d], &[0x81, 0x5e], &[0x81, 0x5f], &[0x81, 0x60], &[0x81, 0x61], &[0x81, 0x62], &[0x81, 0x63], &[0x81, 0x64], &[0x81, 0x65], &[0x81, 0x66], &[0x81, 0x67], &[0x81, 0x68], &[0x81, 0x69], &[0x81, 0x6a], &[0x81, 0x6b], &[0x81, 0x6c], &[0x81, 0x6d], &[0x81, 0x6e], &[0x81, 0x6f], &[0x81, 0x70], &[0x81, 0x71], &[0x81, 0x72], &[0x81, 0x73], &[0x81, 0x74], &[0x81, 0x75], &[0x81, 0x76], &[0x81, 0x77], &[0x81, 0x78], &[0x81, 0x79], &[0x81, 0x7a], &[0x81, 0x7b], &[0x81, 0x7c], &[0x81, 0x7d], &[0x81, 0x7e]]; +static INVALID_RLPS: &[&[u8]] = &[&[0x81, 0x0], &[0x81, 0x1], &[0x81, 0x2], &[0x81, 0x3], &[0x81, 0x4], &[0x81, 0x5], &[0x81, 0x6], &[0x81, 0x7], &[0x81, 0x8], &[0x81, 0x9], &[0x81, 0xa], &[0x81, 0xb], &[0x81, 0xc], &[0x81, 0xd], &[0x81, 0xe], &[0x81, 0xf], &[0x81, 0x10], &[0x81, 0x11], &[0x81, 0x12], &[0x81, 0x13], &[0x81, 0x14], &[0x81, 0x15], &[0x81, 0x16], &[0x81, 0x17], &[0x81, 0x18], &[0x81, 0x19], &[0x81, 0x1a], &[0x81, 0x1b], &[0x81, 0x1c], &[0x81, 0x1d], &[0x81, 0x1e], &[0x81, 0x1f], &[0x81, 0x20], &[0x81, 0x21], &[0x81, 0x22], &[0x81, 0x23], &[0x81, 0x24], &[0x81, 0x25], &[0x81, 0x26], &[0x81, 0x27], &[0x81, 0x28], &[0x81, 0x29], &[0x81, 0x2a], &[0x81, 0x2b], &[0x81, 0x2c], &[0x81, 0x2d], &[0x81, 0x2e], &[0x81, 0x2f], &[0x81, 0x30], &[0x81, 0x31], &[0x81, 0x32], &[0x81, 0x33], &[0x81, 0x34], &[0x81, 0x35], &[0x81, 0x36], &[0x81, 0x37], &[0x81, 0x38], &[0x81, 0x39], &[0x81, 0x3a], &[0x81, 0x3b], &[0x81, 0x3c], &[0x81, 0x3d], &[0x81, 0x3e], &[0x81, 0x3f], &[0x81, 0x40], &[0x81, 0x41], &[0x81, 0x42], &[0x81, 0x43], &[0x81, 0x44], &[0x81, 0x45], &[0x81, 0x46], &[0x81, 0x47], &[0x81, 0x48], &[0x81, 0x49], &[0x81, 0x4a], &[0x81, 0x4b], &[0x81, 0x4c], &[0x81, 0x4d], &[0x81, 0x4e], &[0x81, 0x4f], &[0x81, 0x50], &[0x81, 0x51], &[0x81, 0x52], &[0x81, 0x53], &[0x81, 0x54], &[0x81, 0x55], &[0x81, 0x56], &[0x81, 0x57], &[0x81, 0x58], &[0x81, 0x59], &[0x81, 0x5a], &[0x81, 0x5b], &[0x81, 0x5c], &[0x81, 0x5d], &[0x81, 0x5e], &[0x81, 0x5f], &[0x81, 0x60], &[0x81, 0x61], &[0x81, 0x62], &[0x81, 0x63], &[0x81, 0x64], &[0x81, 0x65], &[0x81, 0x66], &[0x81, 0x67], &[0x81, 0x68], &[0x81, 0x69], &[0x81, 0x6a], &[0x81, 0x6b], &[0x81, 0x6c], &[0x81, 0x6d], &[0x81, 0x6e], &[0x81, 0x6f], &[0x81, 0x70], &[0x81, 0x71], &[0x81, 0x72], &[0x81, 0x73], &[0x81, 0x74], &[0x81, 0x75], &[0x81, 0x76], &[0x81, 0x77], &[0x81, 0x78], &[0x81, 0x79], &[0x81, 0x7a], &[0x81, 0x7b], &[0x81, 0x7c], &[0x81, 0x7d], &[0x81, 0x7e]]; diff --git a/util/rlp-derive/Cargo.toml b/util/rlp-derive/Cargo.toml deleted file mode 100644 index 878b8016740..00000000000 --- a/util/rlp-derive/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "rlp_derive" -version = "0.1.0" -authors = ["Parity Technologies "] -edition = "2018" - -[lib] -name = "rlp_derive" -proc-macro = true - -[dependencies] -syn = "1.0.14" -quote = "1.0.2" -proc-macro2 = "1.0.8" - -[dev-dependencies] -rlp = "0.4.0" diff --git a/util/rlp-derive/src/de.rs b/util/rlp-derive/src/de.rs deleted file mode 100644 index 3ae4b3aff2f..00000000000 --- a/util/rlp-derive/src/de.rs +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use proc_macro2::TokenStream; -use quote::quote; - -struct ParseQuotes { - single: TokenStream, - list: TokenStream, - takes_index: bool, -} - -fn decodable_parse_quotes() -> ParseQuotes { - ParseQuotes { - single: quote! { rlp.val_at }, - list: quote! { rlp.list_at }, - takes_index: true, - } -} - -fn decodable_wrapper_parse_quotes() -> ParseQuotes { - ParseQuotes { - single: quote! { rlp.as_val }, - list: quote! { rlp.as_list }, - takes_index: false, - } -} - -pub fn impl_decodable(ast: &syn::DeriveInput) -> TokenStream { - let body = match ast.data { - syn::Data::Struct(ref s) => s, - _ => panic!("#[derive(RlpDecodable)] is only defined for structs."), - }; - - let mut default_attribute_encountered = false; - let stmts: Vec<_> = body - .fields - .iter() - .enumerate() - .map(|(i, field)| decodable_field( - i, - field, - decodable_parse_quotes(), - &mut default_attribute_encountered, - )).collect(); - let name = &ast.ident; - - let impl_block = quote! { - impl rlp::Decodable for #name { - fn decode(rlp: &rlp::Rlp) -> Result { - let result = #name { - #(#stmts)* - }; - - Ok(result) - } - } - }; - - quote! { - const _: () = { - extern crate rlp; - #impl_block - }; - } -} - -pub fn impl_decodable_wrapper(ast: &syn::DeriveInput) -> TokenStream { - let body = match ast.data { - syn::Data::Struct(ref s) => s, - _ => panic!("#[derive(RlpDecodableWrapper)] is only defined for structs."), - }; - - let stmt = { - let fields: Vec<_> = body.fields.iter().collect(); - if fields.len() == 1 { - let field = fields.first().expect("fields.len() == 1; qed"); - let mut default_attribute_encountered = false; - decodable_field( - 0, - field, - decodable_wrapper_parse_quotes(), - &mut default_attribute_encountered, - ) - } else { - panic!("#[derive(RlpEncodableWrapper)] is only defined for structs with one field.") - } - }; - - let name = &ast.ident; - - let impl_block = quote! { - impl rlp::Decodable for #name { - fn decode(rlp: &rlp::Rlp) -> Result { - let result = #name { - #stmt - }; - - Ok(result) - } - } - }; - - quote! { - const _: () = { - extern crate rlp; - #impl_block - }; - } -} - -fn decodable_field( - index: usize, - field: &syn::Field, - quotes: ParseQuotes, - default_attribute_encountered: &mut bool, -) -> TokenStream { - let id = match field.ident { - Some(ref ident) => quote! { #ident }, - None => { - let index: syn::Index = index.into(); - quote! { #index } - } - }; - - let index = index - *default_attribute_encountered as usize; - let index = quote! { #index }; - - let single = quotes.single; - let list = quotes.list; - - let attributes = &field.attrs; - let default = if let Some(attr) = attributes.iter().find(|attr| attr.path.is_ident("rlp")) { - if *default_attribute_encountered { - panic!("only 1 #[rlp(default)] attribute is allowed in a struct") - } - match attr.parse_args() { - Ok(proc_macro2::TokenTree::Ident(ident)) if ident.to_string() == "default" => {}, - _ => panic!("only #[rlp(default)] attribute is supported"), - } - *default_attribute_encountered = true; - true - } else { - false - }; - - match field.ty { - syn::Type::Path(ref path) => { - let ident = &path - .path - .segments - .first() - .expect("there must be at least 1 segment") - .ident; - let ident_type = ident.to_string(); - if &ident_type == "Vec" { - if quotes.takes_index { - if default { - quote! { #id: #list(#index).unwrap_or_default(), } - } else { - quote! { #id: #list(#index)?, } - } - } else { - quote! { #id: #list()?, } - } - } else { - if quotes.takes_index { - if default { - quote! { #id: #single(#index).unwrap_or_default(), } - } else { - quote! { #id: #single(#index)?, } - } - } else { - quote! { #id: #single()?, } - } - } - } - _ => panic!("rlp_derive not supported"), - } -} diff --git a/util/rlp-derive/src/en.rs b/util/rlp-derive/src/en.rs deleted file mode 100644 index 32905456a8f..00000000000 --- a/util/rlp-derive/src/en.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use proc_macro2::TokenStream; -use quote::quote; - -pub fn impl_encodable(ast: &syn::DeriveInput) -> TokenStream { - let body = match ast.data { - syn::Data::Struct(ref s) => s, - _ => panic!("#[derive(RlpEncodable)] is only defined for structs."), - }; - - let stmts: Vec<_> = body - .fields - .iter() - .enumerate() - .map(|(i, field)| encodable_field(i, field)) - .collect(); - let name = &ast.ident; - - let stmts_len = stmts.len(); - let stmts_len = quote! { #stmts_len }; - let impl_block = quote! { - impl rlp::Encodable for #name { - fn rlp_append(&self, stream: &mut rlp::RlpStream) { - stream.begin_list(#stmts_len); - #(#stmts)* - } - } - }; - - quote! { - const _: () = { - extern crate rlp; - #impl_block - }; - } -} - -pub fn impl_encodable_wrapper(ast: &syn::DeriveInput) -> TokenStream { - let body = match ast.data { - syn::Data::Struct(ref s) => s, - _ => panic!("#[derive(RlpEncodableWrapper)] is only defined for structs."), - }; - - let stmt = { - let fields: Vec<_> = body.fields.iter().collect(); - if fields.len() == 1 { - let field = fields.first().expect("fields.len() == 1; qed"); - encodable_field(0, field) - } else { - panic!("#[derive(RlpEncodableWrapper)] is only defined for structs with one field.") - } - }; - - let name = &ast.ident; - - let impl_block = quote! { - impl rlp::Encodable for #name { - fn rlp_append(&self, stream: &mut rlp::RlpStream) { - #stmt - } - } - }; - - quote! { - const _: () = { - extern crate rlp; - #impl_block - }; - } -} - -fn encodable_field(index: usize, field: &syn::Field) -> TokenStream { - let ident = match field.ident { - Some(ref ident) => quote! { #ident }, - None => { - let index: syn::Index = index.into(); - quote! { #index } - } - }; - - let id = quote! { self.#ident }; - - match field.ty { - syn::Type::Path(ref path) => { - let top_segment = path - .path - .segments - .first() - .expect("there must be at least 1 segment"); - let ident = &top_segment.ident; - if &ident.to_string() == "Vec" { - let inner_ident = match top_segment.arguments { - syn::PathArguments::AngleBracketed(ref angle) => { - let ty = angle - .args - .first() - .expect("Vec has only one angle bracketed type; qed"); - match *ty { - syn::GenericArgument::Type(syn::Type::Path(ref path)) => { - &path - .path - .segments - .first() - .expect("there must be at least 1 segment") - .ident - } - _ => panic!("rlp_derive not supported"), - } - } - _ => unreachable!("Vec has only one angle bracketed type; qed"), - }; - quote! { stream.append_list::<#inner_ident, _>(&#id); } - } else { - quote! { stream.append(&#id); } - } - } - _ => panic!("rlp_derive not supported"), - } -} diff --git a/util/rlp-derive/src/lib.rs b/util/rlp-derive/src/lib.rs deleted file mode 100644 index 3c9799e2ae5..00000000000 --- a/util/rlp-derive/src/lib.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -extern crate proc_macro; - -mod de; -mod en; - -use de::{impl_decodable, impl_decodable_wrapper}; -use en::{impl_encodable, impl_encodable_wrapper}; -use proc_macro::TokenStream; - -#[proc_macro_derive(RlpEncodable, attributes(rlp))] -pub fn encodable(input: TokenStream) -> TokenStream { - let ast = syn::parse(input).unwrap(); - let gen = impl_encodable(&ast); - gen.into() -} - -#[proc_macro_derive(RlpEncodableWrapper)] -pub fn encodable_wrapper(input: TokenStream) -> TokenStream { - let ast = syn::parse(input).unwrap(); - let gen = impl_encodable_wrapper(&ast); - gen.into() -} - -#[proc_macro_derive(RlpDecodable, attributes(rlp))] -pub fn decodable(input: TokenStream) -> TokenStream { - let ast = syn::parse(input).unwrap(); - let gen = impl_decodable(&ast); - gen.into() -} - -#[proc_macro_derive(RlpDecodableWrapper)] -pub fn decodable_wrapper(input: TokenStream) -> TokenStream { - let ast = syn::parse(input).unwrap(); - let gen = impl_decodable_wrapper(&ast); - gen.into() -} diff --git a/util/rlp-derive/tests/rlp.rs b/util/rlp-derive/tests/rlp.rs deleted file mode 100644 index adf73766752..00000000000 --- a/util/rlp-derive/tests/rlp.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -use rlp::{decode, encode}; -use rlp_derive::{RlpDecodable, RlpDecodableWrapper, RlpEncodable, RlpEncodableWrapper}; - -#[derive(Debug, PartialEq, RlpEncodable, RlpDecodable)] -struct Foo { - a: String, -} - -#[derive(Debug, PartialEq, RlpEncodableWrapper, RlpDecodableWrapper)] -struct FooWrapper { - a: String, -} - -#[test] -fn test_encode_foo() { - let foo = Foo { a: "cat".into() }; - - let expected = vec![0xc4, 0x83, b'c', b'a', b't']; - let out = encode(&foo); - assert_eq!(out, expected); - - let decoded = decode(&expected).expect("decode failure"); - assert_eq!(foo, decoded); -} - -#[test] -fn test_encode_foo_wrapper() { - let foo = FooWrapper { a: "cat".into() }; - - let expected = vec![0x83, b'c', b'a', b't']; - let out = encode(&foo); - assert_eq!(out, expected); - - let decoded = decode(&expected).expect("decode failure"); - assert_eq!(foo, decoded); -} - -#[test] -fn test_encode_foo_default() { - #[derive(Debug, PartialEq, RlpEncodable, RlpDecodable)] - struct FooDefault { - a: String, - /// It works with other attributes. - #[rlp(default)] - b: Option>, - } - - let attack_of = String::from("clones"); - let foo = Foo { a: attack_of.clone() }; - - let expected = vec![0xc7, 0x86, b'c', b'l', b'o', b'n', b'e', b's']; - let out = encode(&foo); - assert_eq!(out, expected); - - let foo_default = FooDefault { a: attack_of.clone(), b: None }; - - let decoded = decode(&expected).expect("default failure"); - assert_eq!(foo_default, decoded); - - let foo_some = FooDefault { a: attack_of.clone(), b: Some(vec![1, 2, 3]) }; - let out = encode(&foo_some); - assert_eq!(decode(&out), Ok(foo_some)); -} diff --git a/util/runtime/Cargo.toml b/util/runtime/Cargo.toml deleted file mode 100644 index d22106c4927..00000000000 --- a/util/runtime/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -description = "Parity Runtime" -homepage = "http://parity.io" -license = "GPL-3.0" -name = "parity-runtime" -version = "0.1.0" -authors = ["Parity Technologies "] - -[dependencies] -futures = "0.1" -tokio = "0.1.22" diff --git a/util/runtime/src/lib.rs b/util/runtime/src/lib.rs deleted file mode 100644 index 568b2771fd7..00000000000 --- a/util/runtime/src/lib.rs +++ /dev/null @@ -1,274 +0,0 @@ -// Copyright 2015-2020 Parity Technologies (UK) Ltd. -// This file is part of Parity Ethereum. - -// Parity Ethereum is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Parity Ethereum is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Parity Ethereum. If not, see . - -//! Tokio Runtime wrapper. - -pub extern crate futures; -pub extern crate tokio; - -use std::{fmt, thread}; -use std::sync::mpsc; -use std::time::{Duration, Instant}; -use futures::{future, Future, IntoFuture}; -pub use tokio::timer::Delay; -pub use tokio::runtime::{Runtime as TokioRuntime, Builder as TokioRuntimeBuilder, TaskExecutor}; - -/// Runtime for futures. -/// -/// Runs in a separate thread. -pub struct Runtime { - executor: Executor, - handle: RuntimeHandle, -} - -impl Runtime { - fn new(runtime_bldr: &mut TokioRuntimeBuilder) -> Self { - let mut runtime = runtime_bldr - .build() - .expect("Building a Tokio runtime will only fail when mio components \ - cannot be initialized (catastrophic)"); - let (stop, stopped) = futures::oneshot(); - let (tx, rx) = mpsc::channel(); - let handle = thread::spawn(move || { - tx.send(runtime.executor()).expect("Rx is blocking upper thread."); - runtime.block_on(futures::empty().select(stopped).map(|_| ()).map_err(|_| ())) - .expect("Tokio runtime should not have unhandled errors."); - }); - let executor = rx.recv().expect("tx is transfered to a newly spawned thread."); - - Runtime { - executor: Executor { - inner: Mode::Tokio(executor), - }, - handle: RuntimeHandle { - close: Some(stop), - handle: Some(handle), - }, - } - } - - /// Spawns a new tokio runtime with a default thread count on a background - /// thread and returns a `Runtime` which can be used to spawn tasks via - /// its executor. - pub fn with_default_thread_count() -> Self { - let mut runtime_bldr = TokioRuntimeBuilder::new(); - Self::new(&mut runtime_bldr) - } - - /// Spawns a new tokio runtime with a the specified thread count on a - /// background thread and returns a `Runtime` which can be used to spawn - /// tasks via its executor. - pub fn with_thread_count(thread_count: usize) -> Self { - let mut runtime_bldr = TokioRuntimeBuilder::new(); - runtime_bldr.core_threads(thread_count); - - Self::new(&mut runtime_bldr) - } - - /// Returns this runtime raw executor. - /// - /// Deprecated: Exists only to connect with current JSONRPC implementation. - pub fn raw_executor(&self) -> TaskExecutor { - if let Mode::Tokio(ref executor) = self.executor.inner { - executor.clone() - } else { - panic!("Runtime is not initialized in Tokio mode.") - } - } - - /// Returns runtime executor. - pub fn executor(&self) -> Executor { - self.executor.clone() - } -} - -#[derive(Clone)] -enum Mode { - Tokio(TaskExecutor), - Sync, - ThreadPerFuture, -} - -impl fmt::Debug for Mode { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - use self::Mode::*; - - match *self { - Tokio(_) => write!(fmt, "tokio"), - Sync => write!(fmt, "synchronous"), - ThreadPerFuture => write!(fmt, "thread per future"), - } - } -} - -/// Returns a future which runs `f` until `duration` has elapsed, at which -/// time `on_timeout` is run and the future resolves. -fn timeout(f: F, duration: Duration, on_timeout: T) - -> impl Future + Send + 'static -where - T: FnOnce() -> () + Send + 'static, - F: FnOnce() -> R + Send + 'static, - R: IntoFuture + Send + 'static, - R::Future: Send + 'static, -{ - let future = future::lazy(f); - let timeout = Delay::new(Instant::now() + duration) - .then(move |_| { - on_timeout(); - Ok(()) - }); - future.select(timeout).then(|_| Ok(())) -} - -#[derive(Debug, Clone)] -pub struct Executor { - inner: Mode, -} - -impl Executor { - /// Executor for existing runtime. - /// - /// Deprecated: Exists only to connect with current JSONRPC implementation. - pub fn new(executor: TaskExecutor) -> Self { - Executor { - inner: Mode::Tokio(executor), - } - } - - /// Synchronous executor, used mostly for tests. - pub fn new_sync() -> Self { - Executor { - inner: Mode::Sync, - } - } - - /// Spawns a new thread for each future (use only for tests). - pub fn new_thread_per_future() -> Self { - Executor { - inner: Mode::ThreadPerFuture, - } - } - - /// Spawn a future to this runtime - pub fn spawn(&self, r: R) where - R: IntoFuture + Send + 'static, - R::Future: Send + 'static, - { - match self.inner { - Mode::Tokio(ref executor) => executor.spawn(r.into_future()), - Mode::Sync => { - let _= r.into_future().wait(); - }, - Mode::ThreadPerFuture => { - thread::spawn(move || { - let _= r.into_future().wait(); - }); - }, - } - } - - /// Spawn a new future returned by given closure. - pub fn spawn_fn(&self, f: F) where - F: FnOnce() -> R + Send + 'static, - R: IntoFuture + Send + 'static, - R::Future: Send + 'static, - { - match self.inner { - Mode::Tokio(ref executor) => executor.spawn(future::lazy(f)), - Mode::Sync => { - let _ = future::lazy(f).wait(); - }, - Mode::ThreadPerFuture => { - thread::spawn(move || { - let _= f().into_future().wait(); - }); - }, - } - } - - /// Spawn a new future and wait for it or for a timeout to occur. - pub fn spawn_with_timeout(&self, f: F, duration: Duration, on_timeout: T) where - T: FnOnce() -> () + Send + 'static, - F: FnOnce() -> R + Send + 'static, - R: IntoFuture + Send + 'static, - R::Future: Send + 'static, - { - match self.inner { - Mode::Tokio(ref executor) => { - executor.spawn(timeout(f, duration, on_timeout)) - }, - Mode::Sync => { - let _ = timeout(f, duration, on_timeout).wait(); - }, - Mode::ThreadPerFuture => { - thread::spawn(move || { - let _ = timeout(f, duration, on_timeout).wait(); - }); - }, - } - } -} - -impl + Send + 'static> future::Executor for Executor { - fn execute(&self, future: F) -> Result<(), future::ExecuteError> { - match self.inner { - Mode::Tokio(ref executor) => executor.execute(future), - Mode::Sync => { - let _= future.wait(); - Ok(()) - }, - Mode::ThreadPerFuture => { - thread::spawn(move || { - let _= future.wait(); - }); - Ok(()) - }, - } - } -} - -/// A handle to a runtime. Dropping the handle will cause runtime to shutdown. -pub struct RuntimeHandle { - close: Option>, - handle: Option> -} - -impl From for RuntimeHandle { - fn from(el: Runtime) -> Self { - el.handle - } -} - -impl Drop for RuntimeHandle { - fn drop(&mut self) { - self.close.take().map(|v| v.send(())); - } -} - -impl RuntimeHandle { - /// Blocks current thread and waits until the runtime is finished. - pub fn wait(mut self) -> thread::Result<()> { - self.handle.take() - .expect("Handle is taken only in `wait`, `wait` is consuming; qed").join() - } - - /// Finishes this runtime. - pub fn close(mut self) { - let _ = self.close.take() - .expect("Close is taken only in `close` and `drop`. `close` is consuming; qed") - .send(()); - } -} diff --git a/util/stats/src/lib.rs b/util/stats/src/lib.rs index 9b42b3946f5..ccebcbb9419 100644 --- a/util/stats/src/lib.rs +++ b/util/stats/src/lib.rs @@ -100,9 +100,9 @@ impl Histogram { // Histogram of a sorted corpus if it at least spans the buckets. Bounds are left closed. fn create(corpus: &[T], bucket_number: usize) -> Option> { - if corpus.len() < 1 { return None; } - let corpus_end = corpus.last().expect("there is at least 1 element; qed").clone(); - let corpus_start = corpus.first().expect("there is at least 1 element; qed").clone(); + if corpus.is_empty() { return None; } + let corpus_end = *corpus.last().expect("there is at least 1 element; qed"); + let corpus_start = *corpus.first().expect("there is at least 1 element; qed"); trace!(target: "stats", "Computing histogram from {} to {} with {} buckets.", corpus_start, corpus_end, bucket_number); // Bucket needs to be at least 1 wide. let bucket_size = { @@ -126,7 +126,7 @@ impl Histogram bucket_bounds[bucket + 1] = bucket_end; bucket_end = bucket_end + bucket_size; } - Some(Histogram { bucket_bounds: bucket_bounds, counts: counts }) + Some(Histogram { bucket_bounds, counts }) } }