diff --git a/.gitignore b/.gitignore index fd2fef5..e03bfd4 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ target *.iml .idea/ .vscode/ + +mwixnet-config.toml \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 043c36f..de4c36d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,59 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "aead" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "age" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf320f937ccd0eb7f63450be0f071586cd918cd86785303ec1d052a3e243b550" +dependencies = [ + "age-core", + "base64 0.13.0", + "bech32 0.8.1", + "chacha20poly1305", + "cookie-factory", + "hkdf", + "hmac 0.11.0", + "i18n-embed", + "i18n-embed-fl", + "lazy_static", + "nom 7.1.0", + "pin-project", + "rand 0.7.3", + "rand 0.8.4", + "rust-embed", + "scrypt", + "sha2 0.9.8", + "subtle", + "x25519-dalek 1.1.1", + "zeroize", +] + +[[package]] +name = "age-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a485102f6c7a23e0666b169ba77c9ff6c6d249c05395c379be3cbab48a948e84" +dependencies = [ + "base64 0.13.0", + "chacha20poly1305", + "cookie-factory", + "hkdf", + "nom 7.1.0", + "rand 0.8.4", + "secrecy 0.8.0", + "sha2 0.9.8", +] + [[package]] name = "aho-corasick" version = "0.7.18" @@ -26,6 +79,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi 0.3.9", +] + [[package]] name = "arc-swap" version = "0.4.8" @@ -51,6 +113,17 @@ dependencies = [ "nodrop", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi 0.3.9", +] + [[package]] name = "autocfg" version = "0.1.7" @@ -78,12 +151,87 @@ dependencies = [ "rustc-demangle", ] +[[package]] +name = "base64" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" +dependencies = [ + "byteorder", + "safemem", +] + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + [[package]] name = "base64" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + +[[package]] +name = "base64ct" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a30f9c631ae8b97868a2e841015b72f2c99b05e3f03a14d543b843816a0a5b4d" + +[[package]] +name = "bech32" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dabbe35f96fb9507f7330793dc490461b2962659ac5d427181e451a623751d1" + +[[package]] +name = "bech32" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" + +[[package]] +name = "bindgen" +version = "0.56.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "clap", + "env_logger", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2 1.0.34", + "quote 1.0.10", + "regex", + "rustc-hash", + "shlex", + "which", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bitflags" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4efd02e230a02e18f92fc2735f44597385ed02ad8f831e7c1c1156ee5e1ab3a5" + [[package]] name = "bitflags" version = "1.3.2" @@ -100,15 +248,51 @@ dependencies = [ "constant_time_eq", ] +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.4", +] + [[package]] name = "block-buffer" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" dependencies = [ - "generic-array", + "generic-array 0.14.4", ] +[[package]] +name = "block-buffer" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "bs58" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" + [[package]] name = "bstr" version = "0.2.17" @@ -118,12 +302,34 @@ dependencies = [ "memchr", ] +[[package]] +name = "bumpalo" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +[[package]] +name = "bytes" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "206fdffcfa2df7cbe15601ef46c813fce0965eb3286db6b56c583b814b51c81c" +dependencies = [ + "byteorder", + "iovec", +] + [[package]] name = "bytes" version = "0.5.6" @@ -142,6 +348,15 @@ version = "1.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" +[[package]] +name = "cexpr" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" +dependencies = [ + "nom 5.1.2", +] + [[package]] name = "cfg-if" version = "0.1.10" @@ -163,6 +378,20 @@ dependencies = [ "cfg-if 1.0.0", "cipher", "cpufeatures", + "zeroize", +] + +[[package]] +name = "chacha20poly1305" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b84ed6d1d5f7aa9bdde921a5090e0ca4d934d250ea3b402a5fab3a994e28a2a" +dependencies = [ + "aead", + "chacha20", + "cipher", + "poly1305", + "zeroize", ] [[package]] @@ -173,9 +402,10 @@ checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" dependencies = [ "libc", "num-integer", - "num-traits", + "num-traits 0.2.14", + "serde", "time", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -184,7 +414,34 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" dependencies = [ - "generic-array", + "generic-array 0.14.4", +] + +[[package]] +name = "clang-sys" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa66045b9cb23c2e9c1520732030608b02ee07e5cfaa5a521ec15ded7fa24c90" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c" +dependencies = [ + "ansi_term", + "atty", + "bitflags 1.3.2", + "strsim 0.8.0", + "textwrap", + "unicode-width", + "vec_map", + "yaml-rust 0.3.5", ] [[package]] @@ -193,7 +450,7 @@ version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] @@ -202,6 +459,44 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "cookie-factory" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396de984970346b0d9e93d1415082923c679e5ae5c3ee3dcbd104f5610af126b" + +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys 0.7.0", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6888e10551bb93e424d8df1d07f1a8b4fceb0001a3a4b048bfc47554946f47b3" +dependencies = [ + "core-foundation-sys 0.8.3", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + +[[package]] +name = "core-foundation-sys" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" + [[package]] name = "cpufeatures" version = "0.2.1" @@ -220,31 +515,290 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "croaring" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00d14ad7d8cc067d7a5c93e8563791bfec3f7182361db955530db11d94ed63c" +dependencies = [ + "byteorder", + "croaring-sys", + "libc", +] + +[[package]] +name = "croaring-sys" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d6a46501bb403a61e43bc7cd19977b4f9c54efd703949b00259cc61afb5a86" +dependencies = [ + "bindgen", + "cc", + "libc", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-common" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0" +dependencies = [ + "generic-array 0.14.4", +] + [[package]] name = "crypto-mac" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" dependencies = [ - "generic-array", + "generic-array 0.14.4", "subtle", ] +[[package]] +name = "ct-logs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3686f5fa27dbc1d76c751300376e167c5a43387f44bb451fd1c24776e49113" +dependencies = [ + "sct", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b85542f99a2dfa2a1b8e192662741c9859a846b296bef1c92ef9b58b5a216" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61" +dependencies = [ + "byteorder", + "digest 0.9.0", + "rand_core 0.5.1", + "subtle", + "zeroize", +] + +[[package]] +name = "dashmap" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c" +dependencies = [ + "cfg-if 1.0.0", + "num_cpus", +] + +[[package]] +name = "data-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ee2393c4a91429dffb4bedf19f4d6abf27d8a732c8ce4980305d782e5426d57" + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.4", +] + [[package]] name = "digest" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" dependencies = [ - "generic-array", + "generic-array 0.14.4", ] +[[package]] +name = "digest" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b" +dependencies = [ + "block-buffer 0.10.0", + "crypto-common", + "generic-array 0.14.4", + "subtle", +] + +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if 0.1.10", + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03d86534ed367a67548dc68113a0f5db55432fdfbb6e6f9d77704397d95d5780" +dependencies = [ + "libc", + "redox_users", + "winapi 0.3.9", +] + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "dtoa" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" +[[package]] +name = "easy-jsonrpc-mw" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b1a91569d50e3bba3c9febb22ef54d78c6e8a8d8dd91ae859896c8ba05f4e3" +dependencies = [ + "easy-jsonrpc-proc-macro-mw", + "jsonrpc-core 10.1.0", + "rand 0.6.5", + "serde", + "serde_json", +] + +[[package]] +name = "easy-jsonrpc-proc-macro-mw" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6368dbd2c6685fb84fc6e6a4749917ddc98905793fd06341c7e11a2504f2724" +dependencies = [ + "heck", + "proc-macro2 0.4.30", + "quote 0.6.13", + "syn 0.15.44", +] + +[[package]] +name = "ed25519" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816" +dependencies = [ + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d" +dependencies = [ + "curve25519-dalek 3.2.0", + "ed25519", + "rand 0.7.3", + "serde", + "sha2 0.9.8", + "zeroize", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "encoding_rs" +version = "0.8.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "enum_primitive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" +dependencies = [ + "num-traits 0.1.43", +] + +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "atty", + "humantime 2.1.0", + "log", + "regex", + "termcolor", +] + [[package]] name = "failure" version = "0.1.8" @@ -261,12 +815,27 @@ version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", "synstructure", ] +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "find-crate" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59a98bbaacea1c0eb6a0876280051b892eb73594fd90cf3b20e9c817029c57d2" +dependencies = [ + "toml", +] + [[package]] name = "flate2" version = "1.0.22" @@ -279,18 +848,119 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fluent" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61f69378194459db76abd2ce3952b790db103ceb003008d3d50d97c41ff847a7" +dependencies = [ + "fluent-bundle", + "unic-langid", +] + +[[package]] +name = "fluent-bundle" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e242c601dec9711505f6d5bbff5bedd4b61b2469f2e8bb8e57ee7c9747a87ffd" +dependencies = [ + "fluent-langneg", + "fluent-syntax", + "intl-memoizer", + "intl_pluralrules", + "rustc-hash", + "self_cell", + "smallvec", + "unic-langid", +] + +[[package]] +name = "fluent-langneg" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ad0989667548f06ccd0e306ed56b61bd4d35458d54df5ec7587c0e8ed5e94" +dependencies = [ + "unic-langid", +] + +[[package]] +name = "fluent-syntax" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0abed97648395c902868fee9026de96483933faa54ea3b40d652f7dfe61ca78" +dependencies = [ + "thiserror", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi 0.3.9", +] + [[package]] name = "fuchsia-cprng" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" +[[package]] +name = "fuchsia-zircon" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e9763c69ebaae630ba35f74888db465e49e259ba1bc0eda7d06f4a067615d82" +dependencies = [ + "bitflags 1.3.2", + "fuchsia-zircon-sys", +] + +[[package]] +name = "fuchsia-zircon-sys" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" + +[[package]] +name = "futures" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678" + [[package]] name = "futures" version = "0.3.17" @@ -347,9 +1017,9 @@ checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" dependencies = [ "autocfg 1.0.1", "proc-macro-hack", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", ] [[package]] @@ -378,13 +1048,28 @@ dependencies = [ "futures-sink", "futures-task", "memchr", - "pin-project-lite", + "pin-project-lite 0.2.7", "pin-utils", "proc-macro-hack", "proc-macro-nested", "slab", ] +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "generic-array" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd" +dependencies = [ + "typenum", +] + [[package]] name = "generic-array" version = "0.14.4" @@ -395,6 +1080,17 @@ dependencies = [ "version_check", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.3" @@ -403,7 +1099,7 @@ checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" dependencies = [ "cfg-if 1.0.0", "libc", - "wasi", + "wasi 0.10.2+wasi-snapshot-preview1", ] [[package]] @@ -412,6 +1108,12 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + [[package]] name = "globset" version = "0.4.8" @@ -425,6 +1127,298 @@ dependencies = [ "regex", ] +[[package]] +name = "grin_api" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin?branch=master#78c9794d30c21d40a24f255c411e04f3eab5d512" +dependencies = [ + "bytes 0.5.6", + "easy-jsonrpc-mw", + "failure", + "failure_derive", + "futures 0.3.17", + "grin_chain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_p2p 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_pool 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_store 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "http", + "hyper 0.13.10", + "hyper-rustls 0.20.0", + "hyper-timeout", + "lazy_static", + "log", + "regex", + "ring", + "rustls 0.17.0", + "serde", + "serde_derive", + "serde_json", + "tokio 0.2.25", + "tokio-rustls 0.13.1", + "url", +] + +[[package]] +name = "grin_api" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin#382e914c50c46ddd167b47f9f0c2c3d33f273ef4" +dependencies = [ + "bytes 0.5.6", + "easy-jsonrpc-mw", + "failure", + "failure_derive", + "futures 0.3.17", + "grin_chain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_p2p 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_pool 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_store 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "http", + "hyper 0.13.10", + "hyper-rustls 0.20.0", + "hyper-timeout", + "lazy_static", + "log", + "regex", + "ring", + "rustls 0.17.0", + "serde", + "serde_derive", + "serde_json", + "tokio 0.2.25", + "tokio-rustls 0.13.1", + "url", +] + +[[package]] +name = "grin_chain" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin?branch=master#78c9794d30c21d40a24f255c411e04f3eab5d512" +dependencies = [ + "bit-vec", + "bitflags 1.3.2", + "byteorder", + "chrono", + "croaring", + "enum_primitive", + "failure", + "failure_derive", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_keychain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_store 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "lazy_static", + "log", + "lru-cache", + "serde", + "serde_derive", +] + +[[package]] +name = "grin_chain" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin#382e914c50c46ddd167b47f9f0c2c3d33f273ef4" +dependencies = [ + "bit-vec", + "bitflags 1.3.2", + "byteorder", + "chrono", + "croaring", + "enum_primitive", + "failure", + "failure_derive", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_keychain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_store 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "lazy_static", + "log", + "lru-cache", + "serde", + "serde_derive", +] + +[[package]] +name = "grin_core" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin?branch=master#78c9794d30c21d40a24f255c411e04f3eab5d512" +dependencies = [ + "blake2-rfc", + "byteorder", + "bytes 0.5.6", + "chrono", + "croaring", + "enum_primitive", + "failure", + "failure_derive", + "grin_keychain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "lazy_static", + "log", + "lru-cache", + "num", + "num-bigint", + "rand 0.6.5", + "serde", + "serde_derive", + "siphasher", + "zeroize", +] + +[[package]] +name = "grin_core" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin#382e914c50c46ddd167b47f9f0c2c3d33f273ef4" +dependencies = [ + "blake2-rfc", + "byteorder", + "bytes 0.5.6", + "chrono", + "croaring", + "enum_primitive", + "failure", + "failure_derive", + "grin_keychain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "lazy_static", + "log", + "lru-cache", + "num", + "num-bigint", + "rand 0.6.5", + "serde", + "serde_derive", + "siphasher", + "zeroize", +] + +[[package]] +name = "grin_keychain" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin?branch=master#78c9794d30c21d40a24f255c411e04f3eab5d512" +dependencies = [ + "blake2-rfc", + "byteorder", + "digest 0.9.0", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "hmac 0.11.0", + "lazy_static", + "log", + "pbkdf2 0.8.0", + "rand 0.6.5", + "ripemd160", + "serde", + "serde_derive", + "serde_json", + "sha2 0.9.8", + "zeroize", +] + +[[package]] +name = "grin_keychain" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin#382e914c50c46ddd167b47f9f0c2c3d33f273ef4" +dependencies = [ + "blake2-rfc", + "byteorder", + "digest 0.9.0", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "hmac 0.11.0", + "lazy_static", + "log", + "pbkdf2 0.8.0", + "rand 0.6.5", + "ripemd160", + "serde", + "serde_derive", + "serde_json", + "sha2 0.9.8", + "zeroize", +] + +[[package]] +name = "grin_p2p" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin?branch=master#78c9794d30c21d40a24f255c411e04f3eab5d512" +dependencies = [ + "bitflags 1.3.2", + "bytes 0.5.6", + "chrono", + "enum_primitive", + "grin_chain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_store 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "log", + "lru-cache", + "num", + "rand 0.6.5", + "serde", + "serde_derive", + "tempfile", +] + +[[package]] +name = "grin_p2p" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin#382e914c50c46ddd167b47f9f0c2c3d33f273ef4" +dependencies = [ + "bitflags 1.3.2", + "bytes 0.5.6", + "chrono", + "enum_primitive", + "grin_chain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_store 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "log", + "lru-cache", + "num", + "rand 0.6.5", + "serde", + "serde_derive", + "tempfile", +] + +[[package]] +name = "grin_pool" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin?branch=master#78c9794d30c21d40a24f255c411e04f3eab5d512" +dependencies = [ + "blake2-rfc", + "chrono", + "failure", + "failure_derive", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_keychain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "log", + "rand 0.6.5", + "serde", + "serde_derive", +] + +[[package]] +name = "grin_pool" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin#382e914c50c46ddd167b47f9f0c2c3d33f273ef4" +dependencies = [ + "blake2-rfc", + "chrono", + "failure", + "failure_derive", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_keychain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "log", + "rand 0.6.5", + "serde", + "serde_derive", +] + [[package]] name = "grin_secp256k1zkp" version = "0.7.11" @@ -441,6 +1435,76 @@ dependencies = [ "zeroize", ] +[[package]] +name = "grin_servers" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin#382e914c50c46ddd167b47f9f0c2c3d33f273ef4" +dependencies = [ + "chrono", + "fs2", + "futures 0.3.17", + "grin_api 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_chain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_keychain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_p2p 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_pool 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_store 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "http", + "hyper 0.13.10", + "hyper-rustls 0.20.0", + "lmdb-zero", + "log", + "rand 0.6.5", + "serde", + "serde_derive", + "serde_json", + "tokio 0.2.25", + "tokio-util 0.2.0", + "walkdir", +] + +[[package]] +name = "grin_store" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin?branch=master#78c9794d30c21d40a24f255c411e04f3eab5d512" +dependencies = [ + "byteorder", + "croaring", + "failure", + "failure_derive", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "libc", + "lmdb-zero", + "log", + "memmap", + "serde", + "serde_derive", + "tempfile", +] + +[[package]] +name = "grin_store" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin#382e914c50c46ddd167b47f9f0c2c3d33f273ef4" +dependencies = [ + "byteorder", + "croaring", + "failure", + "failure_derive", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "libc", + "lmdb-zero", + "log", + "memmap", + "serde", + "serde_derive", + "tempfile", +] + [[package]] name = "grin_util" version = "5.1.1" @@ -448,7 +1512,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdbab6869a23e2c2b4307f4015aa1f04242aace9ca62fbe34b5ad192f6b9d9e6" dependencies = [ "backtrace", - "base64", + "base64 0.12.3", "byteorder", "grin_secp256k1zkp", "lazy_static", @@ -463,6 +1527,191 @@ dependencies = [ "zip", ] +[[package]] +name = "grin_util" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin?branch=master#78c9794d30c21d40a24f255c411e04f3eab5d512" +dependencies = [ + "backtrace", + "base64 0.12.3", + "byteorder", + "grin_secp256k1zkp", + "lazy_static", + "log", + "log4rs", + "parking_lot 0.10.2", + "rand 0.6.5", + "serde", + "serde_derive", + "walkdir", + "zeroize", + "zip", +] + +[[package]] +name = "grin_util" +version = "5.2.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin#382e914c50c46ddd167b47f9f0c2c3d33f273ef4" +dependencies = [ + "backtrace", + "base64 0.12.3", + "byteorder", + "grin_secp256k1zkp", + "lazy_static", + "log", + "log4rs", + "parking_lot 0.10.2", + "rand 0.6.5", + "serde", + "serde_derive", + "walkdir", + "zeroize", + "zip", +] + +[[package]] +name = "grin_wallet_api" +version = "5.1.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#cecb084753bb4cd545c11e4802f4e2f4b11b9213" +dependencies = [ + "base64 0.12.3", + "chrono", + "easy-jsonrpc-mw", + "ed25519-dalek", + "failure", + "failure_derive", + "grin_wallet_config", + "grin_wallet_impls", + "grin_wallet_libwallet", + "grin_wallet_util", + "log", + "rand 0.6.5", + "ring", + "serde", + "serde_derive", + "serde_json", + "uuid", +] + +[[package]] +name = "grin_wallet_config" +version = "5.1.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#cecb084753bb4cd545c11e4802f4e2f4b11b9213" +dependencies = [ + "dirs", + "grin_wallet_util", + "rand 0.6.5", + "serde", + "serde_derive", + "toml", +] + +[[package]] +name = "grin_wallet_impls" +version = "5.1.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#cecb084753bb4cd545c11e4802f4e2f4b11b9213" +dependencies = [ + "base64 0.12.3", + "blake2-rfc", + "byteorder", + "chrono", + "data-encoding", + "ed25519-dalek", + "failure", + "failure_derive", + "futures 0.3.17", + "grin_wallet_config", + "grin_wallet_libwallet", + "grin_wallet_util", + "lazy_static", + "log", + "rand 0.6.5", + "regex", + "reqwest", + "ring", + "serde", + "serde_derive", + "serde_json", + "sysinfo", + "timer", + "tokio 0.2.25", + "url", + "uuid", + "x25519-dalek 0.6.0", +] + +[[package]] +name = "grin_wallet_libwallet" +version = "5.1.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#cecb084753bb4cd545c11e4802f4e2f4b11b9213" +dependencies = [ + "age", + "base64 0.9.3", + "bech32 0.7.3", + "blake2-rfc", + "bs58", + "byteorder", + "chrono", + "curve25519-dalek 2.1.3", + "ed25519-dalek", + "failure", + "failure_derive", + "grin_wallet_config", + "grin_wallet_util", + "lazy_static", + "log", + "rand 0.6.5", + "regex", + "secrecy 0.6.0", + "serde", + "serde_derive", + "serde_json", + "sha2 0.8.2", + "strum", + "strum_macros", + "uuid", + "x25519-dalek 0.6.0", +] + +[[package]] +name = "grin_wallet_util" +version = "5.1.0-alpha.1" +source = "git+https://github.com/mimblewimble/grin-wallet?branch=master#cecb084753bb4cd545c11e4802f4e2f4b11b9213" +dependencies = [ + "data-encoding", + "ed25519-dalek", + "grin_api 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_chain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_keychain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_store 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "grin_util 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin?branch=master)", + "rand 0.6.5", + "serde", + "serde_derive", + "sha3", +] + +[[package]] +name = "h2" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e4728fd124914ad25e99e3d15a9361a879f6620f63cb56bbb08f95abb97a535" +dependencies = [ + "bytes 0.5.6", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio 0.2.25", + "tokio-util 0.3.1", + "tracing", + "tracing-futures", +] + [[package]] name = "h2" version = "0.3.7" @@ -477,8 +1726,8 @@ dependencies = [ "http", "indexmap", "slab", - "tokio", - "tokio-util", + "tokio 1.12.0", + "tokio-util 0.6.8", "tracing", ] @@ -488,6 +1737,15 @@ version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +[[package]] +name = "heck" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -497,6 +1755,16 @@ dependencies = [ "libc", ] +[[package]] +name = "hkdf" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01706d578d5c281058480e673ae4086a9f4710d8df1ad80a5b03e39ece5f886b" +dependencies = [ + "digest 0.9.0", + "hmac 0.11.0", +] + [[package]] name = "hmac" version = "0.11.0" @@ -504,7 +1772,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" dependencies = [ "crypto-mac", - "digest", + "digest 0.9.0", +] + +[[package]] +name = "hmac" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddca131f3e7f2ce2df364b57949a9d47915cfbd35e46cfee355ccebbf794d6a2" +dependencies = [ + "digest 0.10.1", ] [[package]] @@ -518,6 +1795,16 @@ dependencies = [ "itoa", ] +[[package]] +name = "http-body" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13d5ff830006f7646652e057693569bfe0d51760c0085a071769d142a205111b" +dependencies = [ + "bytes 0.5.6", + "http", +] + [[package]] name = "http-body" version = "0.4.4" @@ -526,7 +1813,7 @@ checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" dependencies = [ "bytes 1.1.0", "http", - "pin-project-lite", + "pin-project-lite 0.2.7", ] [[package]] @@ -535,6 +1822,12 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" +[[package]] +name = "httpdate" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47" + [[package]] name = "httpdate" version = "1.0.1" @@ -550,6 +1843,36 @@ dependencies = [ "quick-error", ] +[[package]] +name = "humantime" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" + +[[package]] +name = "hyper" +version = "0.13.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a6f157065790a3ed2f88679250419b5cdd96e714a0d65f7797fd337186e96bb" +dependencies = [ + "bytes 0.5.6", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.2.7", + "http", + "http-body 0.3.1", + "httparse", + "httpdate 0.3.2", + "itoa", + "pin-project", + "socket2 0.3.19", + "tokio 0.2.25", + "tower-service", + "tracing", + "want", +] + [[package]] name = "hyper" version = "0.14.14" @@ -560,20 +1883,158 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "h2", + "h2 0.3.7", "http", - "http-body", + "http-body 0.4.4", "httparse", - "httpdate", + "httpdate 1.0.1", "itoa", - "pin-project-lite", - "socket2", - "tokio", + "pin-project-lite 0.2.7", + "socket2 0.4.2", + "tokio 1.12.0", "tower-service", "tracing", "want", ] +[[package]] +name = "hyper-rustls" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac965ea399ec3a25ac7d13b8affd4b8f39325cca00858ddf5eb29b79e6b14b08" +dependencies = [ + "bytes 0.5.6", + "ct-logs", + "futures-util", + "hyper 0.13.10", + "log", + "rustls 0.17.0", + "rustls-native-certs", + "tokio 0.2.25", + "tokio-rustls 0.13.1", + "webpki", +] + +[[package]] +name = "hyper-rustls" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37743cc83e8ee85eacfce90f2f4102030d9ff0a95244098d781e9bee4a90abb6" +dependencies = [ + "bytes 0.5.6", + "futures-util", + "hyper 0.13.10", + "log", + "rustls 0.18.1", + "tokio 0.2.25", + "tokio-rustls 0.14.1", + "webpki", +] + +[[package]] +name = "hyper-timeout" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d1f9b0b8258e3ef8f45928021d3ef14096c2b93b99e4b8cfcabf1f58ec84b0a" +dependencies = [ + "bytes 0.5.6", + "hyper 0.13.10", + "tokio 0.2.25", + "tokio-io-timeout", +] + +[[package]] +name = "hyper-tls" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d979acc56dcb5b8dddba3917601745e877576475aa046df3226eabdecef78eed" +dependencies = [ + "bytes 0.5.6", + "hyper 0.13.10", + "native-tls", + "tokio 0.2.25", + "tokio-tls", +] + +[[package]] +name = "i18n-config" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b62affcd43abfb51f3cbd8736f9407908dc5b44fc558a9be07460bbfd104d983" +dependencies = [ + "log", + "serde", + "serde_derive", + "thiserror", + "toml", + "unic-langid", +] + +[[package]] +name = "i18n-embed" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a0b4598fcd199eb5da38f70ece82903b178ad638839661c00612719bcfc0ad" +dependencies = [ + "fluent", + "fluent-langneg", + "fluent-syntax", + "i18n-embed-impl", + "intl-memoizer", + "lazy_static", + "log", + "parking_lot 0.11.2", + "rust-embed", + "thiserror", + "unic-langid", + "walkdir", +] + +[[package]] +name = "i18n-embed-fl" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c1d347d2f7f9f2f977385b43b204d59ebeb1b2f93ce73cd23622df2d2da1033" +dependencies = [ + "dashmap", + "find-crate", + "fluent", + "fluent-syntax", + "i18n-config", + "i18n-embed", + "lazy_static", + "proc-macro-error", + "proc-macro2 1.0.34", + "quote 1.0.10", + "strsim 0.10.0", + "syn 1.0.84", + "unic-langid", +] + +[[package]] +name = "i18n-embed-impl" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0db2330e035808eb064afb67e6743ddce353763af3e0f2bdfc2476e00ce76136" +dependencies = [ + "find-crate", + "i18n-config", + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "indexmap" version = "1.7.0" @@ -593,19 +2054,85 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "intl-memoizer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c310433e4a310918d6ed9243542a6b83ec1183df95dff8f23f87bb88a264a66f" +dependencies = [ + "type-map", + "unic-langid", +] + +[[package]] +name = "intl_pluralrules" +version = "7.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b18f988384267d7066cc2be425e6faf352900652c046b6971d2e228d3b1c5ecf" +dependencies = [ + "tinystr", + "unic-langid", +] + +[[package]] +name = "iovec" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" +dependencies = [ + "libc", +] + +[[package]] +name = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "jsonrpc-core" +version = "10.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc15eef5f8b6bef5ac5f7440a957ff95d036e2f98706947741bfc93d1976db4c" +dependencies = [ + "futures 0.1.31", + "log", + "serde", + "serde_derive", + "serde_json", +] + [[package]] name = "jsonrpc-core" version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" dependencies = [ - "futures", + "futures 0.3.17", "futures-executor", "futures-util", "log", @@ -621,9 +2148,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2" dependencies = [ "proc-macro-crate", - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", ] [[package]] @@ -632,9 +2159,9 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff" dependencies = [ - "futures", - "hyper", - "jsonrpc-core", + "futures 0.3.17", + "hyper 0.14.14", + "jsonrpc-core 18.0.0", "jsonrpc-server-utils", "log", "net2", @@ -649,35 +2176,89 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4" dependencies = [ "bytes 1.1.0", - "futures", + "futures 0.3.17", "globset", - "jsonrpc-core", + "jsonrpc-core 18.0.0", "lazy_static", "log", - "tokio", + "tokio 1.12.0", "tokio-stream", - "tokio-util", + "tokio-util 0.6.8", "unicase", ] +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + [[package]] name = "libc" version = "0.2.105" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013" +[[package]] +name = "liblmdb-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feed38a3a580f60bf61aaa067b0ff4123395966839adeaf67258a9e50c4d2e49" +dependencies = [ + "gcc", + "libc", +] + +[[package]] +name = "libloading" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afe203d669ec979b7128619bae5a63b7b42e9203c1b29146079ee05e2f604b52" +dependencies = [ + "cfg-if 1.0.0", + "winapi 0.3.9", +] + [[package]] name = "linked-hash-map" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +[[package]] +name = "lmdb-zero" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13416eee745b087c22934f35f1f24da22da41ba2a5ce197143d168ce055cc58d" +dependencies = [ + "bitflags 0.9.1", + "libc", + "liblmdb-sys", + "supercow", +] + [[package]] name = "lock_api" version = "0.3.4" @@ -722,7 +2303,7 @@ dependencies = [ "chrono", "flate2", "fnv", - "humantime", + "humantime 1.3.0", "libc", "log", "log-mdc", @@ -734,15 +2315,71 @@ dependencies = [ "serde_yaml", "thread-id", "typemap", - "winapi", + "winapi 0.3.9", ] +[[package]] +name = "lru-cache" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31e24f1ad8321ca0e8a1e0ac13f23cb668e6f5466c2c57319f6a5cf1cc8e3b1c" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + [[package]] name = "memchr" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg 1.0.1", +] + +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + +[[package]] +name = "mime_guess" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2684d4c2e97d99848d30b324b00c8fcc7e5c897b7cbb5819b09e7c90e8baf212" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "miniz_oxide" version = "0.4.4" @@ -753,6 +2390,25 @@ dependencies = [ "autocfg 1.0.1", ] +[[package]] +name = "mio" +version = "0.6.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4afd66f5b91bf2a3bc13fad0e21caedac168ca4c707504e75585648ae80e4cc4" +dependencies = [ + "cfg-if 0.1.10", + "fuchsia-zircon", + "fuchsia-zircon-sys", + "iovec", + "kernel32-sys", + "libc", + "log", + "miow 0.2.2", + "net2", + "slab", + "winapi 0.2.8", +] + [[package]] name = "mio" version = "0.7.14" @@ -761,9 +2417,44 @@ checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" dependencies = [ "libc", "log", - "miow", + "miow 0.3.7", "ntapi", - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "mio-named-pipes" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0840c1c50fd55e521b247f949c241c9997709f23bd7f023b9762cd561e935656" +dependencies = [ + "log", + "mio 0.6.23", + "miow 0.3.7", + "winapi 0.3.9", +] + +[[package]] +name = "mio-uds" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afcb699eb26d4332647cc848492bbc15eafb26f08d0304550d5aa1f612e066f0" +dependencies = [ + "iovec", + "libc", + "mio 0.6.23", +] + +[[package]] +name = "miow" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebd808424166322d4a38da87083bfddd3ac4c131334ed55856112eb06d46944d" +dependencies = [ + "kernel32-sys", + "net2", + "winapi 0.2.8", + "ws2_32-sys", ] [[package]] @@ -772,7 +2463,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -783,22 +2474,56 @@ dependencies = [ "byteorder", "bytes 0.5.6", "chacha20", + "clap", + "dirs", "failure", - "futures", + "futures 0.3.17", + "grin_api 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_chain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_core 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", + "grin_keychain 5.2.0-alpha.1 (git+https://github.com/mimblewimble/grin)", "grin_secp256k1zkp", - "grin_util", - "hmac", - "hyper", - "jsonrpc-core", + "grin_servers", + "grin_util 5.1.1", + "grin_wallet_api", + "grin_wallet_impls", + "grin_wallet_libwallet", + "hmac 0.12.0", + "hyper 0.14.14", + "itertools", + "jsonrpc-core 18.0.0", "jsonrpc-derive", "jsonrpc-http-server", "lazy_static", + "pbkdf2 0.8.0", "rand 0.8.4", + "ring", + "rpassword", "serde", "serde_derive", "serde_json", - "sha2", - "tokio", + "sha2 0.10.0", + "thiserror", + "tokio 1.12.0", + "toml", +] + +[[package]] +name = "native-tls" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework 2.4.2", + "security-framework-sys 2.4.2", + "tempfile", ] [[package]] @@ -809,7 +2534,7 @@ checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" dependencies = [ "cfg-if 0.1.10", "libc", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -818,13 +2543,69 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "memchr", + "version_check", +] + +[[package]] +name = "nom" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d11e1ef389c76fe5b81bcaf2ea32cf88b62bc494e19f493d0b30e7a930109" +dependencies = [ + "memchr", + "minimal-lexical", + "version_check", +] + [[package]] name = "ntapi" version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" dependencies = [ - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "num" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits 0.2.14", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg 1.0.1", + "num-integer", + "num-traits 0.2.14", +] + +[[package]] +name = "num-complex" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" +dependencies = [ + "autocfg 1.0.1", + "num-traits 0.2.14", ] [[package]] @@ -834,7 +2615,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" dependencies = [ "autocfg 1.0.1", - "num-traits", + "num-traits 0.2.14", +] + +[[package]] +name = "num-iter" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59" +dependencies = [ + "autocfg 1.0.1", + "num-integer", + "num-traits 0.2.14", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg 1.0.1", + "num-bigint", + "num-integer", + "num-traits 0.2.14", +] + +[[package]] +name = "num-traits" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +dependencies = [ + "num-traits 0.2.14", ] [[package]] @@ -877,19 +2690,58 @@ version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + [[package]] name = "opaque-debug" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" +[[package]] +name = "openssl" +version = "0.10.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95" +dependencies = [ + "bitflags 1.3.2", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" + +[[package]] +name = "openssl-sys" +version = "0.9.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb" +dependencies = [ + "autocfg 1.0.1", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "ordered-float" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" dependencies = [ - "num-traits", + "num-traits 0.2.14", ] [[package]] @@ -924,7 +2776,7 @@ dependencies = [ "libc", "redox_syscall 0.1.57", "smallvec", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -938,9 +2790,80 @@ dependencies = [ "libc", "redox_syscall 0.2.10", "smallvec", - "winapi", + "winapi 0.3.9", ] +[[package]] +name = "password-hash" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7" +dependencies = [ + "base64ct", + "rand_core 0.6.3", + "subtle", +] + +[[package]] +name = "pbkdf2" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa" +dependencies = [ + "base64ct", + "crypto-mac", + "hmac 0.11.0", + "password-hash", + "sha2 0.9.8", +] + +[[package]] +name = "pbkdf2" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4628cc3cf953b82edcd3c1388c5715401420ce5524fedbab426bd5aba017434" +dependencies = [ + "digest 0.10.1", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1622113ce508488160cff04e6abc60960e676d330e1ca0f77c0b8df17c81438f" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b95af56fee93df76d721d356ac1ca41fccf168bc448eb14049234df764ba3e76" +dependencies = [ + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" + [[package]] name = "pin-project-lite" version = "0.2.7" @@ -953,6 +2876,23 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" + +[[package]] +name = "poly1305" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede" +dependencies = [ + "cpufeatures", + "opaque-debug 0.3.0", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.15" @@ -968,6 +2908,30 @@ dependencies = [ "toml", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2 1.0.34", + "quote 1.0.10", + "version_check", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -982,11 +2946,20 @@ checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" [[package]] name = "proc-macro2" -version = "1.0.30" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70" +checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" dependencies = [ - "unicode-xid", + "unicode-xid 0.1.0", +] + +[[package]] +name = "proc-macro2" +version = "1.0.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f84e92c0f7c9d58328b85a78557813e4bd845130db68d7184635344399423b1" +dependencies = [ + "unicode-xid 0.2.2", ] [[package]] @@ -995,13 +2968,22 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quote" +version = "0.6.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" +dependencies = [ + "proc-macro2 0.4.30", +] + [[package]] name = "quote" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" dependencies = [ - "proc-macro2", + "proc-macro2 1.0.34", ] [[package]] @@ -1014,7 +2996,7 @@ dependencies = [ "fuchsia-cprng", "libc", "rand_core 0.3.1", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1033,7 +3015,20 @@ dependencies = [ "rand_os", "rand_pcg", "rand_xorshift", - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom 0.1.16", + "libc", + "rand_chacha 0.2.2", + "rand_core 0.5.1", + "rand_hc 0.2.0", ] [[package]] @@ -1058,6 +3053,16 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core 0.5.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -1083,13 +3088,22 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom 0.1.16", +] + [[package]] name = "rand_core" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ - "getrandom", + "getrandom 0.2.3", ] [[package]] @@ -1101,6 +3115,15 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core 0.5.1", +] + [[package]] name = "rand_hc" version = "0.3.1" @@ -1127,7 +3150,7 @@ checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" dependencies = [ "libc", "rand_core 0.4.2", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1141,7 +3164,7 @@ dependencies = [ "libc", "rand_core 0.4.2", "rdrand", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1163,6 +3186,31 @@ dependencies = [ "rand_core 0.3.1", ] +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg 1.0.1", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + [[package]] name = "rdrand" version = "0.4.0" @@ -1184,7 +3232,17 @@ version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_users" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +dependencies = [ + "getrandom 0.2.3", + "redox_syscall 0.2.10", ] [[package]] @@ -1204,24 +3262,202 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "reqwest" +version = "0.10.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0718f81a8e14c4dbb3b34cf23dc6aaf9ab8a0dfec160c534b3dbca1aaa21f47c" +dependencies = [ + "base64 0.13.0", + "bytes 0.5.6", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "http-body 0.3.1", + "hyper 0.13.10", + "hyper-rustls 0.21.0", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "mime_guess", + "native-tls", + "percent-encoding", + "pin-project-lite 0.2.7", + "rustls 0.18.1", + "serde", + "serde_urlencoded", + "tokio 0.2.25", + "tokio-rustls 0.14.1", + "tokio-socks", + "tokio-tls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", + "winreg", +] + +[[package]] +name = "ring" +version = "0.16.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc" +dependencies = [ + "cc", + "libc", + "once_cell", + "spin", + "untrusted", + "web-sys", + "winapi 0.3.9", +] + +[[package]] +name = "ripemd160" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eca4ecc81b7f313189bf73ce724400a07da2a6dac19588b03c8bd76a2dcc251" +dependencies = [ + "block-buffer 0.9.0", + "digest 0.9.0", + "opaque-debug 0.3.0", +] + +[[package]] +name = "rpassword" +version = "4.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99371657d3c8e4d816fb6221db98fa408242b0b53bac08f8676a41f8554fe99f" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "rust-embed" +version = "6.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40377bff8cceee81e28ddb73ac97f5c2856ce5522f0b260b763f434cdfae602" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "6.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94e763e24ba2bf0c72bc6be883f967f794a019fafd1b86ba1daff9c91a7edd30" +dependencies = [ + "proc-macro2 1.0.34", + "quote 1.0.10", + "rust-embed-utils", + "syn 1.0.84", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "7.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad22c7226e4829104deab21df575e995bfbc4adfad13a595e387477f238c1aec" +dependencies = [ + "sha2 0.9.8", + "walkdir", +] + [[package]] name = "rustc-demangle" version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + [[package]] name = "rustc-serialize" version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" +[[package]] +name = "rustls" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0d4a31f5d68413404705d6982529b0e11a9aacd4839d1d6222ee3b8cb4015e1" +dependencies = [ + "base64 0.11.0", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d1126dcf58e93cee7d098dbda643b5f92ed724f1f6a63007c1116eed6700c81" +dependencies = [ + "base64 0.12.3", + "log", + "ring", + "sct", + "webpki", +] + +[[package]] +name = "rustls-native-certs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75ffeb84a6bd9d014713119542ce415db3a3e4748f0bfce1e1416cd224a23a5" +dependencies = [ + "openssl-probe", + "rustls 0.17.0", + "schannel", + "security-framework 0.4.4", +] + [[package]] name = "ryu" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" + +[[package]] +name = "salsa20" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0fbb5f676da676c260ba276a8f43a8dc67cf02d1438423aeb1c677a7212686" +dependencies = [ + "cipher", +] + [[package]] name = "same-file" version = "1.0.6" @@ -1231,12 +3467,114 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi 0.3.9", +] + [[package]] name = "scopeguard" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "scrypt" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e73d6d7c6311ebdbd9184ad6c4447b2f36337e327bda107d3ba9e3c374f9d325" +dependencies = [ + "hmac 0.12.0", + "pbkdf2 0.10.0", + "salsa20", + "sha2 0.10.0", +] + +[[package]] +name = "sct" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b362b83898e0e69f38515b82ee15aa80636befe47c3b6d3d89a911e78fc228ce" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "secrecy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9182278ed645df3477a9c27bfee0621c621aa16f6972635f7f795dae3d81070f" +dependencies = [ + "zeroize", +] + +[[package]] +name = "secrecy" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" +dependencies = [ + "zeroize", +] + +[[package]] +name = "security-framework" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64808902d7d99f78eaddd2b4e2509713babc3dc3c85ad6f4c447680f3c01e535" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.7.0", + "core-foundation-sys 0.7.0", + "libc", + "security-framework-sys 0.4.3", +] + +[[package]] +name = "security-framework" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.2", + "core-foundation-sys 0.8.3", + "libc", + "security-framework-sys 2.4.2", +] + +[[package]] +name = "security-framework-sys" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17bf11d99252f512695eb468de5516e5cf75455521e69dfe343f3b74e4748405" +dependencies = [ + "core-foundation-sys 0.7.0", + "libc", +] + +[[package]] +name = "security-framework-sys" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +dependencies = [ + "core-foundation-sys 0.8.3", + "libc", +] + +[[package]] +name = "self_cell" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ef965a420fe14fdac7dd018862966a4c14094f900e1650bbc71ddd7d580c8af" + [[package]] name = "serde" version = "1.0.130" @@ -1262,9 +3600,9 @@ version = "1.0.130" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", ] [[package]] @@ -1278,6 +3616,18 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_yaml" version = "0.8.21" @@ -1287,7 +3637,19 @@ dependencies = [ "dtoa", "indexmap", "serde", - "yaml-rust", + "yaml-rust 0.4.5", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer 0.7.3", + "digest 0.8.1", + "fake-simd", + "opaque-debug 0.2.3", ] [[package]] @@ -1296,13 +3658,43 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" dependencies = [ - "block-buffer", + "block-buffer 0.9.0", "cfg-if 1.0.0", "cpufeatures", - "digest", - "opaque-debug", + "digest 0.9.0", + "opaque-debug 0.3.0", ] +[[package]] +name = "sha2" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900d964dd36bb15bcf2f2b35694c072feab74969a54f2bbeec7a2d725d2bdcb6" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest 0.10.1", +] + +[[package]] +name = "sha3" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" +dependencies = [ + "block-buffer 0.7.3", + "byte-tools", + "digest 0.8.1", + "keccak", + "opaque-debug 0.2.3", +] + +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -1312,6 +3704,18 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02658e48d89f2bec991f9a78e69cfa4c316f8d6a6c4ec12fae1aeb263d486788" + +[[package]] +name = "siphasher" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b" + [[package]] name = "slab" version = "0.4.5" @@ -1324,6 +3728,17 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" +[[package]] +name = "socket2" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "winapi 0.3.9", +] + [[package]] name = "socket2" version = "0.4.2" @@ -1331,7 +3746,43 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strum" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" + +[[package]] +name = "strum_macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" +dependencies = [ + "heck", + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", ] [[package]] @@ -1341,14 +3792,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" [[package]] -name = "syn" -version = "1.0.80" +name = "supercow" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" +checksum = "171758edb47aa306a78dfa4ab9aeb5167405bd4e3dc2b64e88f6a84bbe98bd63" + +[[package]] +name = "syn" +version = "0.15.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "proc-macro2 0.4.30", + "quote 0.6.13", + "unicode-xid 0.1.0", +] + +[[package]] +name = "syn" +version = "1.0.84" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b" +dependencies = [ + "proc-macro2 1.0.34", + "quote 1.0.10", + "unicode-xid 0.2.2", ] [[package]] @@ -1357,30 +3825,77 @@ version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ - "proc-macro2", - "quote", - "syn", - "unicode-xid", + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", + "unicode-xid 0.2.2", +] + +[[package]] +name = "sysinfo" +version = "0.14.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2983daff11a197c7c406b130579bc362177aa54cf2cc1f34d6ac88fccaa6a5e1" +dependencies = [ + "cfg-if 0.1.10", + "doc-comment", + "libc", + "ntapi", + "once_cell", + "rayon", + "winapi 0.3.9", +] + +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "rand 0.8.4", + "redox_syscall 0.2.10", + "remove_dir_all", + "winapi 0.3.9", +] + +[[package]] +name = "termcolor" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", ] [[package]] name = "thiserror" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.30" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", ] [[package]] @@ -1391,7 +3906,7 @@ checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" dependencies = [ "libc", "redox_syscall 0.1.57", - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1401,7 +3916,61 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" dependencies = [ "libc", - "winapi", + "winapi 0.3.9", +] + +[[package]] +name = "timer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31d42176308937165701f50638db1c31586f183f1aab416268216577aec7306b" +dependencies = [ + "chrono", +] + +[[package]] +name = "tinystr" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29738eedb4388d9ea620eeab9384884fc3f06f586a2eddb56bedc5885126c7c1" + +[[package]] +name = "tinyvec" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092" +dependencies = [ + "bytes 0.5.6", + "fnv", + "futures-core", + "iovec", + "lazy_static", + "libc", + "memchr", + "mio 0.6.23", + "mio-named-pipes", + "mio-uds", + "num_cpus", + "pin-project-lite 0.1.12", + "signal-hook-registry", + "slab", + "tokio-macros 0.2.6", + "winapi 0.3.9", ] [[package]] @@ -1414,14 +3983,35 @@ dependencies = [ "bytes 1.1.0", "libc", "memchr", - "mio", + "mio 0.7.14", "num_cpus", "once_cell", "parking_lot 0.11.2", - "pin-project-lite", + "pin-project-lite 0.2.7", "signal-hook-registry", - "tokio-macros", - "winapi", + "tokio-macros 1.5.0", + "winapi 0.3.9", +] + +[[package]] +name = "tokio-io-timeout" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9390a43272c8a6ac912ed1d1e2b6abeafd5047e05530a2fa304deee041a06215" +dependencies = [ + "bytes 0.5.6", + "tokio 0.2.25", +] + +[[package]] +name = "tokio-macros" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e44da00bfc73a25f814cd8d7e57a68a5c31b74b3152a0a1d1f590c97ed06265a" +dependencies = [ + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", ] [[package]] @@ -1430,9 +4020,46 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2dd85aeaba7b68df939bd357c6afb36c87951be9e80bf9c859f2fc3e9fca0fd" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", +] + +[[package]] +name = "tokio-rustls" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15cb62a0d2770787abc96e99c1cd98fcf17f94959f3af63ca85bdfb203f051b4" +dependencies = [ + "futures-core", + "rustls 0.17.0", + "tokio 0.2.25", + "webpki", +] + +[[package]] +name = "tokio-rustls" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e12831b255bcfa39dc0436b01e19fea231a37db570686c06ee72c423479f889a" +dependencies = [ + "futures-core", + "rustls 0.18.1", + "tokio 0.2.25", + "webpki", +] + +[[package]] +name = "tokio-socks" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d611fd5d241872372d52a0a3d309c52d0b95a6a67671a6c8f7ab2c4a37fb2539" +dependencies = [ + "bytes 0.4.12", + "either", + "futures 0.3.17", + "thiserror", + "tokio 0.2.25", ] [[package]] @@ -1442,8 +4069,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" dependencies = [ "futures-core", - "pin-project-lite", - "tokio", + "pin-project-lite 0.2.7", + "tokio 1.12.0", +] + +[[package]] +name = "tokio-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a70f4fcd7b3b24fb194f837560168208f669ca8cb70d0c4b862944452396343" +dependencies = [ + "native-tls", + "tokio 0.2.25", +] + +[[package]] +name = "tokio-util" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "571da51182ec208780505a32528fc5512a8fe1443ab960b3f2f3ef093cd16930" +dependencies = [ + "bytes 0.5.6", + "futures-core", + "futures-sink", + "log", + "pin-project-lite 0.1.12", + "tokio 0.2.25", +] + +[[package]] +name = "tokio-util" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be8242891f2b6cbef26a2d7e8605133c2c554cd35b3e4948ea892d6d68436499" +dependencies = [ + "bytes 0.5.6", + "futures-core", + "futures-sink", + "log", + "pin-project-lite 0.1.12", + "tokio 0.2.25", ] [[package]] @@ -1456,8 +4121,8 @@ dependencies = [ "futures-core", "futures-sink", "log", - "pin-project-lite", - "tokio", + "pin-project-lite 0.2.7", + "tokio 1.12.0", ] [[package]] @@ -1482,7 +4147,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" dependencies = [ "cfg-if 1.0.0", - "pin-project-lite", + "log", + "pin-project-lite 0.2.7", "tracing-core", ] @@ -1495,6 +4161,16 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "pin-project", + "tracing", +] + [[package]] name = "traitobject" version = "0.1.0" @@ -1507,6 +4183,15 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +[[package]] +name = "type-map" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d3364c5e96cb2ad1603037ab253ddd34d7fb72a58bdddf4b7350760fc69a46" +dependencies = [ + "rustc-hash", +] + [[package]] name = "typemap" version = "0.3.3" @@ -1522,6 +4207,25 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" +[[package]] +name = "unic-langid" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73328fcd730a030bdb19ddf23e192187a6b01cd98be6d3140622a89129459ce5" +dependencies = [ + "unic-langid-impl", +] + +[[package]] +name = "unic-langid-impl" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a4a8eeaf0494862c1404c95ec2f4c33a2acff5076f64314b465e3ddae1b934d" +dependencies = [ + "serde", + "tinystr", +] + [[package]] name = "unicase" version = "2.6.0" @@ -1531,12 +4235,55 @@ dependencies = [ "version_check", ] +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" + +[[package]] +name = "unicode-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" + +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" + [[package]] name = "unicode-xid" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array 0.14.4", + "subtle", +] + [[package]] name = "unsafe-any" version = "0.4.2" @@ -1546,6 +4293,46 @@ dependencies = [ "traitobject", ] +[[package]] +name = "untrusted" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a" + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +dependencies = [ + "getrandom 0.2.3", + "serde", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + [[package]] name = "version_check" version = "0.9.3" @@ -1559,7 +4346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" dependencies = [ "same-file", - "winapi", + "winapi 0.3.9", "winapi-util", ] @@ -1573,12 +4360,130 @@ dependencies = [ "try-lock", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if 1.0.0", + "serde", + "serde_json", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote 1.0.10", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki" +version = "0.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "webpki-roots" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f20dea7535251981a9670857150d571846545088359b28e4951d350bdaf179f" +dependencies = [ + "webpki", +] + +[[package]] +name = "which" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" +dependencies = [ + "libc", +] + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + [[package]] name = "winapi" version = "0.3.9" @@ -1589,6 +4494,12 @@ dependencies = [ "winapi-x86_64-pc-windows-gnu", ] +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" @@ -1601,7 +4512,7 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" dependencies = [ - "winapi", + "winapi 0.3.9", ] [[package]] @@ -1610,6 +4521,53 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "ws2_32-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "x25519-dalek" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "637ff90c9540fa3073bb577e65033069e4bae7c79d49d74aa3ffdf5342a53217" +dependencies = [ + "curve25519-dalek 2.1.3", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "x25519-dalek" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a0c105152107e3b96f6a00a65e86ce82d9b125230e1c4302940eca58ff71f4f" +dependencies = [ + "curve25519-dalek 3.2.0", + "rand_core 0.5.1", + "zeroize", +] + +[[package]] +name = "yaml-rust" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992" + [[package]] name = "yaml-rust" version = "0.4.5" @@ -1634,9 +4592,9 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bdff2024a851a322b08f179173ae2ba620445aef1e838f0c196820eade4ae0c7" dependencies = [ - "proc-macro2", - "quote", - "syn", + "proc-macro2 1.0.34", + "quote 1.0.10", + "syn 1.0.84", "synstructure", ] diff --git a/Cargo.toml b/Cargo.toml index 491644d..7a4e3d4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,19 +10,35 @@ blake2 = { package = "blake2-rfc", version = "0.2"} byteorder = "1" bytes = "0.5.6" chacha20 = "0.8.1" +clap = { version = "2.33", features = ["yaml"] } +dirs = "2.0" failure = "0.1.8" futures = "0.3" -hmac = { version = "0.11.0", features = ["std"]} +hmac = { version = "0.12.0", features = ["std"]} hyper = { version = "0.14", features = ["full"] } +itertools = { version = "0.10.3"} jsonrpc-core = "18.0" jsonrpc-derive = "18.0" jsonrpc-http-server = "18.0" lazy_static = "1" +pbkdf2 = "0.8.0" rand = "0.8.4" +ring = "0.16" +rpassword = "4.0" serde = { version = "1", features= ["derive"]} serde_derive = "1" serde_json = "1" -sha2 = "0.9.8" +sha2 = "0.10.0" +thiserror = "1.0.31" tokio = { version = "1", features = ["full"] } +toml = "0.5" grin_secp256k1zkp = { version = "0.7.11", features = ["bullet-proof-sizing"]} -grin_util = "5" \ No newline at end of file +grin_util = "5" +grin_api = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" } +grin_core = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" } +grin_chain = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" } +grin_keychain = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" } +grin_servers = { git = "https://github.com/mimblewimble/grin", version = "5.2.0-alpha.1" } +grin_wallet_api = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } +grin_wallet_impls = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } +grin_wallet_libwallet = { git = "https://github.com/mimblewimble/grin-wallet", branch = "master" } \ No newline at end of file diff --git a/README.md b/README.md index 361d3c3..3a2a1fc 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,21 @@ This is an implementation of @tromp's [CoinSwap Proposal](https://forum.grin.mw/ A set of n CoinSwap servers (nodei with i=1...n) are agreed upon in advance. They each have a known public key. +### Setup +#### init-config +To setup a new server, run `mwixnet init-config`. Then enter a password for the server key when prompted. + +This will generate a key for the server and then create a new config file named `mwixnet-config.toml` in the current working directory. +The configuration file will contain the private key of the server encrypted with the server password you provided. + +**Back this config file up! It's the only copy of the server's private key!** + +#### Wallet +A grin-wallet account must be created for receiving extra mwixnet fees. The wallet's owner API should be available (run `grin-wallet owner_api`). + +### Usage +With your wallet and fully synced node both online and listening at the addresses configured, the mwixnet server can be started by running `mwixnet` and providing the server key password and wallet password when prompted. + ### SWAP API The first CoinSwap server (n1) provides the `swap` API, publicly available for use by GRIN wallets. @@ -11,12 +26,13 @@ The first CoinSwap server (n1) provides the `swap` API, publicly avai **params:** ``` [{ - "comsig": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", - "msg": "00010203", + "comsig": "0835f4b8b9cd286c9e35475f575c3e4ae71ceb4ff36598504662627afd628a17d6ba7dedb1aa4c47f0fabad026b76fc86d06f3bef8d0621b8ac4601d4b1b98401586ca3374a401508f32049212478ae91cfa474dfaa5ef2c3dd559d5a292e02334", "onion": { - "commit": "0967593792bc958cd73848c0b948ecab2c6e996ab3c550d462fe41359e447b651f", - "data": ["3719e5fba260c71a5a4bcf9d9caa58cd5dc49531388782fae7699c6fa6b30b09fe42"], - "pubkey": "020dd38a220280f14515f6901a3a366cb7b87630814e4b68b3189a32df964961e5" + "commit": "099a8922343f242dd3da29935ba5bbc7e38bf68eccfb8c96aec87aec0535199139", + "data":[ + "758bc7339edfe8a1f117ef2ef02de58b33d99e820ffd2c21a3d108f4cbdadfbcbc061b705fc351ae2fb34a855bb64180fe8b4913ea13b3bf826773e4b293166cdad1fdf59cadd7d33f73a8f3fc0f339a849f9c505c573d8cc2f006082d8f5bf2c2c84c18d5873a821d9f60bcdd44cf0566d04d761a1eda005cd19ab0b1c593da4656dd2a09322fe1d725f61e8855c927844ec9e80b9260a58012f7c519c5ca3d9b192ab1104d30ac09fb844bd7f692214e5cf0f6cdf1477c20708c2f3ecb098dce6be661907593918840b0fe8eb5043996dace45f2fb66e8f1e2a732035b4e6447b8904f313badebcba99f75c003e297d8dd815915f534dfa7ed416af0c415b60d2a0186752af6af33b781f31fdd3016aeee3bd2e47743fe2ce391b3354b9036b56ec38ed7539adafbc96bef1dbaf354a805b03ac0df7a0d32cff91716926bce68c8ccebb607340f2ffe09c08a9c9fd282ea19b33c69107ed5c54d4872eb0ed83c38d7e07606722069d7709fb914e1e02ea23323f3ae9252902dbfa6f15bd83a3f64587c9ae23aaf96b2a95e1341da12a6e423cf95375184752e10c1dd1a599db74ac0c3d74ec270c589f6a3bdd0877eb986d9a58a8548b917e22bfb93a4a06c36d7cad8d4a8791a8d1e1dc683429b440b136c43ad2f664dafc5156b808050a3c4d28771877d3f1d3a9daa2585eae259aaa64745c6cd260f577e538e27be3c985db41b7c456b63c5b18d7d17420a277d4abc04ae892ceb26940b09fb322445846c14898f5f59305490b1338c56384cd0c7bf5950a0a403aec4d2c2f5e2378b5eb7b1e7fcdbd8d6cc547f3b5a372b22e50e37d858bb197392a10fb9e6e292d6ed6bd8eab1fef7f2d069b6250a0e3e597ccf9a062e04b68821f5c57328ddab775d141147b71c1764c911bad03d8b88e2e62034bc899395514ecab4dec8ab341ba114f0a4e5d1dcfa182396c0e4826ddee187b07bb524dfeaa5297f7a5465f99eaaaa37f082c787b94811feb15b57d68369e6a7e3761d" + ], + "pubkey": "033946e6a495e7278027b38be3d500cfc23d3e0836f1b7e24513841437f316ccb0" } }] ``` diff --git a/mwixnet.yml b/mwixnet.yml new file mode 100644 index 0000000..a341dfd --- /dev/null +++ b/mwixnet.yml @@ -0,0 +1,39 @@ +name: mwixnet +about: MWixnet CoinSwap Server +author: scilio + +args: + - config_file: + help: Path to load/save the mwixnet-config.toml configuration file + short: c + long: config_file + takes_value: true + - grin_node_url: + help: Api address of running GRIN node on which to check inputs and post transactions + short: n + long: grin_node_url + takes_value: true + - grin_node_secret_path: + help: Path to a file containing the secret for the GRIN node api + long: grin_node_secret_path + takes_value: true + - wallet_owner_url: + help: Api address of running wallet owner listener + short: l + long: wallet_owner_url + takes_value: true + - wallet_owner_secret_path: + help: Path to a file containing the secret for the wallet owner api + long: wallet_owner_secret_path + takes_value: true + - wallet_pass: + help: The wallet's password + long: wallet_pass + takes_value: true + - bind_addr: + help: Address to bind the rpc server to (e.g. 0.0.0.0:3000) + long: bind_addr + takes_value: true +subcommands: + - init-config: + about: Writes a new configuration file \ No newline at end of file diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..c691d12 --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1,2 @@ +hard_tabs = true +edition = "2021" diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..b9b2980 --- /dev/null +++ b/src/config.rs @@ -0,0 +1,265 @@ +use crate::error::{self, Result}; +use crate::secp::SecretKey; + +use core::num::NonZeroU32; +use grin_util::{file, ToHex, ZeroingString}; +use rand::{thread_rng, Rng}; +use ring::{aead, pbkdf2}; +use serde_derive::{Deserialize, Serialize}; +use std::fs::File; +use std::io::prelude::*; +use std::net::SocketAddr; +use std::path::PathBuf; + +const GRIN_HOME: &str = ".grin"; +const CHAIN_NAME: &str = "main"; +const NODE_API_SECRET_FILE_NAME: &str = ".api_secret"; +const WALLET_OWNER_API_SECRET_FILE_NAME: &str = ".owner_api_secret"; + +/// The decrypted server config to be passed around and used by the rest of the mwixnet code +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct ServerConfig { + /// private key used by the server to decrypt onion packets + pub key: SecretKey, + /// interval (in seconds) to wait before each mixing round + pub interval_s: u32, + /// socket address the server listener should bind to + pub addr: SocketAddr, + /// foreign api address of the grin node + pub grin_node_url: SocketAddr, + /// path to file containing api secret for the grin node + pub grin_node_secret_path: Option, + /// owner api address of the grin wallet + pub wallet_owner_url: SocketAddr, + /// path to file containing secret for the grin wallet's owner api + pub wallet_owner_secret_path: Option, +} + +impl ServerConfig { + pub fn node_api_secret(&self) -> Option { + file::get_first_line(self.grin_node_secret_path.clone()) + } + + pub fn wallet_owner_api_secret(&self) -> Option { + file::get_first_line(self.wallet_owner_secret_path.clone()) + } +} + +/// Encrypted server key, for storing on disk and decrypting with a password. +/// Includes a salt used by key derivation and a nonce used when sealing the encrypted data. +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +struct EncryptedServerKey { + encrypted_key: String, + salt: String, + nonce: String, +} + +impl EncryptedServerKey { + /// Generates a random salt for pbkdf2 key derivation and a random nonce for aead sealing. + /// Then derives an encryption key from the password and salt. Finally, it encrypts and seals + /// the server key with chacha20-poly1305 using the derived key and random nonce. + pub fn from_secret_key( + server_key: &SecretKey, + password: &ZeroingString, + ) -> Result { + let salt: [u8; 8] = thread_rng().gen(); + let password = password.as_bytes(); + let mut key = [0; 32]; + pbkdf2::derive( + pbkdf2::PBKDF2_HMAC_SHA512, + NonZeroU32::new(100).unwrap(), + &salt, + password, + &mut key, + ); + let content = server_key.0.to_vec(); + let mut enc_bytes = content; + + let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap(); + let sealing_key: aead::LessSafeKey = aead::LessSafeKey::new(unbound_key); + let nonce: [u8; 12] = thread_rng().gen(); + let aad = aead::Aad::from(&[]); + let _ = sealing_key + .seal_in_place_append_tag( + aead::Nonce::assume_unique_for_key(nonce), + aad, + &mut enc_bytes, + ) + .map_err(|e| { + error::ErrorKind::SaveConfigError(format!( + "Failure while encrypting server key: {}", + e + )) + })?; + + Ok(EncryptedServerKey { + encrypted_key: enc_bytes.to_hex(), + salt: salt.to_hex(), + nonce: nonce.to_hex(), + }) + } + + /// Decrypt the server secret key using the provided password. + pub fn decrypt(&self, password: &str) -> Result { + let mut encrypted_seed = grin_util::from_hex(&self.encrypted_key.clone()) + .map_err(|_| error::ErrorKind::LoadConfigError("Seed not valid hex".to_string()))?; + let salt = grin_util::from_hex(&self.salt.clone()) + .map_err(|_| error::ErrorKind::LoadConfigError("Salt not valid hex".to_string()))?; + let nonce = grin_util::from_hex(&self.nonce.clone()) + .map_err(|_| error::ErrorKind::LoadConfigError("Nonce not valid hex".to_string()))?; + let password = password.as_bytes(); + let mut key = [0; 32]; + pbkdf2::derive( + pbkdf2::PBKDF2_HMAC_SHA512, + NonZeroU32::new(100).unwrap(), + &salt, + password, + &mut key, + ); + + let mut n = [0u8; 12]; + n.copy_from_slice(&nonce[0..12]); + let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key).unwrap(); + let opening_key: aead::LessSafeKey = aead::LessSafeKey::new(unbound_key); + let aad = aead::Aad::from(&[]); + let _ = opening_key + .open_in_place( + aead::Nonce::assume_unique_for_key(n), + aad, + &mut encrypted_seed, + ) + .map_err(|e| { + error::ErrorKind::LoadConfigError(format!("Error decrypting seed: {}", e)) + })?; + + for _ in 0..aead::AES_256_GCM.tag_len() { + encrypted_seed.pop(); + } + + let secp = secp256k1zkp::Secp256k1::new(); + let decrypted = SecretKey::from_slice(&secp, &encrypted_seed).map_err(|_| { + error::ErrorKind::LoadConfigError("Decrypted key not valid".to_string()) + })?; + Ok(decrypted) + } +} + +/// The config attributes saved to disk +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +struct RawConfig { + encrypted_key: String, + salt: String, + nonce: String, + interval_s: u32, + addr: SocketAddr, + grin_node_url: SocketAddr, + grin_node_secret_path: Option, + wallet_owner_url: SocketAddr, + wallet_owner_secret_path: Option, +} + +/// Writes the server config to the config_path given, encrypting the server_key first. +pub fn write_config( + config_path: &PathBuf, + server_config: &ServerConfig, + password: &ZeroingString, +) -> Result<()> { + let encrypted = EncryptedServerKey::from_secret_key(&server_config.key, &password)?; + + let raw_config = RawConfig { + encrypted_key: encrypted.encrypted_key, + salt: encrypted.salt, + nonce: encrypted.nonce, + interval_s: server_config.interval_s, + addr: server_config.addr, + grin_node_url: server_config.grin_node_url, + grin_node_secret_path: server_config.grin_node_secret_path.clone(), + wallet_owner_url: server_config.wallet_owner_url, + wallet_owner_secret_path: server_config.wallet_owner_secret_path.clone(), + }; + let encoded: String = toml::to_string(&raw_config).map_err(|e| { + error::ErrorKind::SaveConfigError(format!("Error while encoding config as toml: {}", e)) + })?; + + let mut file = File::create(config_path)?; + file.write_all(encoded.as_bytes()).map_err(|e| { + error::ErrorKind::SaveConfigError(format!("Error while writing config to file: {}", e)) + })?; + + Ok(()) +} + +/// Reads the server config from the config_path given and decrypts it with the provided password. +pub fn load_config(config_path: &PathBuf, password: &ZeroingString) -> Result { + let contents = std::fs::read_to_string(config_path).map_err(|e| { + error::ErrorKind::LoadConfigError(format!( + "Unable to read server config. Perform init-config or pass in config path.\nError: {}", + e + )) + })?; + let raw_config: RawConfig = + toml::from_str(&contents).map_err(|e| error::ErrorKind::LoadConfigError(e.to_string()))?; + + let encrypted_key = EncryptedServerKey { + encrypted_key: raw_config.encrypted_key, + salt: raw_config.salt, + nonce: raw_config.nonce, + }; + let secret_key = encrypted_key.decrypt(&password)?; + + Ok(ServerConfig { + key: secret_key, + interval_s: raw_config.interval_s, + addr: raw_config.addr, + grin_node_url: raw_config.grin_node_url, + grin_node_secret_path: raw_config.grin_node_secret_path, + wallet_owner_url: raw_config.wallet_owner_url, + wallet_owner_secret_path: raw_config.wallet_owner_secret_path, + }) +} + +pub fn get_grin_path() -> PathBuf { + let mut grin_path = match dirs::home_dir() { + Some(p) => p, + None => PathBuf::new(), + }; + grin_path.push(GRIN_HOME); + grin_path.push(CHAIN_NAME); + grin_path +} + +pub fn node_secret_path() -> PathBuf { + let mut path = get_grin_path(); + path.push(NODE_API_SECRET_FILE_NAME); + path +} + +pub fn wallet_owner_secret_path() -> PathBuf { + let mut path = get_grin_path(); + path.push(WALLET_OWNER_API_SECRET_FILE_NAME); + path +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::secp; + + #[test] + fn server_key_encrypt() { + let password = ZeroingString::from("password"); + let server_key = secp::random_secret(); + let mut enc_key = EncryptedServerKey::from_secret_key(&server_key, &password).unwrap(); + let decrypted_key = enc_key.decrypt(&password).unwrap(); + assert_eq!(server_key, decrypted_key); + + // Wrong password + let decrypted_key = enc_key.decrypt("wrongpass"); + assert!(decrypted_key.is_err()); + + // Wrong nonce + enc_key.nonce = "wrongnonce".to_owned(); + let decrypted_key = enc_key.decrypt(&password); + assert!(decrypted_key.is_err()); + } +} diff --git a/src/error.rs b/src/error.rs index 034ae27..fe54f7a 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,110 +1,161 @@ use failure::{self, Context, Fail}; +use grin_wallet_libwallet as libwallet; use std::fmt::{self, Display}; use std::io; /// MWixnet error definition #[derive(Debug)] pub struct Error { - inner: Context, + inner: Context, } pub type Result = std::result::Result; +pub type StdResult = std::result::Result; -#[derive(Clone, Debug, Eq, Fail, PartialEq)] /// MWixnet error types +#[derive(Clone, Debug, Eq, Fail, PartialEq)] pub enum ErrorKind { - /// Unsupported payload version - #[fail(display = "Unsupported Payload Version")] - UnsupportedPayload, - /// Error from secp256k1-zkp library - #[fail(display = "Secp Error")] - SecpError, - /// Invalid key length for MAC initialization - #[fail(display = "InvalidKeyLength")] - InvalidKeyLength, - /// Wraps an io error produced when reading or writing - #[fail(display = "IOError")] - IOErr( - String, - io::ErrorKind, - ), - /// Expected a given value that wasn't found - #[fail(display = "UnexpectedData")] - UnexpectedData { - /// What we wanted - expected: Vec, - /// What we got - received: Vec, - }, - /// Data wasn't in a consumable format - #[fail(display = "CorruptedData")] - CorruptedData, - /// Incorrect number of elements (when deserializing a vec via read_multi say). - #[fail(display = "CountError")] - CountError, - /// When asked to read too much data - #[fail(display = "TooLargeReadErr")] - TooLargeReadErr, + /// Error from secp256k1-zkp library + #[fail(display = "Secp Error")] + SecpError, + /// Invalid key length for MAC initialization + #[fail(display = "InvalidKeyLength")] + InvalidKeyLength, + /// Wraps an io error produced when reading or writing + #[fail(display = "IO Error: {}", _0)] + IOErr(String, io::ErrorKind), + /// Data wasn't in a consumable format + #[fail(display = "CorruptedData")] + CorruptedData, + /// Error from grin's api crate + #[fail(display = "GRIN API Error: {}", _0)] + GrinApiError(String), + /// Error from grin core + #[fail(display = "GRIN Core Error: {}", _0)] + GrinCoreError(String), + /// Error from grin-wallet's libwallet + #[fail(display = "libwallet error: {}", _0)] + LibWalletError(String), + /// Error from serde-json + #[fail(display = "serde json error: {}", _0)] + SerdeJsonError(String), + /// Error from invalid signature + #[fail(display = "invalid signature")] + InvalidSigError, + /// Error while saving config + #[fail(display = "save config error: {}", _0)] + SaveConfigError(String), + /// Error while loading config + #[fail(display = "load config error: {}", _0)] + LoadConfigError(String), } -impl std::error::Error for Error { - -} +impl std::error::Error for Error {} impl From for Error { - fn from(e: io::Error) -> Error { - ErrorKind::IOErr(format!("{}", e), e.kind()).into() - } + fn from(e: io::Error) -> Error { + ErrorKind::IOErr(format!("{}", e), e.kind()).into() + } } impl From for Error { - fn from(e: io::ErrorKind) -> Error { - ErrorKind::IOErr(format!("{}", io::Error::from(e)), e).into() - } + fn from(e: io::ErrorKind) -> Error { + ErrorKind::IOErr(format!("{}", io::Error::from(e)), e).into() + } +} + +impl From for Error { + fn from(e: grin_core::ser::Error) -> Error { + Error { + inner: Context::new(ErrorKind::GrinCoreError(e.to_string())), + } + } } impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.inner, f) - } + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&self.inner, f) + } } impl Error { - pub fn kind(&self) -> ErrorKind { - self.inner.get_context().clone() - } + pub fn new(kind: ErrorKind) -> Error { + Error { + inner: Context::new(kind), + } + } - pub fn message(&self) -> String { - format!("{}", self).into() - } + pub fn message(&self) -> String { + format!("{}", self).into() + } } impl From for Error { - fn from(kind: ErrorKind) -> Error { - Error { - inner: Context::new(kind), - } - } + fn from(kind: ErrorKind) -> Error { + Error { + inner: Context::new(kind), + } + } } impl From> for Error { - fn from(inner: Context) -> Error { - Error { inner } - } + fn from(inner: Context) -> Error { + Error { inner } + } } impl From for Error { - fn from(_error: secp256k1zkp::Error) -> Error { - Error { - inner: Context::new(ErrorKind::SecpError), - } - } + fn from(_error: secp256k1zkp::Error) -> Error { + Error { + inner: Context::new(ErrorKind::SecpError), + } + } } -impl From for Error { - fn from(_error: hmac::crypto_mac::InvalidKeyLength) -> Error { - Error { - inner: Context::new(ErrorKind::InvalidKeyLength), - } - } -} \ No newline at end of file +impl From for Error { + fn from(_error: hmac::digest::InvalidLength) -> Error { + Error { + inner: Context::new(ErrorKind::InvalidKeyLength), + } + } +} + +impl From for Error { + fn from(e: grin_api::Error) -> Error { + Error { + inner: Context::new(ErrorKind::GrinApiError(e.to_string())), + } + } +} + +impl From for Error { + fn from(e: grin_api::json_rpc::Error) -> Error { + Error { + inner: Context::new(ErrorKind::GrinApiError(e.to_string())), + } + } +} + +impl From for Error { + fn from(e: grin_core::core::transaction::Error) -> Error { + Error { + inner: Context::new(ErrorKind::GrinCoreError(e.to_string())), + } + } +} + +impl From for Error { + fn from(e: libwallet::Error) -> Error { + Error { + inner: Context::new(ErrorKind::LibWalletError(e.to_string())), + } + } +} + +impl From for Error { + fn from(e: serde_json::Error) -> Error { + Error { + inner: Context::new(ErrorKind::SerdeJsonError(e.to_string())), + } + } +} diff --git a/src/main.rs b/src/main.rs index 82e890a..c601d23 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,29 +1,190 @@ -use server::ServerConfig; +use config::ServerConfig; +use node::HttpGrinNode; +use wallet::HttpWallet; + +use clap::App; +use grin_util::{StopState, ZeroingString}; +use rpassword; +use std::path::PathBuf; +use std::sync::Arc; +use tokio::runtime::Runtime; #[macro_use] -extern crate lazy_static; +extern crate clap; +mod config; mod error; +mod node; mod onion; +mod rpc; mod secp; -mod ser; mod server; mod types; +mod wallet; -fn main() -> Result<(), Box> { - let secret_key = secp::insecure_rand_secret()?; // todo - load from encrypted key file - let server_config = ServerConfig { - key: secret_key, - addr: "127.0.0.1:3000".parse().unwrap(), - is_first: true - }; +const DEFAULT_INTERVAL: u32 = 12 * 60 * 60; - let shutdown_signal = async move { - // Wait for the CTRL+C signal - tokio::signal::ctrl_c() - .await - .expect("failed to install CTRL+C signal handler"); - }; +fn main() { + real_main().unwrap(); + std::process::exit(0); +} - server::listen(&server_config, shutdown_signal) -} \ No newline at end of file +fn real_main() -> Result<(), Box> { + let yml = load_yaml!("../mwixnet.yml"); + let args = App::from_yaml(yml).get_matches(); + + let config_path = match args.value_of("config_file") { + Some(path) => PathBuf::from(path), + None => { + let mut grin_path = config::get_grin_path(); + grin_path.push("mwixnet-config.toml"); + grin_path + } + }; + + let round_time = args + .value_of("round_time") + .map(|t| t.parse::().unwrap()); + let bind_addr = args.value_of("bind_addr"); + let grin_node_url = args.value_of("grin_node_url"); + let grin_node_secret_path = args.value_of("grin_node_secret_path"); + let wallet_owner_url = args.value_of("wallet_owner_url"); + let wallet_owner_secret_path = args.value_of("wallet_owner_secret_path"); + + // Write a new config file if init-config command is supplied + if let ("init-config", Some(_)) = args.subcommand() { + if config_path.exists() { + panic!( + "Config file already exists at {}", + config_path.to_string_lossy() + ); + } + + let server_config = ServerConfig { + key: secp::random_secret(), + interval_s: round_time.unwrap_or(DEFAULT_INTERVAL), + addr: bind_addr.unwrap_or("0.0.0.0:3000").parse()?, + grin_node_url: grin_node_url.unwrap_or("127.0.0.1:3413").parse()?, + grin_node_secret_path: match grin_node_secret_path { + Some(p) => Some(p.to_owned()), + None => config::node_secret_path().to_str().map(|p| p.to_owned()), + }, + wallet_owner_url: wallet_owner_url.unwrap_or("127.0.0.1:3420").parse()?, + wallet_owner_secret_path: match wallet_owner_secret_path { + Some(p) => Some(p.to_owned()), + None => config::wallet_owner_secret_path() + .to_str() + .map(|p| p.to_owned()), + }, + }; + + let password = prompt_password_confirm(); + config::write_config(&config_path, &server_config, &password)?; + println!( + "Config file written to {:?}. Please back this file up in a safe place.", + config_path + ); + return Ok(()); + } + + let password = prompt_password(); + let mut server_config = config::load_config(&config_path, &password)?; + + // Override bind_addr, if supplied + if let Some(bind_addr) = bind_addr { + server_config.addr = bind_addr.parse()?; + } + + // Override grin_node_url, if supplied + if let Some(grin_node_url) = grin_node_url { + server_config.grin_node_url = grin_node_url.parse()?; + } + + // Override grin_node_secret_path, if supplied + if let Some(grin_node_secret_path) = grin_node_secret_path { + server_config.grin_node_secret_path = Some(grin_node_secret_path.to_owned()); + } + + // Override wallet_owner_url, if supplied + if let Some(wallet_owner_url) = wallet_owner_url { + server_config.wallet_owner_url = wallet_owner_url.parse()?; + } + + // Override wallet_owner_secret_path, if supplied + if let Some(wallet_owner_secret_path) = wallet_owner_secret_path { + server_config.wallet_owner_secret_path = Some(wallet_owner_secret_path.to_owned()); + } + + // Open wallet + let wallet_pass = prompt_wallet_password(&args.value_of("wallet_pass")); + let wallet = HttpWallet::open_wallet( + &server_config.wallet_owner_url, + &server_config.wallet_owner_api_secret(), + &wallet_pass, + )?; + + // Create GrinNode + let node = HttpGrinNode::new( + &server_config.grin_node_url, + &server_config.node_api_secret(), + ); + + let stop_state = Arc::new(StopState::new()); + let stop_state_clone = stop_state.clone(); + + let rt = Runtime::new()?; + rt.spawn(async move { + futures::executor::block_on(build_signals_fut()); + stop_state_clone.stop(); + }); + + // Start the mwixnet JSON-RPC HTTP server + rpc::listen(server_config, Arc::new(wallet), Arc::new(node), stop_state) +} + +async fn build_signals_fut() { + if cfg!(unix) { + use tokio::signal::unix::{signal, SignalKind}; + + // Listen for SIGINT, SIGQUIT, and SIGTERM + let mut terminate_signal = + signal(SignalKind::terminate()).expect("failed to create terminate signal"); + let mut quit_signal = signal(SignalKind::quit()).expect("failed to create quit signal"); + let mut interrupt_signal = + signal(SignalKind::interrupt()).expect("failed to create interrupt signal"); + + futures::future::select_all(vec![ + Box::pin(terminate_signal.recv()), + Box::pin(quit_signal.recv()), + Box::pin(interrupt_signal.recv()), + ]) + .await; + } else { + tokio::signal::ctrl_c() + .await + .expect("failed to install CTRL+C signal handler"); + } +} + +fn prompt_password() -> ZeroingString { + ZeroingString::from(rpassword::prompt_password_stdout("Server password: ").unwrap()) +} + +fn prompt_password_confirm() -> ZeroingString { + let mut first = "first".to_string(); + let mut second = "second".to_string(); + while first != second { + first = rpassword::prompt_password_stdout("Server password: ").unwrap(); + second = rpassword::prompt_password_stdout("Confirm server password: ").unwrap(); + } + ZeroingString::from(first) +} + +fn prompt_wallet_password(wallet_pass: &Option<&str>) -> ZeroingString { + match *wallet_pass { + Some(wallet_pass) => ZeroingString::from(wallet_pass), + None => { + ZeroingString::from(rpassword::prompt_password_stdout("Wallet password: ").unwrap()) + } + } +} diff --git a/src/node.rs b/src/node.rs new file mode 100644 index 0000000..a32ea22 --- /dev/null +++ b/src/node.rs @@ -0,0 +1,219 @@ +use crate::error::{ErrorKind, Result}; +use crate::secp::Commitment; + +use grin_api::client; +use grin_api::json_rpc::{build_request, Request, Response}; +use grin_api::{OutputPrintable, OutputType, Tip}; +use grin_core::consensus::COINBASE_MATURITY; +use grin_core::core::{Input, OutputFeatures, Transaction}; +use grin_util::ToHex; + +use serde_json::json; +use std::net::SocketAddr; +use std::sync::Arc; + +pub trait GrinNode: Send + Sync { + /// Retrieves the unspent output with a matching commitment + fn get_utxo(&self, output_commit: &Commitment) -> Result>; + + /// Gets the height of the chain tip + fn get_chain_height(&self) -> Result; + + /// Posts a transaction to the grin node + fn post_tx(&self, tx: &Transaction) -> Result<()>; +} + +/// Checks if a commitment is in the UTXO set +pub fn is_unspent(node: &Arc, commit: &Commitment) -> Result { + let utxo = node.get_utxo(&commit)?; + Ok(utxo.is_some()) +} + +/// Checks whether a commitment is spendable at the block height provided +pub fn is_spendable( + node: &Arc, + output_commit: &Commitment, + next_block_height: u64, +) -> Result { + let output = node.get_utxo(&output_commit)?; + if let Some(out) = output { + let is_coinbase = match out.output_type { + OutputType::Coinbase => true, + OutputType::Transaction => false, + }; + + if is_coinbase { + if let Some(block_height) = out.block_height { + if block_height + COINBASE_MATURITY < next_block_height { + return Ok(false); + } + } else { + return Ok(false); + } + } + + return Ok(true); + } + + Ok(false) +} + +/// Builds an input for an unspent output commitment +pub fn build_input(node: &Arc, output_commit: &Commitment) -> Result> { + let output = node.get_utxo(&output_commit)?; + + if let Some(out) = output { + let features = match out.output_type { + OutputType::Coinbase => OutputFeatures::Coinbase, + OutputType::Transaction => OutputFeatures::Plain, + }; + + let input = Input::new(features, out.commit); + return Ok(Some(input)); + } + + Ok(None) +} + +/// HTTP (JSON-RPC) implementation of the 'GrinNode' trait +#[derive(Clone)] +pub struct HttpGrinNode { + node_url: SocketAddr, + node_api_secret: Option, +} + +const ENDPOINT: &str = "/v2/foreign"; + +impl HttpGrinNode { + pub fn new(node_url: &SocketAddr, node_api_secret: &Option) -> HttpGrinNode { + HttpGrinNode { + node_url: node_url.to_owned(), + node_api_secret: node_api_secret.to_owned(), + } + } + + fn send_json_request( + &self, + method: &str, + params: &serde_json::Value, + ) -> Result { + let url = format!("http://{}{}", self.node_url, ENDPOINT); + let req = build_request(method, params); + let res = + client::post::(url.as_str(), self.node_api_secret.clone(), &req)?; + let parsed = res.clone().into_result()?; + Ok(parsed) + } +} + +impl GrinNode for HttpGrinNode { + fn get_utxo(&self, output_commit: &Commitment) -> Result> { + let commits: Vec = vec![output_commit.to_hex()]; + let start_height: Option = None; + let end_height: Option = None; + let include_proof: Option = Some(false); + let include_merkle_proof: Option = Some(false); + + let params = json!([ + Some(commits), + start_height, + end_height, + include_proof, + include_merkle_proof + ]); + let outputs = self.send_json_request::>("get_outputs", ¶ms)?; + if outputs.is_empty() { + return Ok(None); + } + + Ok(Some(outputs[0].clone())) + } + + fn get_chain_height(&self) -> Result { + let params = json!([]); + let tip_json = self.send_json_request::("get_tip", ¶ms)?; + + let tip: Result = serde_json::from_value(tip_json["Ok"].clone()) + .map_err(|e| ErrorKind::SerdeJsonError(e.to_string()).into()); + + Ok(tip?.height) + } + + fn post_tx(&self, tx: &Transaction) -> Result<()> { + let params = json!([tx, true]); + self.send_json_request::("push_transaction", ¶ms)?; + Ok(()) + } +} + +#[cfg(test)] +pub mod mock { + use super::GrinNode; + use crate::error::Result; + use crate::secp::Commitment; + + use grin_api::{OutputPrintable, OutputType}; + use grin_core::core::Transaction; + use std::collections::HashMap; + use std::sync::RwLock; + + /// Implementation of 'GrinNode' trait that mocks a grin node instance. + /// Use only for testing purposes. + pub struct MockGrinNode { + utxos: HashMap, + txns_posted: RwLock>, + } + + impl MockGrinNode { + pub fn new() -> MockGrinNode { + MockGrinNode { + utxos: HashMap::new(), + txns_posted: RwLock::new(Vec::new()), + } + } + + pub fn add_utxo(&mut self, output_commit: &Commitment, utxo: &OutputPrintable) { + self.utxos.insert(output_commit.clone(), utxo.clone()); + } + + pub fn add_default_utxo(&mut self, output_commit: &Commitment) { + let utxo = OutputPrintable { + output_type: OutputType::Transaction, + commit: output_commit.to_owned(), + spent: false, + proof: None, + proof_hash: String::from(""), + block_height: None, + merkle_proof: None, + mmr_index: 0, + }; + + self.add_utxo(&output_commit, &utxo); + } + + pub fn get_posted_txns(&self) -> Vec { + let read = self.txns_posted.read().unwrap(); + read.clone() + } + } + + impl GrinNode for MockGrinNode { + fn get_utxo(&self, output_commit: &Commitment) -> Result> { + if let Some(utxo) = self.utxos.get(&output_commit) { + return Ok(Some(utxo.clone())); + } + + Ok(None) + } + + fn get_chain_height(&self) -> Result { + Ok(100) + } + + fn post_tx(&self, tx: &Transaction) -> Result<()> { + let mut write = self.txns_posted.write().unwrap(); + write.push(tx.clone()); + Ok(()) + } + } +} diff --git a/src/onion.rs b/src/onion.rs index 8c49e85..cc59280 100644 --- a/src/onion.rs +++ b/src/onion.rs @@ -1,167 +1,348 @@ use crate::error::Result; use crate::secp::{self, Commitment, PublicKey, Secp256k1, SecretKey, SharedSecret}; -use crate::types::{Hop, Onion, RawBytes, Payload, deserialize_payload, serialize_payload}; -use crate::ser; +use crate::types::Payload; -use chacha20::{ChaCha20, Key, Nonce}; use chacha20::cipher::{NewCipher, StreamCipher}; -use hmac::{Hmac, Mac, NewMac}; +use chacha20::{ChaCha20, Key, Nonce}; +use grin_core::ser::{self, ProtocolVersion, Writeable, Writer}; +use grin_util::{self, ToHex}; +use hmac::{Hmac, Mac}; +use serde::ser::SerializeStruct; +use serde::Deserialize; use sha2::{Digest, Sha256}; +use std::fmt; type HmacSha256 = Hmac; +type RawBytes = Vec; -/// Create an Onion for the Commitment, encrypting the payload for each hop -pub fn create_onion(commitment: &Commitment, session_key: &SecretKey, hops: &Vec) -> Result { - let secp = Secp256k1::new(); - let mut ephemeral_key = session_key.clone(); - - let mut shared_secrets: Vec = Vec::new(); - let mut enc_payloads: Vec = Vec::new(); - for hop in hops { - let shared_secret = SharedSecret::new(&secp, &hop.pubkey, &ephemeral_key); - - let ephemeral_pubkey = PublicKey::from_secret_key(&secp, &ephemeral_key)?; - let blinding_factor = calc_blinding_factor(&shared_secret, &ephemeral_pubkey)?; - - shared_secrets.push(shared_secret); - enc_payloads.push(serialize_payload(&hop.payload)?); - ephemeral_key.mul_assign(&secp, &blinding_factor)?; - } - - for i in (0..shared_secrets.len()).rev() { - let mut cipher = new_stream_cipher(&shared_secrets[i])?; - for j in i..shared_secrets.len() { - cipher.apply_keystream(&mut enc_payloads[j]); - } - } - - let onion = Onion{ - ephemeral_pubkey: secp::to_public_key(&session_key)?, - commit: commitment.clone(), - enc_payloads: enc_payloads, - }; - Ok(onion) +/// A data packet with layers of encryption +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct Onion { + /// The onion originator's portion of the shared secret + pub ephemeral_pubkey: PublicKey, + /// The pedersen commitment before adjusting the excess and subtracting the fee + pub commit: Commitment, + /// The encrypted payloads which represent the layers of the onion + pub enc_payloads: Vec, } -/// Peel a single layer off of the Onion, returning the peeled Onion and decrypted Payload -pub fn peel_layer(onion: &Onion, secret_key: &SecretKey) -> Result<(Payload, Onion)> { - let secp = Secp256k1::new(); - - let shared_secret = SharedSecret::new(&secp, &onion.ephemeral_pubkey, &secret_key); - let mut cipher = new_stream_cipher(&shared_secret)?; +impl Onion { + pub fn serialize(&self) -> Result> { + let mut vec = vec![]; + ser::serialize_default(&mut vec, &self)?; + Ok(vec) + } - let mut decrypted_bytes = onion.enc_payloads[0].clone(); - cipher.apply_keystream(&mut decrypted_bytes); - let decrypted_payload = deserialize_payload(&decrypted_bytes)?; + /// Peel a single layer off of the Onion, returning the peeled Onion and decrypted Payload + pub fn peel_layer(&self, secret_key: &SecretKey) -> Result<(Payload, Onion)> { + let secp = Secp256k1::new(); - let enc_payloads : Vec = onion.enc_payloads.iter() - .enumerate() - .filter(|&(i, _)| i != 0) - .map(|(_, enc_payload)| { - let mut p = enc_payload.clone(); - cipher.apply_keystream(&mut p); - p - }) - .collect(); + let shared_secret = SharedSecret::new(&secp, &self.ephemeral_pubkey, &secret_key); + let mut cipher = new_stream_cipher(&shared_secret)?; - let blinding_factor = calc_blinding_factor(&shared_secret, &onion.ephemeral_pubkey)?; - - let mut ephemeral_pubkey = onion.ephemeral_pubkey.clone(); - ephemeral_pubkey.mul_assign(&secp, &blinding_factor)?; + let mut decrypted_bytes = self.enc_payloads[0].clone(); + cipher.apply_keystream(&mut decrypted_bytes); + let decrypted_payload = Payload::deserialize(&decrypted_bytes)?; - let mut commitment = onion.commit.clone(); - commitment = secp::add_excess(&commitment, &decrypted_payload.excess)?; + let enc_payloads: Vec = self + .enc_payloads + .iter() + .enumerate() + .filter(|&(i, _)| i != 0) + .map(|(_, enc_payload)| { + let mut p = enc_payload.clone(); + cipher.apply_keystream(&mut p); + p + }) + .collect(); - let peeled_onion = Onion{ - ephemeral_pubkey: ephemeral_pubkey, - commit: commitment.clone(), - enc_payloads: enc_payloads, - }; - Ok((decrypted_payload, peeled_onion)) + let blinding_factor = calc_blinding_factor(&shared_secret, &self.ephemeral_pubkey)?; + + let mut ephemeral_pubkey = self.ephemeral_pubkey.clone(); + ephemeral_pubkey.mul_assign(&secp, &blinding_factor)?; + + let mut commitment = self.commit.clone(); + commitment = secp::add_excess(&commitment, &decrypted_payload.excess)?; + commitment = secp::sub_value(&commitment, decrypted_payload.fee.into())?; + + let peeled_onion = Onion { + ephemeral_pubkey, + commit: commitment.clone(), + enc_payloads, + }; + Ok((decrypted_payload, peeled_onion)) + } } -fn calc_blinding_factor(shared_secret: &SharedSecret, ephemeral_pubkey: &PublicKey) -> Result { - let serialized_pubkey = ser::ser_vec(&ephemeral_pubkey)?; +fn calc_blinding_factor( + shared_secret: &SharedSecret, + ephemeral_pubkey: &PublicKey, +) -> Result { + let serialized_pubkey = ser::ser_vec(&ephemeral_pubkey, ProtocolVersion::local())?; - let mut hasher = Sha256::default(); - hasher.update(&serialized_pubkey); - hasher.update(&shared_secret[0..32]); - - let secp = Secp256k1::new(); - let blind = SecretKey::from_slice(&secp, &hasher.finalize())?; - Ok(blind) + let mut hasher = Sha256::default(); + hasher.update(&serialized_pubkey); + hasher.update(&shared_secret[0..32]); + + let secp = Secp256k1::new(); + let blind = SecretKey::from_slice(&secp, &hasher.finalize())?; + Ok(blind) } fn new_stream_cipher(shared_secret: &SharedSecret) -> Result { - let mut mu_hmac = HmacSha256::new_from_slice(b"MWIXNET")?; - mu_hmac.update(&shared_secret[0..32]); - let mukey = mu_hmac.finalize().into_bytes(); + let mut mu_hmac = HmacSha256::new_from_slice(b"MWIXNET")?; + mu_hmac.update(&shared_secret[0..32]); + let mukey = mu_hmac.finalize().into_bytes(); - let key = Key::from_slice(&mukey[0..32]); - let nonce = Nonce::from_slice(b"NONCE1234567"); - - Ok(ChaCha20::new(&key, &nonce)) + let key = Key::from_slice(&mukey[0..32]); + let nonce = Nonce::from_slice(b"NONCE1234567"); + + Ok(ChaCha20::new(&key, &nonce)) +} + +impl Writeable for Onion { + fn write(&self, writer: &mut W) -> std::result::Result<(), ser::Error> { + self.ephemeral_pubkey.write(writer)?; + writer.write_fixed_bytes(&self.commit)?; + writer.write_u64(self.enc_payloads.len() as u64)?; + for p in &self.enc_payloads { + writer.write_u64(p.len() as u64)?; + p.write(writer)?; + } + Ok(()) + } +} + +impl serde::ser::Serialize for Onion { + fn serialize(&self, serializer: S) -> std::result::Result + where + S: serde::ser::Serializer, + { + let mut state = serializer.serialize_struct("Onion", 3)?; + + let secp = Secp256k1::new(); + state.serialize_field( + "pubkey", + &self.ephemeral_pubkey.serialize_vec(&secp, true).to_hex(), + )?; + state.serialize_field("commit", &self.commit.to_hex())?; + + let hex_payloads: Vec = self.enc_payloads.iter().map(|v| v.to_hex()).collect(); + state.serialize_field("data", &hex_payloads)?; + state.end() + } +} + +impl<'de> serde::de::Deserialize<'de> for Onion { + fn deserialize(deserializer: D) -> std::result::Result + where + D: serde::de::Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(field_identifier, rename_all = "snake_case")] + enum Field { + Pubkey, + Commit, + Data, + } + + struct OnionVisitor; + + impl<'de> serde::de::Visitor<'de> for OnionVisitor { + type Value = Onion; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("an Onion") + } + + fn visit_map(self, mut map: A) -> std::result::Result + where + A: serde::de::MapAccess<'de>, + { + let mut pubkey = None; + let mut commit = None; + let mut data = None; + + while let Some(key) = map.next_key()? { + match key { + Field::Pubkey => { + let val: String = map.next_value()?; + let vec = + grin_util::from_hex(&val).map_err(serde::de::Error::custom)?; + let secp = Secp256k1::new(); + pubkey = Some( + PublicKey::from_slice(&secp, &vec[..]) + .map_err(serde::de::Error::custom)?, + ); + } + Field::Commit => { + let val: String = map.next_value()?; + let vec = + grin_util::from_hex(&val).map_err(serde::de::Error::custom)?; + commit = Some(Commitment::from_vec(vec)); + } + Field::Data => { + let val: Vec = map.next_value()?; + let mut vec: Vec> = Vec::new(); + for hex in val { + vec.push( + grin_util::from_hex(&hex).map_err(serde::de::Error::custom)?, + ); + } + data = Some(vec); + } + } + } + + Ok(Onion { + ephemeral_pubkey: pubkey.unwrap(), + commit: commit.unwrap(), + enc_payloads: data.unwrap(), + }) + } + } + + const FIELDS: &[&str] = &["pubkey", "commit", "data"]; + deserializer.deserialize_struct("Onion", &FIELDS, OnionVisitor) + } } #[cfg(test)] -mod tests { - use super::super::secp; - use super::super::types; - use super::super::onion; +pub mod test_util { + use super::{Onion, RawBytes}; + use crate::error::Result; + use crate::secp::{Commitment, PublicKey, Secp256k1, SecretKey, SharedSecret}; + use crate::types::Payload; - /// Test end-to-end Onion creation and unwrapping logic. - #[test] - fn onion() { - let value : u64 = 1000; - let blind = secp::insecure_rand_secret().unwrap(); - let commitment = secp::commit(value, &blind).unwrap(); - - let session_key = secp::insecure_rand_secret().unwrap(); - let mut hops : Vec = Vec::new(); - - let mut keys : Vec = Vec::new(); - let mut final_commit = commitment.clone(); - let mut final_blind = blind.clone(); - for i in 0..5 { - keys.push(secp::insecure_rand_secret().unwrap()); - - let excess = secp::insecure_rand_secret().unwrap(); - - let secp = secp256k1zkp::Secp256k1::with_caps(secp256k1zkp::ContextFlag::Commit); - final_blind.add_assign(&secp, &excess).unwrap(); - final_commit = secp::add_excess(&final_commit, &excess).unwrap(); - let proof = if i == 4 { - let n1 = secp::insecure_rand_secret().unwrap(); - let rp = secp.bullet_proof(value, final_blind.clone(), n1.clone(), n1.clone(), None, None); - assert!(secp.verify_bullet_proof(final_commit, rp, None).is_ok()); - Some(rp) - } else { - None - }; - - hops.push(types::Hop{ - pubkey: secp::PublicKey::from_secret_key(&secp, &keys[i]).unwrap(), - payload: types::Payload{ - excess: excess, - rangeproof: proof, - } - }); - } - - let mut onion_packet = onion::create_onion(&commitment, &session_key, &hops).unwrap(); - - let mut payload = types::Payload{ - excess: secp::insecure_rand_secret().unwrap(), - rangeproof: None - }; - for i in 0..5 { - let peeled = onion::peel_layer(&onion_packet, &keys[i]).unwrap(); - payload = peeled.0; - onion_packet = peeled.1; - } - - assert!(payload.rangeproof.is_some()); - assert_eq!(payload.rangeproof.unwrap(), hops[4].payload.rangeproof.unwrap()); - assert_eq!(secp::commit(value, &final_blind).unwrap(), final_commit); - } -} \ No newline at end of file + use crate::secp; + use chacha20::cipher::StreamCipher; + + #[derive(Clone)] + pub struct Hop { + pub pubkey: PublicKey, + pub payload: Payload, + } + + /// Create an Onion for the Commitment, encrypting the payload for each hop + pub fn create_onion(commitment: &Commitment, hops: &Vec) -> Result { + let secp = Secp256k1::new(); + let session_key = secp::random_secret(); + let mut ephemeral_key = session_key.clone(); + + let mut shared_secrets: Vec = Vec::new(); + let mut enc_payloads: Vec = Vec::new(); + for hop in hops { + let shared_secret = SharedSecret::new(&secp, &hop.pubkey, &ephemeral_key); + + let ephemeral_pubkey = PublicKey::from_secret_key(&secp, &ephemeral_key)?; + let blinding_factor = super::calc_blinding_factor(&shared_secret, &ephemeral_pubkey)?; + + shared_secrets.push(shared_secret); + enc_payloads.push(hop.payload.serialize()?); + ephemeral_key.mul_assign(&secp, &blinding_factor)?; + } + + for i in (0..shared_secrets.len()).rev() { + let mut cipher = super::new_stream_cipher(&shared_secrets[i])?; + for j in i..shared_secrets.len() { + cipher.apply_keystream(&mut enc_payloads[j]); + } + } + + let onion = Onion { + ephemeral_pubkey: PublicKey::from_secret_key(&secp, &session_key)?, + commit: commitment.clone(), + enc_payloads, + }; + Ok(onion) + } + + /// Calculates the expected next ephemeral pubkey after peeling a layer off of the Onion. + pub fn next_ephemeral_pubkey(onion: &Onion, server_key: &SecretKey) -> Result { + let secp = Secp256k1::new(); + let mut ephemeral_pubkey = onion.ephemeral_pubkey.clone(); + let shared_secret = SharedSecret::new(&secp, &ephemeral_pubkey, &server_key); + let blinding_factor = super::calc_blinding_factor(&shared_secret, &ephemeral_pubkey)?; + ephemeral_pubkey.mul_assign(&secp, &blinding_factor)?; + Ok(ephemeral_pubkey) + } +} + +#[cfg(test)] +pub mod tests { + use super::test_util::{self, Hop}; + use crate::secp; + use crate::types::Payload; + + use grin_core::core::FeeFields; + + /// Test end-to-end Onion creation and unwrapping logic. + #[test] + fn onion() { + let total_fee: u64 = 10; + let fee_per_hop: u64 = 2; + let in_value: u64 = 1000; + let out_value: u64 = in_value - total_fee; + let blind = secp::random_secret(); + let commitment = secp::commit(in_value, &blind).unwrap(); + + let mut hops: Vec = Vec::new(); + let mut keys: Vec = Vec::new(); + let mut final_commit = secp::commit(out_value, &blind).unwrap(); + let mut final_blind = blind.clone(); + for i in 0..5 { + keys.push(secp::random_secret()); + + let excess = secp::random_secret(); + + let secp = secp256k1zkp::Secp256k1::with_caps(secp256k1zkp::ContextFlag::Commit); + final_blind.add_assign(&secp, &excess).unwrap(); + final_commit = secp::add_excess(&final_commit, &excess).unwrap(); + let proof = if i == 4 { + let n1 = secp::random_secret(); + let rp = secp.bullet_proof( + out_value, + final_blind.clone(), + n1.clone(), + n1.clone(), + None, + None, + ); + assert!(secp.verify_bullet_proof(final_commit, rp, None).is_ok()); + Some(rp) + } else { + None + }; + + hops.push(Hop { + pubkey: secp::PublicKey::from_secret_key(&secp, &keys[i]).unwrap(), + payload: Payload { + excess, + fee: FeeFields::from(fee_per_hop as u32), + rangeproof: proof, + }, + }); + } + + let mut onion_packet = test_util::create_onion(&commitment, &hops).unwrap(); + + let mut payload = Payload { + excess: secp::random_secret(), + fee: FeeFields::from(fee_per_hop as u32), + rangeproof: None, + }; + for i in 0..5 { + let peeled = onion_packet.peel_layer(&keys[i]).unwrap(); + payload = peeled.0; + onion_packet = peeled.1; + } + + assert!(payload.rangeproof.is_some()); + assert_eq!( + payload.rangeproof.unwrap(), + hops[4].payload.rangeproof.unwrap() + ); + assert_eq!(secp::commit(out_value, &final_blind).unwrap(), final_commit); + assert_eq!(payload.fee, FeeFields::from(fee_per_hop as u32)); + } +} diff --git a/src/rpc.rs b/src/rpc.rs new file mode 100644 index 0000000..eac079b --- /dev/null +++ b/src/rpc.rs @@ -0,0 +1,271 @@ +use crate::config::ServerConfig; +use crate::node::GrinNode; +use crate::onion::Onion; +use crate::secp::{self, ComSignature}; +use crate::server::{Server, ServerImpl, SwapError}; +use crate::wallet::Wallet; + +use grin_util::StopState; +use jsonrpc_core::{Result, Value}; +use jsonrpc_derive::rpc; +use jsonrpc_http_server::jsonrpc_core::*; +use jsonrpc_http_server::*; +use serde::{Deserialize, Serialize}; +use std::sync::{Arc, Mutex}; +use std::thread::{sleep, spawn}; +use std::time::Duration; + +#[derive(Serialize, Deserialize)] +pub struct SwapReq { + onion: Onion, + #[serde(with = "secp::comsig_serde")] + comsig: ComSignature, +} + +#[rpc(server)] +pub trait API { + #[rpc(name = "swap")] + fn swap(&self, swap: SwapReq) -> Result; + + // milestone 3: Used by mwixnet coinswap servers to communicate with each other + // fn derive_outputs(&self, entries: Vec) -> Result; + // fn derive_kernel(&self, tx: Tx) -> Result; +} + +#[derive(Clone)] +struct RPCServer { + server_config: ServerConfig, + server: Arc>, +} + +impl RPCServer { + /// Spin up an instance of the JSON-RPC HTTP server. + fn start_http(&self) -> jsonrpc_http_server::Server { + let mut io = IoHandler::new(); + io.extend_with(RPCServer::to_delegate(self.clone())); + + ServerBuilder::new(io) + .cors(DomainsValidation::Disabled) + .request_middleware(|request: hyper::Request| { + if request.uri() == "/v1" { + request.into() + } else { + jsonrpc_http_server::Response::bad_request("Only v1 supported").into() + } + }) + .start_http(&self.server_config.addr) + .expect("Unable to start RPC server") + } +} + +impl From for Error { + fn from(e: SwapError) -> Self { + match e { + SwapError::UnknownError(_) => Error { + message: e.to_string(), + code: ErrorCode::InternalError, + data: None, + }, + _ => Error::invalid_params(e.to_string()), + } + } +} + +impl API for RPCServer { + /// Implements the 'swap' API + fn swap(&self, swap: SwapReq) -> Result { + self.server + .lock() + .unwrap() + .swap(&swap.onion, &swap.comsig)?; + Ok(Value::String("success".into())) + } +} + +/// Spin up the JSON-RPC web server +pub fn listen( + server_config: ServerConfig, + wallet: Arc, + node: Arc, + stop_state: Arc, +) -> std::result::Result<(), Box> { + let server = ServerImpl::new(server_config.clone(), wallet.clone(), node.clone()); + let server = Arc::new(Mutex::new(server)); + + let rpc_server = RPCServer { + server_config: server_config.clone(), + server: server.clone(), + }; + + let http_server = rpc_server.start_http(); + println!("Server listening on {}", server_config.addr); + + let close_handle = http_server.close_handle(); + let round_handle = spawn(move || { + let mut secs = 0; + loop { + if stop_state.is_stopped() { + close_handle.close(); + break; + } + + sleep(Duration::from_secs(1)); + secs = (secs + 1) % server_config.interval_s; + + if secs == 0 { + let _ = server.lock().unwrap().execute_round(); + } + } + }); + + http_server.wait(); + round_handle.join().unwrap(); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use crate::config::ServerConfig; + use crate::onion::test_util; + use crate::rpc::{RPCServer, SwapReq}; + use crate::secp::{self, ComSignature}; + use crate::server::mock::MockServer; + use crate::server::{Server, SwapError}; + + use std::net::TcpListener; + use std::sync::{Arc, Mutex}; + + use hyper::{Body, Client, Request, Response}; + use tokio::runtime::Runtime; + + async fn body_to_string(req: Response) -> String { + let body_bytes = hyper::body::to_bytes(req.into_body()).await.unwrap(); + String::from_utf8(body_bytes.to_vec()).unwrap() + } + + /// Spin up a temporary web service, query the API, then cleanup and return response + fn make_request( + server: Arc>, + req: String, + ) -> Result> { + let server_config = ServerConfig { + key: secp::random_secret(), + interval_s: 1, + addr: TcpListener::bind("127.0.0.1:0")?.local_addr()?, + grin_node_url: "127.0.0.1:3413".parse()?, + grin_node_secret_path: None, + wallet_owner_url: "127.0.0.1:3420".parse()?, + wallet_owner_secret_path: None, + }; + + let rpc_server = RPCServer { + server_config: server_config.clone(), + server: server.clone(), + }; + + // Start the JSON-RPC server + let http_server = rpc_server.start_http(); + + let uri = format!("http://{}/v1", server_config.addr); + + let threaded_rt = Runtime::new()?; + let do_request = async move { + let request = Request::post(uri) + .header("Content-Type", "application/json") + .body(Body::from(req)) + .unwrap(); + + Client::new().request(request).await + }; + + let response = threaded_rt.block_on(do_request)?; + let response_str: String = threaded_rt.block_on(body_to_string(response)); + + // Wait for shutdown + threaded_rt.shutdown_background(); + + // Execute one round + server.lock().unwrap().execute_round()?; + + // Stop the server + http_server.close(); + + Ok(response_str) + } + + // todo: Test all error types + + /// Demonstrates a successful swap response + #[test] + fn swap_success() -> Result<(), Box> { + let commitment = secp::commit(1234, &secp::random_secret())?; + let onion = test_util::create_onion(&commitment, &vec![])?; + let comsig = ComSignature::sign(1234, &secp::random_secret(), &onion.serialize()?)?; + let swap = SwapReq { + onion: onion.clone(), + comsig, + }; + + let server: Arc> = Arc::new(Mutex::new(MockServer::new())); + + let req = format!( + "{{\"jsonrpc\": \"2.0\", \"method\": \"swap\", \"params\": [{}], \"id\": \"1\"}}", + serde_json::json!(swap) + ); + println!("Request: {}", req); + let response = make_request(server, req)?; + let expected = "{\"jsonrpc\":\"2.0\",\"result\":\"success\",\"id\":\"1\"}\n"; + assert_eq!(response, expected); + + Ok(()) + } + + #[test] + fn swap_bad_request() -> Result<(), Box> { + let server: Arc> = Arc::new(Mutex::new(MockServer::new())); + + let params = "{ \"param\": \"Not a valid Swap request\" }"; + let req = format!( + "{{\"jsonrpc\": \"2.0\", \"method\": \"swap\", \"params\": [{}], \"id\": \"1\"}}", + params + ); + let response = make_request(server, req)?; + let expected = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: missing field `onion`.\"},\"id\":\"1\"}\n"; + assert_eq!(response, expected); + Ok(()) + } + + /// Returns "Commitment not found" when there's no matching output in the UTXO set. + #[test] + fn swap_utxo_missing() -> Result<(), Box> { + let commitment = secp::commit(1234, &secp::random_secret())?; + let onion = test_util::create_onion(&commitment, &vec![])?; + let comsig = ComSignature::sign(1234, &secp::random_secret(), &onion.serialize()?)?; + let swap = SwapReq { + onion: onion.clone(), + comsig, + }; + + let mut server = MockServer::new(); + server.set_response( + &onion, + SwapError::CoinNotFound { + commit: commitment.clone(), + }, + ); + let server: Arc> = Arc::new(Mutex::new(server)); + + let req = format!( + "{{\"jsonrpc\": \"2.0\", \"method\": \"swap\", \"params\": [{}], \"id\": \"1\"}}", + serde_json::json!(swap) + ); + let response = make_request(server, req)?; + let expected = format!( + "{{\"jsonrpc\":\"2.0\",\"error\":{{\"code\":-32602,\"message\":\"Output {:?} does not exist, or is already spent.\"}},\"id\":\"1\"}}\n", + commitment + ); + assert_eq!(response, expected); + Ok(()) + } +} diff --git a/src/secp.rs b/src/secp.rs index 44267be..8196c4c 100644 --- a/src/secp.rs +++ b/src/secp.rs @@ -1,194 +1,227 @@ -pub use secp256k1zkp::{ContextFlag, Message, Secp256k1, Signature}; +pub use secp256k1zkp::aggsig; +pub use secp256k1zkp::constants::{ + AGG_SIGNATURE_SIZE, COMPRESSED_PUBLIC_KEY_SIZE, MAX_PROOF_SIZE, PEDERSEN_COMMITMENT_SIZE, + SECRET_KEY_SIZE, +}; pub use secp256k1zkp::ecdh::SharedSecret; +pub use secp256k1zkp::key::{PublicKey, SecretKey, ZERO_KEY}; pub use secp256k1zkp::pedersen::{Commitment, RangeProof}; -pub use secp256k1zkp::key::{PublicKey, SecretKey}; -pub use secp256k1zkp::constants::{AGG_SIGNATURE_SIZE, COMPRESSED_PUBLIC_KEY_SIZE, MAX_PROOF_SIZE, PEDERSEN_COMMITMENT_SIZE, SECRET_KEY_SIZE}; +pub use secp256k1zkp::{ContextFlag, Message, Secp256k1, Signature}; -use crate::ser::{Readable, Reader, Writeable, Writer}; -use crate::error::{ErrorKind, Result}; +use crate::error::{Error, ErrorKind, Result}; -use rand::RngCore; -use std::cmp; +use blake2::blake2b::Blake2b; +use byteorder::{BigEndian, ByteOrder}; +use grin_core::ser::{self, Readable, Reader, Writeable, Writer}; +use secp256k1zkp::rand::thread_rng; /// A generalized Schnorr signature with a pedersen commitment value & blinding factors as the keys -pub const COM_SIGNATURE_SIZE : usize = 96; - -pub struct ComSignature(pub [u8; COM_SIGNATURE_SIZE]); -impl ComSignature { - /// Builds a ComSignature from a byte vector. If the vector is too short, it will be - /// completed by zeroes. If it's too long, it will be truncated. - pub fn from_vec(v: Vec) -> ComSignature { - let mut h = [0; COM_SIGNATURE_SIZE]; - for i in 0..cmp::min(v.len(), COM_SIGNATURE_SIZE) { - h[i] = v[i]; - } - ComSignature(h) - } - - #[allow(dead_code)] - pub fn sign(_value: u64, _blind: &SecretKey, _msg: &Vec) -> Result { - // milestone 2 - todo - let mut h = [0u8; COM_SIGNATURE_SIZE]; - for i in 0..COM_SIGNATURE_SIZE { - h[i] = i as u8; - } - Ok(ComSignature(h)) - } - - pub fn verify(self, _commit: &Commitment, _msg: &Vec) -> Result<()> { - // milestone 2 - todo - Ok(()) - } +#[derive(Clone)] +pub struct ComSignature { + pub_nonce: Commitment, + s: SecretKey, + t: SecretKey, } -impl AsRef<[u8]> for ComSignature { - fn as_ref(&self) -> &[u8] { - &self.0 - } +impl ComSignature { + pub fn new(pub_nonce: &Commitment, s: &SecretKey, t: &SecretKey) -> ComSignature { + ComSignature { + pub_nonce: pub_nonce.to_owned(), + s: s.to_owned(), + t: t.to_owned(), + } + } + + #[allow(dead_code)] + pub fn sign(amount: u64, blind: &SecretKey, msg: &Vec) -> Result { + let secp = Secp256k1::with_caps(ContextFlag::Commit); + + let mut amt_bytes = [0; 32]; + BigEndian::write_u64(&mut amt_bytes[24..32], amount); + let k_amt = SecretKey::from_slice(&secp, &amt_bytes)?; + + let k_1 = SecretKey::new(&secp, &mut thread_rng()); + let k_2 = SecretKey::new(&secp, &mut thread_rng()); + + let commitment = secp.commit(amount, blind.clone())?; + let nonce_commitment = secp.commit_blind(k_1.clone(), k_2.clone())?; + + let e = ComSignature::calc_challenge(&secp, &commitment, &nonce_commitment, &msg)?; + + // s = k_1 + (e * amount) + let mut s = k_amt.clone(); + s.mul_assign(&secp, &e)?; + s.add_assign(&secp, &k_1)?; + + // t = k_2 + (e * blind) + let mut t = blind.clone(); + t.mul_assign(&secp, &e)?; + t.add_assign(&secp, &k_2)?; + + Ok(ComSignature::new(&nonce_commitment, &s, &t)) + } + + #[allow(non_snake_case)] + pub fn verify(&self, commit: &Commitment, msg: &Vec) -> Result<()> { + let secp = Secp256k1::with_caps(ContextFlag::Commit); + + let S1 = secp.commit_blind(self.s.clone(), self.t.clone())?; + + let mut Ce = commit.to_pubkey(&secp)?; + let e = ComSignature::calc_challenge(&secp, &commit, &self.pub_nonce, &msg)?; + Ce.mul_assign(&secp, &e)?; + + let commits = vec![Commitment::from_pubkey(&secp, &Ce)?, self.pub_nonce.clone()]; + let S2 = secp.commit_sum(commits, Vec::new())?; + + if S1 != S2 { + return Err(Error::new(ErrorKind::InvalidSigError)); + } + + Ok(()) + } + + fn calc_challenge( + secp: &Secp256k1, + commit: &Commitment, + nonce_commit: &Commitment, + msg: &Vec, + ) -> Result { + let mut challenge_hasher = Blake2b::new(32); + challenge_hasher.update(&commit.0); + challenge_hasher.update(&nonce_commit.0); + challenge_hasher.update(msg); + + let mut challenge = [0; 32]; + challenge.copy_from_slice(challenge_hasher.finalize().as_bytes()); + + Ok(SecretKey::from_slice(&secp, &challenge)?) + } } /// Serializes a ComSignature to and from hex pub mod comsig_serde { - use super::ComSignature; - use serde::{Deserialize, Serializer}; - use grin_util::ToHex; + use super::ComSignature; + use grin_core::ser::{self, ProtocolVersion}; + use grin_util::ToHex; + use serde::{Deserialize, Serializer}; - /// Serializes a ComSignature as a hex string - pub fn serialize(comsig: &ComSignature, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&comsig.to_hex()) - } + /// Serializes a ComSignature as a hex string + pub fn serialize(comsig: &ComSignature, serializer: S) -> Result + where + S: Serializer, + { + use serde::ser::Error; + let bytes = ser::ser_vec(&comsig, ProtocolVersion::local()).map_err(Error::custom)?; + serializer.serialize_str(&bytes.to_hex()) + } - /// Creates a ComSignature from a hex string - pub fn deserialize<'de, D>(deserializer: D) -> std::result::Result - where - D: serde::Deserializer<'de>, - { - use serde::de::Error; - String::deserialize(deserializer) - .and_then(|string| grin_util::from_hex(&string).map_err(Error::custom)) - .and_then(|bytes: Vec| Ok(ComSignature::from_vec(bytes.to_vec()))) - } + /// Creates a ComSignature from a hex string + pub fn deserialize<'de, D>(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + use serde::de::Error; + let bytes = String::deserialize(deserializer) + .and_then(|string| grin_util::from_hex(&string).map_err(Error::custom))?; + let sig: ComSignature = ser::deserialize_default(&mut &bytes[..]).map_err(Error::custom)?; + Ok(sig) + } } -/// Compute a PublicKey from a SecretKey -pub fn to_public_key(secret_key: &SecretKey) -> Result { - let secp = Secp256k1::new(); - let pubkey = PublicKey::from_secret_key(&secp, secret_key)?; - Ok(pubkey) +#[allow(non_snake_case)] +impl Readable for ComSignature { + fn read(reader: &mut R) -> std::result::Result { + let R = Commitment::read(reader)?; + let s = read_secret_key(reader)?; + let t = read_secret_key(reader)?; + Ok(ComSignature::new(&R, &s, &t)) + } } -/// Generate a random SecretKey. Not for production use -pub fn insecure_rand_secret() -> Result { - let secp = Secp256k1::new(); - let mut seed = [0u8; 32]; - rand::thread_rng().fill_bytes(&mut seed); - let secret = SecretKey::from_slice(&secp, &seed)?; - Ok(secret) +impl Writeable for ComSignature { + fn write(&self, writer: &mut W) -> std::result::Result<(), ser::Error> { + writer.write_fixed_bytes(self.pub_nonce.0)?; + writer.write_fixed_bytes(self.s.0)?; + writer.write_fixed_bytes(self.t.0)?; + Ok(()) + } +} + +/// Generate a random SecretKey. +pub fn random_secret() -> SecretKey { + let secp = Secp256k1::new(); + SecretKey::new(&secp, &mut thread_rng()) +} + +/// Deserialize a SecretKey from a Reader +pub fn read_secret_key(reader: &mut R) -> std::result::Result { + let buf = reader.read_fixed_bytes(SECRET_KEY_SIZE)?; + let secp = Secp256k1::with_caps(ContextFlag::None); + let pk = SecretKey::from_slice(&secp, &buf).map_err(|_| ser::Error::CorruptedData)?; + Ok(pk) } /// Build a Pedersen Commitment using the provided value and blinding factor pub fn commit(value: u64, blind: &SecretKey) -> Result { - let secp = Secp256k1::with_caps(ContextFlag::Commit); - let commit = secp.commit(value, blind.clone())?; - Ok(commit) + let secp = Secp256k1::with_caps(ContextFlag::Commit); + let commit = secp.commit(value, blind.clone())?; + Ok(commit) } /// Add a blinding factor to an existing Commitment pub fn add_excess(commitment: &Commitment, excess: &SecretKey) -> Result { - let secp = Secp256k1::with_caps(ContextFlag::Commit); - let excess_commit : Commitment = secp.commit(0, excess.clone())?; + let secp = Secp256k1::with_caps(ContextFlag::Commit); + let excess_commit: Commitment = secp.commit(0, excess.clone())?; - let commits = vec![commitment.clone(), excess_commit.clone()]; - let sum = secp.commit_sum(commits, Vec::new())?; - Ok(sum) + let commits = vec![commitment.clone(), excess_commit.clone()]; + let sum = secp.commit_sum(commits, Vec::new())?; + Ok(sum) } -/// secp256k1-zkp object serialization - -impl Readable for Commitment { - fn read(reader: &mut R) -> Result { - let a = reader.read_fixed_bytes(PEDERSEN_COMMITMENT_SIZE)?; - let mut c = [0; PEDERSEN_COMMITMENT_SIZE]; - c[..PEDERSEN_COMMITMENT_SIZE].clone_from_slice(&a[..PEDERSEN_COMMITMENT_SIZE]); - Ok(Commitment(c)) - } +/// Subtracts a value (v*H) from an existing commitment +pub fn sub_value(commitment: &Commitment, value: u64) -> Result { + let secp = Secp256k1::with_caps(ContextFlag::Commit); + let neg_commit: Commitment = secp.commit(value, ZERO_KEY)?; + let sum = secp.commit_sum(vec![commitment.clone()], vec![neg_commit.clone()])?; + Ok(sum) } -impl Writeable for Commitment { - fn write(&self, writer: &mut W) -> Result<()> { - writer.write_fixed_bytes(self) - } +/// Signs the message with the provided SecretKey +pub fn sign(sk: &SecretKey, msg: &Message) -> Result { + let secp = Secp256k1::with_caps(ContextFlag::Full); + let pubkey = PublicKey::from_secret_key(&secp, &sk)?; + let sig = aggsig::sign_single(&secp, &msg, &sk, None, None, None, Some(&pubkey), None)?; + Ok(sig) } -impl Readable for RangeProof { - fn read(reader: &mut R) -> Result { - let len = reader.read_u64()?; - let max_len = cmp::min(len as usize, MAX_PROOF_SIZE); - let p = reader.read_fixed_bytes(max_len)?; - let mut proof = [0; MAX_PROOF_SIZE]; - proof[..p.len()].clone_from_slice(&p[..]); - Ok(RangeProof { - plen: proof.len(), - proof, - }) - } -} +#[cfg(test)] +mod tests { + use super::{ComSignature, ContextFlag, Secp256k1, SecretKey}; + use crate::error::Result; -impl Writeable for RangeProof { - fn write(&self, writer: &mut W) -> Result<()> { - writer.write_bytes(self) - } -} + use rand::Rng; + use secp256k1zkp::rand::{thread_rng, RngCore}; -impl Readable for Signature { - fn read(reader: &mut R) -> Result { - let a = reader.read_fixed_bytes(AGG_SIGNATURE_SIZE)?; - let mut c = [0; AGG_SIGNATURE_SIZE]; - c[..AGG_SIGNATURE_SIZE].clone_from_slice(&a[..AGG_SIGNATURE_SIZE]); - Ok(Signature::from_raw_data(&c).unwrap()) - } -} + /// Test signing and verification of ComSignatures + #[test] + fn verify_comsig() -> Result<()> { + let secp = Secp256k1::with_caps(ContextFlag::Commit); -impl Writeable for Signature { - fn write(&self, writer: &mut W) -> Result<()> { - writer.write_fixed_bytes(self) - } -} + let amount = thread_rng().next_u64(); + let blind = SecretKey::new(&secp, &mut thread_rng()); + let msg: [u8; 16] = rand::thread_rng().gen(); + let comsig = ComSignature::sign(amount, &blind, &msg.to_vec())?; -impl Readable for PublicKey { - // Read the public key in compressed form - fn read(reader: &mut R) -> Result { - let buf = reader.read_fixed_bytes(COMPRESSED_PUBLIC_KEY_SIZE)?; - let secp = Secp256k1::with_caps(ContextFlag::None); - let pk = PublicKey::from_slice(&secp, &buf).map_err(|_| ErrorKind::CorruptedData)?; - Ok(pk) - } -} + let commit = secp.commit(amount, blind.clone())?; + assert!(comsig.verify(&commit, &msg.to_vec()).is_ok()); -impl Writeable for PublicKey { - // Write the public key in compressed form - fn write(&self, writer: &mut W) -> Result<()> { - let secp = Secp256k1::with_caps(ContextFlag::None); - writer.write_fixed_bytes(self.serialize_vec(&secp, true))?; - Ok(()) - } -} + let wrong_msg: [u8; 16] = rand::thread_rng().gen(); + assert!(comsig.verify(&commit, &wrong_msg.to_vec()).is_err()); -impl Readable for SecretKey { - fn read(reader: &mut R) -> Result { - let buf = reader.read_fixed_bytes(SECRET_KEY_SIZE)?; - let secp = Secp256k1::with_caps(ContextFlag::None); - let pk = SecretKey::from_slice(&secp, &buf).map_err(|_| ErrorKind::CorruptedData)?; - Ok(pk) - } -} + let wrong_commit = secp.commit(amount, SecretKey::new(&secp, &mut thread_rng()))?; + assert!(comsig.verify(&wrong_commit, &msg.to_vec()).is_err()); -impl Writeable for SecretKey { - fn write(&self, writer: &mut W) -> Result<()> { - writer.write_fixed_bytes(self.0)?; - Ok(()) - } -} \ No newline at end of file + Ok(()) + } +} diff --git a/src/ser.rs b/src/ser.rs deleted file mode 100644 index a86ef2e..0000000 --- a/src/ser.rs +++ /dev/null @@ -1,624 +0,0 @@ -// Copyright 2021 The Grin Developers -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Serialization and deserialization layer specialized for binary encoding. -//! Ensures consistency and safety. Basically a minimal subset or -//! rustc_serialize customized for our need. -//! -//! To use it simply implement `Writeable` or `Readable` and then use the -//! `serialize` or `deserialize` functions on them as appropriate. - -use crate::error::{Error, ErrorKind, Result}; -use byteorder::{BigEndian, ByteOrder, ReadBytesExt}; -use bytes::Buf; -use std::io::{self, Read, Write}; -use std::marker; - -/// Implementations defined how different numbers and binary structures are -/// written to an underlying stream or container (depending on implementation). -pub trait Writer { - /// Writes a u8 as bytes - fn write_u8(&mut self, n: u8) -> Result<()> { - self.write_fixed_bytes(&[n]) - } - - /// Writes a u16 as bytes - fn write_u16(&mut self, n: u16) -> Result<()> { - let mut bytes = [0; 2]; - BigEndian::write_u16(&mut bytes, n); - self.write_fixed_bytes(&bytes) - } - - /// Writes a u32 as bytes - fn write_u32(&mut self, n: u32) -> Result<()> { - let mut bytes = [0; 4]; - BigEndian::write_u32(&mut bytes, n); - self.write_fixed_bytes(&bytes) - } - - /// Writes a u32 as bytes - fn write_i32(&mut self, n: i32) -> Result<()> { - let mut bytes = [0; 4]; - BigEndian::write_i32(&mut bytes, n); - self.write_fixed_bytes(&bytes) - } - - /// Writes a u64 as bytes - fn write_u64(&mut self, n: u64) -> Result<()> { - let mut bytes = [0; 8]; - BigEndian::write_u64(&mut bytes, n); - self.write_fixed_bytes(&bytes) - } - - /// Writes a i64 as bytes - fn write_i64(&mut self, n: i64) -> Result<()> { - let mut bytes = [0; 8]; - BigEndian::write_i64(&mut bytes, n); - self.write_fixed_bytes(&bytes) - } - - /// Writes a variable number of bytes. The length is encoded as a 64-bit - /// prefix. - fn write_bytes>(&mut self, bytes: T) -> Result<()> { - self.write_u64(bytes.as_ref().len() as u64)?; - self.write_fixed_bytes(bytes) - } - - /// Writes a fixed number of bytes. The reader is expected to know the actual length on read. - fn write_fixed_bytes>(&mut self, bytes: T) -> Result<()>; - - /// Writes a fixed length of "empty" bytes. - fn write_empty_bytes(&mut self, length: usize) -> Result<()> { - self.write_fixed_bytes(vec![0u8; length]) - } -} - -/// Implementations defined how different numbers and binary structures are -/// read from an underlying stream or container (depending on implementation). -pub trait Reader { - /// Read a u8 from the underlying Read - fn read_u8(&mut self) -> Result; - /// Read a u16 from the underlying Read - fn read_u16(&mut self) -> Result; - /// Read a u32 from the underlying Read - fn read_u32(&mut self) -> Result; - /// Read a u64 from the underlying Read - fn read_u64(&mut self) -> Result; - /// Read a i32 from the underlying Read - fn read_i32(&mut self) -> Result; - /// Read a i64 from the underlying Read - fn read_i64(&mut self) -> Result; - /// Read a u64 len prefix followed by that number of exact bytes. - fn read_bytes_len_prefix(&mut self) -> Result>; - /// Read a fixed number of bytes from the underlying reader. - fn read_fixed_bytes(&mut self, length: usize) -> Result>; - /// Consumes a byte from the reader, producing an error if it doesn't have - /// the expected value - fn expect_u8(&mut self, val: u8) -> Result; - - /// Read a fixed number of "empty" bytes from the underlying reader. - /// It is an error if any non-empty bytes encountered. - fn read_empty_bytes(&mut self, length: usize) -> Result<()> { - for _ in 0..length { - if self.read_u8()? != 0u8 { - return Err(ErrorKind::CorruptedData.into()); - } - } - Ok(()) - } -} - -/// Trait that every type that can be serialized as binary must implement. -/// Writes directly to a Writer, a utility type thinly wrapping an -/// underlying Write implementation. -pub trait Writeable { - /// Write the data held by this Writeable to the provided writer - fn write(&self, writer: &mut W) -> Result<()>; -} - -/// Reader that exposes an Iterator interface. -pub struct IteratingReader<'a, T, R: Reader> { - count: u64, - curr: u64, - reader: &'a mut R, - _marker: marker::PhantomData, -} - -impl<'a, T, R: Reader> IteratingReader<'a, T, R> { - /// Constructor to create a new iterating reader for the provided underlying reader. - /// Takes a count so we know how many to iterate over. - pub fn new(reader: &'a mut R, count: u64) -> Self { - let curr = 0; - IteratingReader { - count, - curr, - reader, - _marker: marker::PhantomData, - } - } -} - -impl<'a, T, R> Iterator for IteratingReader<'a, T, R> -where - T: Readable, - R: Reader, -{ - type Item = T; - - fn next(&mut self) -> Option { - if self.curr >= self.count { - return None; - } - self.curr += 1; - T::read(self.reader).ok() - } -} - -/// Reads multiple serialized items into a Vec. -pub fn read_multi(reader: &mut R, count: u64) -> Result> -where - T: Readable, - R: Reader, -{ - // Very rudimentary check to ensure we do not overflow anything - // attempting to read huge amounts of data. - // Probably better than checking if count * size overflows a u64 though. - if count > 1_000_000 { - return Err(ErrorKind::TooLargeReadErr.into()); - } - - let res: Vec = IteratingReader::new(reader, count).collect(); - if res.len() as u64 != count { - return Err(ErrorKind::CountError.into()); - } - Ok(res) -} - -/// Trait that every type that can be deserialized from binary must implement. -/// Reads directly to a Reader, a utility type thinly wrapping an -/// underlying Read implementation. -pub trait Readable -where - Self: Sized, -{ - /// Reads the data necessary to this Readable from the provided reader - fn read(reader: &mut R) -> Result; -} - -/// Deserializes a Readable from any std::io::Read implementation. -pub fn deserialize(source: &mut R) -> Result { - let mut reader = BinReader::new(source); - T::read(&mut reader) -} - -/// Serializes a Writeable into any std::io::Write implementation. -pub fn serialize(sink: &mut dyn Write, thing: &W) -> Result<()> { - let mut writer = BinWriter::new(sink); - thing.write(&mut writer) -} - -/// Utility function to serialize a writeable directly in memory using a -/// Vec. -pub fn ser_vec(thing: &W) -> Result> { - let mut vec = vec![]; - serialize(&mut vec, thing)?; - Ok(vec) -} - -/// Utility to read from a binary source -pub struct BinReader<'a, R: Read> { - source: &'a mut R, -} - -impl<'a, R: Read> BinReader<'a, R> { - /// Constructor for a new BinReader for the provided source. - pub fn new(source: &'a mut R) -> Self { - BinReader { source } - } -} - -fn map_io_err(err: io::Error) -> Error { - ErrorKind::IOErr(format!("{}", err), err.kind()).into() -} - -/// Utility wrapper for an underlying byte Reader. Defines higher level methods -/// to read numbers, byte vectors, hashes, etc. -impl<'a, R: Read> Reader for BinReader<'a, R> { - fn read_u8(&mut self) -> Result { - self.source.read_u8().map_err(map_io_err) - } - fn read_u16(&mut self) -> Result { - self.source.read_u16::().map_err(map_io_err) - } - fn read_u32(&mut self) -> Result { - self.source.read_u32::().map_err(map_io_err) - } - fn read_i32(&mut self) -> Result { - self.source.read_i32::().map_err(map_io_err) - } - fn read_u64(&mut self) -> Result { - self.source.read_u64::().map_err(map_io_err) - } - fn read_i64(&mut self) -> Result { - self.source.read_i64::().map_err(map_io_err) - } - /// Read a variable size vector from the underlying Read. Expects a usize - fn read_bytes_len_prefix(&mut self) -> Result> { - let len = self.read_u64()?; - self.read_fixed_bytes(len as usize) - } - - /// Read a fixed number of bytes. - fn read_fixed_bytes(&mut self, len: usize) -> Result> { - // not reading more than 100k bytes in a single read - if len > 100_000 { - return Err(ErrorKind::TooLargeReadErr.into()); - } - let mut buf = vec![0; len]; - self.source - .read_exact(&mut buf) - .map(move |_| buf) - .map_err(map_io_err) - } - - fn expect_u8(&mut self, val: u8) -> Result { - let b = self.read_u8()?; - if b == val { - Ok(b) - } else { - Err(ErrorKind::UnexpectedData { - expected: vec![val], - received: vec![b], - }.into()) - } - } -} - -/// A reader that reads straight off a stream. -/// Tracks total bytes read so we can verify we read the right number afterwards. -pub struct StreamingReader<'a> { - total_bytes_read: u64, - stream: &'a mut dyn Read, -} - -impl<'a> StreamingReader<'a> { - /// Create a new streaming reader with the provided underlying stream. - /// Also takes a duration to be used for each individual read_exact call. - pub fn new(stream: &'a mut dyn Read) -> StreamingReader<'a> { - StreamingReader { - total_bytes_read: 0, - stream, - } - } - - /// Returns the total bytes read via this streaming reader. - pub fn total_bytes_read(&self) -> u64 { - self.total_bytes_read - } -} - -/// Note: We use read_fixed_bytes() here to ensure our "async" I/O behaves as expected. -impl<'a> Reader for StreamingReader<'a> { - fn read_u8(&mut self) -> Result { - let buf = self.read_fixed_bytes(1)?; - Ok(buf[0]) - } - fn read_u16(&mut self) -> Result { - let buf = self.read_fixed_bytes(2)?; - Ok(BigEndian::read_u16(&buf[..])) - } - fn read_u32(&mut self) -> Result { - let buf = self.read_fixed_bytes(4)?; - Ok(BigEndian::read_u32(&buf[..])) - } - fn read_i32(&mut self) -> Result { - let buf = self.read_fixed_bytes(4)?; - Ok(BigEndian::read_i32(&buf[..])) - } - fn read_u64(&mut self) -> Result { - let buf = self.read_fixed_bytes(8)?; - Ok(BigEndian::read_u64(&buf[..])) - } - fn read_i64(&mut self) -> Result { - let buf = self.read_fixed_bytes(8)?; - Ok(BigEndian::read_i64(&buf[..])) - } - - /// Read a variable size vector from the underlying stream. Expects a usize - fn read_bytes_len_prefix(&mut self) -> Result> { - let len = self.read_u64()?; - self.total_bytes_read += 8; - self.read_fixed_bytes(len as usize) - } - - /// Read a fixed number of bytes. - fn read_fixed_bytes(&mut self, len: usize) -> Result> { - let mut buf = vec![0u8; len]; - self.stream.read_exact(&mut buf)?; - self.total_bytes_read += len as u64; - Ok(buf) - } - - fn expect_u8(&mut self, val: u8) -> Result { - let b = self.read_u8()?; - if b == val { - Ok(b) - } else { - Err(ErrorKind::UnexpectedData { - expected: vec![val], - received: vec![b], - }.into()) - } - } -} - -/// Protocol version-aware wrapper around a `Buf` impl -pub struct BufReader<'a, B: Buf> { - inner: &'a mut B, - bytes_read: usize, -} - -impl<'a, B: Buf> BufReader<'a, B> { - /// Construct a new BufReader - pub fn new(buf: &'a mut B) -> Self { - Self { - inner: buf, - bytes_read: 0, - } - } - - /// Check whether the buffer has enough bytes remaining to perform a read - fn has_remaining(&mut self, len: usize) -> Result<()> { - if self.inner.remaining() >= len { - self.bytes_read += len; - Ok(()) - } else { - Err(io::ErrorKind::UnexpectedEof.into()) - } - } - - /// The total bytes read - pub fn bytes_read(&self) -> u64 { - self.bytes_read as u64 - } - - /// Convenience function to read from the buffer and deserialize - pub fn body(&mut self) -> Result { - T::read(self) - } -} - -impl<'a, B: Buf> Reader for BufReader<'a, B> { - fn read_u8(&mut self) -> Result { - self.has_remaining(1)?; - Ok(self.inner.get_u8()) - } - - fn read_u16(&mut self) -> Result { - self.has_remaining(2)?; - Ok(self.inner.get_u16()) - } - - fn read_u32(&mut self) -> Result { - self.has_remaining(4)?; - Ok(self.inner.get_u32()) - } - - fn read_u64(&mut self) -> Result { - self.has_remaining(8)?; - Ok(self.inner.get_u64()) - } - - fn read_i32(&mut self) -> Result { - self.has_remaining(4)?; - Ok(self.inner.get_i32()) - } - - fn read_i64(&mut self) -> Result { - self.has_remaining(8)?; - Ok(self.inner.get_i64()) - } - - fn read_bytes_len_prefix(&mut self) -> Result> { - let len = self.read_u64()?; - self.read_fixed_bytes(len as usize) - } - - fn read_fixed_bytes(&mut self, len: usize) -> Result> { - // not reading more than 100k bytes in a single read - if len > 100_000 { - return Err(ErrorKind::TooLargeReadErr.into()); - } - self.has_remaining(len)?; - - let mut buf = vec![0; len]; - self.inner.copy_to_slice(&mut buf[..]); - Ok(buf) - } - - fn expect_u8(&mut self, val: u8) -> Result { - let b = self.read_u8()?; - if b == val { - Ok(b) - } else { - Err(ErrorKind::UnexpectedData { - expected: vec![val], - received: vec![b], - }.into()) - } - } -} - -/// Utility wrapper for an underlying byte Writer. Defines higher level methods -/// to write numbers, byte vectors, hashes, etc. -pub struct BinWriter<'a> { - sink: &'a mut dyn Write, -} - -impl<'a> BinWriter<'a> { - /// Wraps a standard Write in a new BinWriter - pub fn new(sink: &'a mut dyn Write) -> BinWriter<'a> { - BinWriter { sink } - } -} - -impl<'a> Writer for BinWriter<'a> { - fn write_fixed_bytes>(&mut self, bytes: T) -> Result<()> { - self.sink.write_all(bytes.as_ref())?; - Ok(()) - } -} - -macro_rules! impl_int { - ($int:ty, $w_fn:ident, $r_fn:ident) => { - impl Writeable for $int { - fn write(&self, writer: &mut W) -> Result<()> { - writer.$w_fn(*self) - } - } - - impl Readable for $int { - fn read(reader: &mut R) -> Result<$int> { - reader.$r_fn() - } - } - }; -} - -impl_int!(u8, write_u8, read_u8); -impl_int!(u16, write_u16, read_u16); -impl_int!(u32, write_u32, read_u32); -impl_int!(i32, write_i32, read_i32); -impl_int!(u64, write_u64, read_u64); -impl_int!(i64, write_i64, read_i64); - -impl Readable for Vec -where - T: Readable, -{ - fn read(reader: &mut R) -> Result> { - let mut buf = Vec::new(); - loop { - let elem = T::read(reader); - match elem { - Ok(e) => buf.push(e), - // Err(ErrorKind::IOErr(ref _d, ref kind)) if *kind == io::ErrorKind::UnexpectedEof => { - // break; - // } - Err(e) => { - match e.kind() { - ErrorKind::IOErr(ref _d, ref kind) if *kind == io::ErrorKind::UnexpectedEof => { - break; - }, - _ => return Err(e), - } - }, - } - } - Ok(buf) - } -} - -impl Writeable for Vec -where - T: Writeable, -{ - fn write(&self, writer: &mut W) -> Result<()> { - for elmt in self { - elmt.write(writer)?; - } - Ok(()) - } -} - -impl<'a, A: Writeable> Writeable for &'a A { - fn write(&self, writer: &mut W) -> Result<()> { - Writeable::write(*self, writer) - } -} - -impl Writeable for (A, B) { - fn write(&self, writer: &mut W) -> Result<()> { - Writeable::write(&self.0, writer)?; - Writeable::write(&self.1, writer) - } -} - -impl Readable for (A, B) { - fn read(reader: &mut R) -> Result<(A, B)> { - Ok((Readable::read(reader)?, Readable::read(reader)?)) - } -} - -impl Writeable for (A, B, C) { - fn write(&self, writer: &mut W) -> Result<()> { - Writeable::write(&self.0, writer)?; - Writeable::write(&self.1, writer)?; - Writeable::write(&self.2, writer) - } -} - -impl Writeable for (A, B, C, D) { - fn write(&self, writer: &mut W) -> Result<()> { - Writeable::write(&self.0, writer)?; - Writeable::write(&self.1, writer)?; - Writeable::write(&self.2, writer)?; - Writeable::write(&self.3, writer) - } -} - -impl Readable for (A, B, C) { - fn read(reader: &mut R) -> Result<(A, B, C)> { - Ok(( - Readable::read(reader)?, - Readable::read(reader)?, - Readable::read(reader)?, - )) - } -} - -impl Readable for (A, B, C, D) { - fn read(reader: &mut R) -> Result<(A, B, C, D)> { - Ok(( - Readable::read(reader)?, - Readable::read(reader)?, - Readable::read(reader)?, - Readable::read(reader)?, - )) - } -} - -/// Serializes a Vec to and from hex -pub mod vec_serde { - use serde::{Deserialize, Serializer}; - use grin_util::ToHex; - - /// Serializes a Vec as a hex string - pub fn serialize(bytes: &Vec, serializer: S) -> Result - where - S: Serializer, - { - serializer.serialize_str(&bytes.to_hex()) - } - - /// Creates a Vec from a hex string - pub fn deserialize<'de, D>(deserializer: D) -> std::result::Result, D::Error> - where - D: serde::Deserializer<'de>, - { - use serde::de::Error; - String::deserialize(deserializer) - .and_then(|string| grin_util::from_hex(&string).map_err(Error::custom)) - } -} \ No newline at end of file diff --git a/src/server.rs b/src/server.rs index 8e402e6..ba20e9a 100644 --- a/src/server.rs +++ b/src/server.rs @@ -1,213 +1,649 @@ -use crate::onion; -use crate::secp::{self, Commitment, ComSignature, SecretKey}; -use crate::ser; -use crate::types::Onion; +use crate::config::ServerConfig; +use crate::node::{self, GrinNode}; +use crate::onion::Onion; +use crate::secp::{ComSignature, Commitment, RangeProof, Secp256k1, SecretKey}; +use crate::wallet::{self, Wallet}; -use jsonrpc_derive::rpc; -use jsonrpc_http_server::*; -use jsonrpc_http_server::jsonrpc_core::*; -use jsonrpc_core::{Result, Value}; -use serde::{Deserialize, Serialize}; -use std::net::SocketAddr; -use std::sync::Mutex; +use grin_core::core::{Input, Output, OutputFeatures, TransactionBody}; +use grin_core::global::DEFAULT_ACCEPT_FEE_BASE; +use itertools::Itertools; +use std::collections::HashMap; +use std::result::Result; +use std::sync::{Arc, Mutex}; +use thiserror::Error; #[derive(Clone, Debug, PartialEq)] -pub struct ServerConfig { - pub key: SecretKey, - pub addr: SocketAddr, - pub is_first: bool, +struct Submission { + /// The total excess for the output commitment + excess: SecretKey, + /// The derived output commitment after applying excess and fee + output_commit: Commitment, + /// The rangeproof, included only for the final hop (node N) + rangeproof: Option, + /// Transaction input being spent + input: Input, + /// Transaction fee + fee: u64, + /// The remaining onion after peeling off our layer + onion: Onion, } -pub struct Submission { - pub excess: SecretKey, - pub input_commit: Commitment, - pub onion: Onion, +/// Swap error types +#[derive(Clone, Error, Debug, PartialEq)] +pub enum SwapError { + #[error("Invalid number of payloads provided (expected {expected:?}, found {found:?})")] + InvalidPayloadLength { expected: usize, found: usize }, + #[error("Commitment Signature is invalid")] + InvalidComSignature, + #[error("Rangeproof is invalid")] + InvalidRangeproof, + #[error("Rangeproof is required but was not supplied")] + MissingRangeproof, + #[error("Output {commit:?} does not exist, or is already spent.")] + CoinNotFound { commit: Commitment }, + #[error("Output {commit:?} is already in the swap list.")] + AlreadySwapped { commit: Commitment }, + #[error("Failed to peel onion layer: {0}")] + PeelOnionFailure(String), + #[error("Fee too low (expected >= {minimum_fee:?}, actual {actual_fee:?})")] + FeeTooLow { minimum_fee: u64, actual_fee: u64 }, + #[error("{0}")] + UnknownError(String), } -#[derive(Serialize, Deserialize)] -pub struct SwapReq { - pub onion: Onion, - #[serde(with = "ser::vec_serde")] - pub msg: Vec, - #[serde(with = "secp::comsig_serde")] - pub comsig: ComSignature, -} - -lazy_static! { - static ref SERVER_STATE: Mutex> = Mutex::new(Vec::new()); -} - -#[rpc(server)] -pub trait Server { - #[rpc(name = "swap")] - fn swap(&self, swap: SwapReq) -> Result; - - // milestone 3: - // fn derive_outputs(&self, entries: Vec) -> Result; - // fn derive_kernel(&self, tx: Tx) -> Result; +/// A MWixnet server +pub trait Server: Send + Sync { + /// Submit a new output to be swapped. + fn swap(&self, onion: &Onion, comsig: &ComSignature) -> Result<(), SwapError>; + + /// Iterate through all saved submissions, filter out any inputs that are no longer spendable, + /// and assemble the coinswap transaction, posting the transaction to the configured node. + /// + /// Currently only a single mix node is used. Milestone 3 will include support for multiple mix nodes. + fn execute_round(&self) -> crate::error::Result<()>; } +/// The standard MWixnet server implementation +#[derive(Clone)] pub struct ServerImpl { - server_key: SecretKey, + server_config: ServerConfig, + wallet: Arc, + node: Arc, + submissions: Arc>>, } impl ServerImpl { - pub fn new(server_key: SecretKey) -> Self { - ServerImpl { server_key } - } + /// Create a new MWixnet server + pub fn new( + server_config: ServerConfig, + wallet: Arc, + node: Arc, + ) -> Self { + ServerImpl { + server_config, + wallet, + node, + submissions: Arc::new(Mutex::new(HashMap::new())), + } + } + + /// The fee base to use. For now, just using the default. + fn get_fee_base(&self) -> u64 { + DEFAULT_ACCEPT_FEE_BASE + } + + /// Minimum fee to perform a swap. + /// Requires enough fee for the mwixnet server's kernel, 1 input and its output to swap. + fn get_minimum_swap_fee(&self) -> u64 { + TransactionBody::weight_by_iok(1, 1, 1) * self.get_fee_base() + } } impl Server for ServerImpl { - fn swap(&self, swap: SwapReq) -> Result { - // milestone 2 - check that commitment is unspent - - // Verify commitment signature to ensure caller owns the output - let _ = swap.comsig.verify(&swap.onion.commit, &swap.msg) - .map_err(|_| jsonrpc_core::Error::invalid_params("ComSignature invalid"))?; - - let peeled = onion::peel_layer(&swap.onion, &self.server_key) - .map_err(|e| jsonrpc_core::Error::invalid_params(e.message()))?; - SERVER_STATE.lock().unwrap().push(Submission{ - excess: peeled.0.excess, - input_commit: swap.onion.commit, - onion: peeled.1 - }); - Ok(Value::String("success".into())) - } - + fn swap(&self, onion: &Onion, comsig: &ComSignature) -> Result<(), SwapError> { + // milestone 3: check that enc_payloads length matches number of configured servers + if onion.enc_payloads.len() != 1 { + return Err(SwapError::InvalidPayloadLength { + expected: 1, + found: onion.enc_payloads.len(), + }); + } + + // Verify commitment signature to ensure caller owns the output + let serialized_onion = onion + .serialize() + .map_err(|e| SwapError::UnknownError(e.to_string()))?; + let _ = comsig + .verify(&onion.commit, &serialized_onion) + .map_err(|_| SwapError::InvalidComSignature)?; + + // Verify that commitment is unspent + let input = node::build_input(&self.node, &onion.commit) + .map_err(|e| SwapError::UnknownError(e.to_string()))?; + let input = input.ok_or(SwapError::CoinNotFound { + commit: onion.commit.clone(), + })?; + + let peeled = onion + .peel_layer(&self.server_config.key) + .map_err(|e| SwapError::PeelOnionFailure(e.message()))?; + + // Verify the fee meets the minimum + let fee: u64 = peeled.0.fee.into(); + if fee < self.get_minimum_swap_fee() { + return Err(SwapError::FeeTooLow { + minimum_fee: self.get_minimum_swap_fee(), + actual_fee: fee, + }); + } + + // Verify the bullet proof and build the final output + if let Some(r) = peeled.0.rangeproof { + let secp = Secp256k1::with_caps(secp256k1zkp::ContextFlag::Commit); + secp.verify_bullet_proof(peeled.1.commit, r, None) + .map_err(|_| SwapError::InvalidRangeproof)?; + } else { + // milestone 3: only the last hop will have a rangeproof + return Err(SwapError::MissingRangeproof); + } + + let mut locked = self.submissions.lock().unwrap(); + if locked.contains_key(&onion.commit) { + return Err(SwapError::AlreadySwapped { + commit: onion.commit.clone(), + }); + } + + locked.insert( + onion.commit, + Submission { + excess: peeled.0.excess, + output_commit: peeled.1.commit, + rangeproof: peeled.0.rangeproof, + input, + fee, + onion: peeled.1, + }, + ); + Ok(()) + } + + fn execute_round(&self) -> crate::error::Result<()> { + let mut locked_state = self.submissions.lock().unwrap(); + let next_block_height = self.node.get_chain_height()? + 1; + + let spendable: Vec = locked_state + .values() + .into_iter() + .unique_by(|s| s.output_commit) + .filter(|s| { + node::is_spendable(&self.node, &s.input.commit, next_block_height).unwrap_or(false) + }) + .filter(|s| !node::is_unspent(&self.node, &s.output_commit).unwrap_or(true)) + .cloned() + .collect(); + + if spendable.len() == 0 { + return Ok(()); + } + + let total_fee: u64 = spendable.iter().enumerate().map(|(_, s)| s.fee).sum(); + + let inputs: Vec = spendable.iter().enumerate().map(|(_, s)| s.input).collect(); + + let outputs: Vec = spendable + .iter() + .enumerate() + .map(|(_, s)| { + Output::new( + OutputFeatures::Plain, + s.output_commit, + s.rangeproof.unwrap(), + ) + }) + .collect(); + + let excesses: Vec = spendable + .iter() + .enumerate() + .map(|(_, s)| s.excess.clone()) + .collect(); + + let tx = wallet::assemble_tx( + &self.wallet, + &inputs, + &outputs, + self.get_fee_base(), + total_fee, + &excesses, + )?; + + self.node.post_tx(&tx)?; + locked_state.clear(); + + Ok(()) + } } -/// Spin up the JSON-RPC web server -pub fn listen(server_config: &ServerConfig, shutdown_signal: F) -> std::result::Result<(), Box> -where - F: futures::future::Future + Send + 'static, -{ - let mut io = IoHandler::new(); - io.extend_with(ServerImpl::to_delegate(ServerImpl::new(server_config.key.clone()))); +#[cfg(test)] +pub mod mock { + use super::{Server, SwapError}; + use crate::onion::Onion; + use crate::secp::ComSignature; - let server = ServerBuilder::new(io) - .cors(DomainsValidation::Disabled) - .request_middleware(|request: hyper::Request| { - if request.uri() == "/v1" { - request.into() - } else { - jsonrpc_http_server::Response::bad_request("Only v1 supported").into() - } - }) - .start_http(&server_config.addr) - .expect("Unable to start RPC server"); + use std::collections::HashMap; - let close_handle = server.close_handle(); - std::thread::spawn(move || { - futures::executor::block_on(shutdown_signal); - close_handle.close(); - }); - server.wait(); + pub struct MockServer { + errors: HashMap, + } - Ok(()) + impl MockServer { + pub fn new() -> MockServer { + MockServer { + errors: HashMap::new(), + } + } + + pub fn set_response(&mut self, onion: &Onion, e: SwapError) { + self.errors.insert(onion.clone(), e); + } + } + + impl Server for MockServer { + fn swap(&self, onion: &Onion, _comsig: &ComSignature) -> Result<(), SwapError> { + if let Some(e) = self.errors.get(&onion) { + return Err(e.clone()); + } + + Ok(()) + } + + fn execute_round(&self) -> crate::error::Result<()> { + Ok(()) + } + } } #[cfg(test)] mod tests { - use crate::{onion, secp, server, types}; - use std::net::TcpListener; - use std::time::Duration; - use std::thread; + use crate::config::ServerConfig; + use crate::node::mock::MockGrinNode; + use crate::onion::test_util::{self, Hop}; + use crate::onion::Onion; + use crate::secp::{ + self, ComSignature, Commitment, PublicKey, RangeProof, Secp256k1, SecretKey, + }; + use crate::server::{Server, ServerImpl, Submission, SwapError}; + use crate::types::Payload; + use crate::wallet::mock::MockWallet; - use hyper::{Body, Client, Request, Response}; - use tokio::runtime; + use grin_core::core::{Committed, FeeFields, Input, OutputFeatures, Transaction, Weighting}; + use grin_core::global::{self, ChainTypes}; + use std::net::TcpListener; + use std::sync::Arc; - async fn body_to_string(req: Response) -> String { - let body_bytes = hyper::body::to_bytes(req.into_body()).await.unwrap(); - String::from_utf8(body_bytes.to_vec()).unwrap() - } + macro_rules! assert_error_type { + ($result:expr, $error_type:pat) => { + assert!($result.is_err()); + assert!(if let $error_type = $result.unwrap_err() { + true + } else { + false + }); + }; + } - /// Spin up a temporary web service, query the API, then cleanup and return response - fn make_request(server_key: secp::SecretKey, req: String) -> Result> { - let server_config = server::ServerConfig { - key: server_key, - addr: TcpListener::bind("127.0.0.1:0")?.local_addr()?, - is_first: true - }; + fn new_server( + server_key: &SecretKey, + utxos: &Vec<&Commitment>, + ) -> (ServerImpl, Arc) { + let config = ServerConfig { + key: server_key.clone(), + interval_s: 1, + addr: TcpListener::bind("127.0.0.1:0") + .unwrap() + .local_addr() + .unwrap(), + grin_node_url: "127.0.0.1:3413".parse().unwrap(), + grin_node_secret_path: None, + wallet_owner_url: "127.0.0.1:3420".parse().unwrap(), + wallet_owner_secret_path: None, + }; + let wallet = Arc::new(MockWallet {}); + let mut mut_node = MockGrinNode::new(); + for utxo in utxos { + mut_node.add_default_utxo(&utxo); + } + let node = Arc::new(mut_node); - let threaded_rt = runtime::Runtime::new()?; - let (shutdown_sender, shutdown_receiver) = futures::channel::oneshot::channel(); - let uri = format!("http://{}/v1", server_config.addr); + let server = ServerImpl::new(config, wallet.clone(), node.clone()); + (server, node) + } - // Spawn the server task - threaded_rt.spawn(async move { - server::listen(&server_config, async { shutdown_receiver.await.ok(); }).unwrap() - }); + fn proof(value: u64, fee: u64, input_blind: &SecretKey, hop_excess: &SecretKey) -> RangeProof { + let secp = Secp256k1::new(); + let nonce = secp::random_secret(); - // Wait for listener - thread::sleep(Duration::from_millis(500)); + let mut blind = input_blind.clone(); + blind.add_assign(&secp, &hop_excess).unwrap(); - let do_request = async move { - let request = Request::post(uri) - .header("Content-Type", "application/json") - .body(Body::from(req)) - .unwrap(); + secp.bullet_proof( + value - fee, + blind.clone(), + nonce.clone(), + nonce.clone(), + None, + None, + ) + } - Client::new().request(request).await - }; + fn new_hop( + server_key: &SecretKey, + hop_excess: &SecretKey, + fee: u64, + proof: Option, + ) -> Hop { + let secp = Secp256k1::new(); + Hop { + pubkey: PublicKey::from_secret_key(&secp, &server_key).unwrap(), + payload: Payload { + excess: hop_excess.clone(), + fee: FeeFields::from(fee as u32), + rangeproof: proof, + }, + } + } - let response = threaded_rt.block_on(do_request)?; - let response_str: String = threaded_rt.block_on(body_to_string(response)); + /// Single hop to demonstrate request validation and onion unwrapping. + #[test] + fn swap_lifecycle() -> Result<(), Box> { + let value: u64 = 200_000_000; + let fee: u64 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; - shutdown_sender.send(()).ok(); + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let proof = proof(value, fee, &blind, &hop_excess); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); - // Wait for shutdown - thread::sleep(Duration::from_millis(500)); - threaded_rt.shutdown_background(); + let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; - Ok(response_str) - } + let (server, node) = new_server(&server_key, &vec![&input_commit]); + server.swap(&onion, &comsig)?; - /// Single hop to demonstrate request validation and onion unwrapping. - /// UTXO creation and bulletproof generation reserved for milestones 2 & 3. - #[test] - fn swap_lifecycle() -> Result<(), Box> { - let server_key = secp::insecure_rand_secret()?; - - let secp = secp::Secp256k1::new(); - let value: u64 = 100; - let blind = secp::insecure_rand_secret()?; - let commitment = secp::commit(value, &blind)?; - let session_key = secp::insecure_rand_secret()?; + // Make sure entry is added to server. + let output_commit = secp::add_excess(&input_commit, &hop_excess)?; + let output_commit = secp::sub_value(&output_commit, fee)?; - let hop = types::Hop { - pubkey: secp::PublicKey::from_secret_key(&secp, &server_key)?, - payload: types::Payload{ - excess: secp::insecure_rand_secret()?, - rangeproof: None, - } - }; - let hops: Vec = vec![hop]; - let onion_packet = onion::create_onion(&commitment, &session_key, &hops)?; - let msg : Vec = vec![0u8, 1u8, 2u8, 3u8]; - let comsig = secp::ComSignature::sign(value, &blind, &msg)?; - let swap = server::SwapReq{ - onion: onion_packet, - msg: msg, - comsig: comsig, - }; + let expected = Submission { + excess: hop_excess.clone(), + output_commit: output_commit.clone(), + rangeproof: Some(proof), + input: Input::new(OutputFeatures::Plain, input_commit.clone()), + fee, + onion: Onion { + ephemeral_pubkey: test_util::next_ephemeral_pubkey(&onion, &server_key)?, + commit: output_commit.clone(), + enc_payloads: vec![], + }, + }; - let req = format!("{{\"jsonrpc\": \"2.0\", \"method\": \"swap\", \"params\": [{}], \"id\": \"1\"}}", serde_json::json!(swap)); - let response = make_request(server_key, req)?; - let expected = "{\"jsonrpc\":\"2.0\",\"result\":\"success\",\"id\":\"1\"}\n"; - assert_eq!(response, expected); - Ok(()) - } + { + let submissions = server.submissions.lock().unwrap(); + assert_eq!(1, submissions.len()); + assert!(submissions.contains_key(&input_commit)); + assert_eq!(&expected, submissions.get(&input_commit).unwrap()); + } - #[test] - fn swap_bad_request() -> Result<(), Box> { - let params = "{ \"param\": \"Not a valid Swap request\" }"; - let req = format!("{{\"jsonrpc\": \"2.0\", \"method\": \"swap\", \"params\": [{}], \"id\": \"1\"}}", params); - let response = make_request(secp::insecure_rand_secret()?, req)?; - let expected = "{\"jsonrpc\":\"2.0\",\"error\":{\"code\":-32602,\"message\":\"Invalid params: missing field `onion`.\"},\"id\":\"1\"}\n"; - assert_eq!(response, expected); - Ok(()) - } + server.execute_round()?; - // milestone 2 - add tests to cover invalid comsig's & inputs not in utxo set -} \ No newline at end of file + // Make sure entry is removed from server.submissions + assert!(server.submissions.lock().unwrap().is_empty()); + + // check that the transaction was posted + let posted_txns = node.get_posted_txns(); + assert_eq!(posted_txns.len(), 1); + let posted_txn: Transaction = posted_txns.into_iter().next().unwrap(); + assert!(posted_txn.inputs_committed().contains(&input_commit)); + assert!(posted_txn.outputs_committed().contains(&output_commit)); + // todo: check that outputs also contain the commitment generated by our wallet + + global::set_local_chain_type(ChainTypes::AutomatedTesting); + posted_txn.validate(Weighting::AsTransaction)?; + + Ok(()) + } + + /// Returns InvalidPayloadLength when too many payloads are provided. + #[test] + fn swap_too_many_payloads() -> Result<(), Box> { + let value: u64 = 200_000_000; + let fee: u64 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let proof = proof(value, fee, &blind, &hop_excess); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); + + let hops: Vec = vec![hop.clone(), hop.clone()]; // Multiple payloads + let onion = test_util::create_onion(&input_commit, &hops)?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let (server, _node) = new_server(&server_key, &vec![&input_commit]); + let result = server.swap(&onion, &comsig); + assert_eq!( + Err(SwapError::InvalidPayloadLength { + expected: 1, + found: 2 + }), + result + ); + + // Make sure no entry is added to server.submissions + assert!(server.submissions.lock().unwrap().is_empty()); + + Ok(()) + } + + /// Returns InvalidComSignature when ComSignature fails to verify. + #[test] + fn swap_invalid_com_signature() -> Result<(), Box> { + let value: u64 = 200_000_000; + let fee: u64 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let proof = proof(value, fee, &blind, &hop_excess); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); + + let onion = test_util::create_onion(&input_commit, &vec![hop])?; + + let wrong_blind = secp::random_secret(); + let comsig = ComSignature::sign(value, &wrong_blind, &onion.serialize()?)?; + + let (server, _node) = new_server(&server_key, &vec![&input_commit]); + let result = server.swap(&onion, &comsig); + assert_eq!(Err(SwapError::InvalidComSignature), result); + + // Make sure no entry is added to server.submissions + assert!(server.submissions.lock().unwrap().is_empty()); + + Ok(()) + } + + /// Returns InvalidRangeProof when the rangeproof fails to verify for the commitment. + #[test] + fn swap_invalid_rangeproof() -> Result<(), Box> { + let value: u64 = 200_000_000; + let fee: u64 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let wrong_value = value + 10_000_000; + let proof = proof(wrong_value, fee, &blind, &hop_excess); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); + + let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let (server, _node) = new_server(&server_key, &vec![&input_commit]); + let result = server.swap(&onion, &comsig); + assert_eq!(Err(SwapError::InvalidRangeproof), result); + + // Make sure no entry is added to server.submissions + assert!(server.submissions.lock().unwrap().is_empty()); + + Ok(()) + } + + /// Returns MissingRangeproof when no rangeproof is provided. + #[test] + fn swap_missing_rangeproof() -> Result<(), Box> { + let value: u64 = 200_000_000; + let fee: u64 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let hop = new_hop(&server_key, &hop_excess, fee, None); + + let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let (server, _node) = new_server(&server_key, &vec![&input_commit]); + let result = server.swap(&onion, &comsig); + assert_eq!(Err(SwapError::MissingRangeproof), result); + + // Make sure no entry is added to server.submissions + assert!(server.submissions.lock().unwrap().is_empty()); + + Ok(()) + } + + /// Returns CoinNotFound when there's no matching output in the UTXO set. + #[test] + fn swap_utxo_missing() -> Result<(), Box> { + let value: u64 = 200_000_000; + let fee: u64 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let proof = proof(value, fee, &blind, &hop_excess); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); + + let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let (server, _node) = new_server(&server_key, &vec![]); + let result = server.swap(&onion, &comsig); + assert_eq!( + Err(SwapError::CoinNotFound { + commit: input_commit.clone() + }), + result + ); + + // Make sure no entry is added to server.submissions + assert!(server.submissions.lock().unwrap().is_empty()); + + Ok(()) + } + + /// Returns AlreadySwapped when trying to swap the same commitment multiple times. + #[test] + fn swap_already_swapped() -> Result<(), Box> { + let value: u64 = 200_000_000; + let fee: u64 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let proof = proof(value, fee, &blind, &hop_excess); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); + + let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let (server, _node) = new_server(&server_key, &vec![&input_commit]); + server.swap(&onion, &comsig)?; + + // Call swap a second time + let result = server.swap(&onion, &comsig); + assert_eq!( + Err(SwapError::AlreadySwapped { + commit: input_commit.clone() + }), + result + ); + + Ok(()) + } + + /// Returns PeelOnionFailure when a failure occurs trying to decrypt the onion payload. + #[test] + fn swap_peel_onion_failure() -> Result<(), Box> { + let value: u64 = 200_000_000; + let fee: u64 = 50_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let proof = proof(value, fee, &blind, &hop_excess); + + let wrong_server_key = secp::random_secret(); + let hop = new_hop(&wrong_server_key, &hop_excess, fee, Some(proof)); + + let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let (server, _node) = new_server(&server_key, &vec![&input_commit]); + let result = server.swap(&onion, &comsig); + + assert!(result.is_err()); + assert_error_type!(result, SwapError::PeelOnionFailure(_)); + + Ok(()) + } + + /// Returns FeeTooLow when the minimum fee is not met. + #[test] + fn swap_fee_too_low() -> Result<(), Box> { + let value: u64 = 200_000_000; + let fee: u64 = 1_000_000; + let blind = secp::random_secret(); + let input_commit = secp::commit(value, &blind)?; + + let server_key = secp::random_secret(); + let hop_excess = secp::random_secret(); + let proof = proof(value, fee, &blind, &hop_excess); + let hop = new_hop(&server_key, &hop_excess, fee, Some(proof)); + + let onion = test_util::create_onion(&input_commit, &vec![hop])?; + let comsig = ComSignature::sign(value, &blind, &onion.serialize()?)?; + + let (server, _node) = new_server(&server_key, &vec![&input_commit]); + let result = server.swap(&onion, &comsig); + assert_eq!( + Err(SwapError::FeeTooLow { + minimum_fee: 12_500_000, + actual_fee: fee + }), + result + ); + + Ok(()) + } +} diff --git a/src/types.rs b/src/types.rs index 0f51aae..9a586f1 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,166 +1,71 @@ -use crate::error::{ErrorKind, Result}; -use crate::secp::{Commitment, PublicKey, RangeProof, SecretKey, Secp256k1}; -use crate::ser::{self, BinReader, Readable, Reader, Writeable, Writer}; -use grin_util::{self, ToHex}; +use crate::error::{Result, StdResult}; +use crate::secp::{self, RangeProof, SecretKey}; + +use grin_core::core::FeeFields; +use grin_core::ser::{self, Readable, Reader, Writeable, Writer}; use serde::{Deserialize, Serialize}; -use serde::ser::SerializeStruct; -use std::fmt; -use std::io::Cursor; -pub type RawBytes = Vec; - -const CURRENT_VERSION : u8 = 0; +const CURRENT_VERSION: u8 = 0; #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Payload { - pub excess: SecretKey, - pub rangeproof: Option, + pub excess: SecretKey, + pub fee: FeeFields, + pub rangeproof: Option, +} + +impl Payload { + pub fn deserialize(bytes: &Vec) -> Result { + let payload: Payload = ser::deserialize_default(&mut &bytes[..])?; + Ok(payload) + } + + #[cfg(test)] + pub fn serialize(&self) -> Result> { + let mut vec = vec![]; + ser::serialize_default(&mut vec, &self)?; + Ok(vec) + } } impl Readable for Payload { - fn read(reader: &mut R) -> Result { - let version = reader.read_u8()?; - if version != CURRENT_VERSION { - return Err(ErrorKind::UnsupportedPayload.into()); - } + fn read(reader: &mut R) -> StdResult { + let version = reader.read_u8()?; + if version != CURRENT_VERSION { + return Err(ser::Error::UnsupportedProtocolVersion); + } - let excess = SecretKey::read(reader)?; - let rangeproof = if reader.read_u8()? == 0 { - None - } else { - Some(RangeProof::read(reader)?) - }; + let excess = secp::read_secret_key(reader)?; + let fee = FeeFields::try_from(reader.read_u64()?).map_err(|_| ser::Error::CorruptedData)?; + let rangeproof = if reader.read_u8()? == 0 { + None + } else { + Some(RangeProof::read(reader)?) + }; - let payload = Payload { - excess: excess, - rangeproof: rangeproof - }; - Ok(payload) - } + let payload = Payload { + excess, + fee, + rangeproof, + }; + Ok(payload) + } } impl Writeable for Payload { - fn write(&self, writer: &mut W) -> Result<()> { - writer.write_u8(CURRENT_VERSION)?; - writer.write_fixed_bytes(&self.excess)?; + fn write(&self, writer: &mut W) -> StdResult<(), ser::Error> { + writer.write_u8(CURRENT_VERSION)?; + writer.write_fixed_bytes(&self.excess)?; + writer.write_u64(self.fee.into())?; - match &self.rangeproof { - Some(proof) => { - writer.write_u8(1)?; - proof.write(writer)?; - }, - None => writer.write_u8(0)?, - }; + match &self.rangeproof { + Some(proof) => { + writer.write_u8(1)?; + proof.write(writer)?; + } + None => writer.write_u8(0)?, + }; - Ok(()) - } + Ok(()) + } } - -pub fn serialize_payload(payload: &Payload) -> Result> { - ser::ser_vec(&payload) -} - -pub fn deserialize_payload(bytes: &Vec) -> Result { - let mut cursor = Cursor::new(&bytes); - let mut reader = BinReader::new(&mut cursor); - Payload::read(&mut reader) -} - -pub struct Hop { - pub pubkey: PublicKey, - pub payload: Payload, -} - -pub struct Onion { - pub ephemeral_pubkey: PublicKey, - pub commit: Commitment, - pub enc_payloads: Vec, -} - -impl serde::ser::Serialize for Onion { - fn serialize(&self, serializer: S) -> std::result::Result - where - S: serde::ser::Serializer, - { - let mut state = serializer.serialize_struct("Onion", 3)?; - - let secp = Secp256k1::new(); - state.serialize_field("pubkey", &self.ephemeral_pubkey.serialize_vec(&secp, true).to_hex())?; - state.serialize_field("commit", &self.commit.to_hex())?; - - let hex_payloads: Vec = self.enc_payloads.iter().map(|v| v.to_hex()).collect(); - state.serialize_field("data", &hex_payloads)?; - state.end() - } -} - -impl<'de> serde::de::Deserialize<'de> for Onion { - fn deserialize(deserializer: D) -> std::result::Result - where - D: serde::de::Deserializer<'de>, - { - #[derive(Deserialize)] - #[serde(field_identifier, rename_all = "snake_case")] - enum Field { - Pubkey, - Commit, - Data - } - - struct OnionVisitor; - - impl<'de> serde::de::Visitor<'de> for OnionVisitor { - type Value = Onion; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - formatter.write_str("an Onion") - } - - fn visit_map(self, mut map: A) -> std::result::Result - where - A: serde::de::MapAccess<'de>, - { - let mut pubkey = None; - let mut commit = None; - let mut data = None; - - while let Some(key) = map.next_key()? { - match key { - Field::Pubkey => { - let val: String = map.next_value()?; - let vec = grin_util::from_hex(&val).map_err(serde::de::Error::custom)?; - let secp = Secp256k1::new(); - pubkey = Some(PublicKey::from_slice(&secp, &vec[..]).map_err(serde::de::Error::custom)?); - } - Field::Commit => { - let val: String = map.next_value()?; - let vec = grin_util::from_hex(&val).map_err(serde::de::Error::custom)?; - commit = Some(Commitment::from_vec(vec)); - } - Field::Data => { - let val: Vec = map.next_value()?; - let mut vec: Vec> = Vec::new(); - for hex in val { - vec.push(grin_util::from_hex(&hex).map_err(serde::de::Error::custom)?); - } - data = Some(vec); - } - } - } - - Ok(Onion { - ephemeral_pubkey: pubkey.unwrap(), - commit: commit.unwrap(), - enc_payloads: data.unwrap(), - }) - } - } - - const FIELDS: &[&str] = &[ - "pubkey", - "commit", - "data" - ]; - deserializer.deserialize_struct("Onion", &FIELDS, OnionVisitor) - } -} \ No newline at end of file diff --git a/src/wallet.rs b/src/wallet.rs new file mode 100644 index 0000000..1520812 --- /dev/null +++ b/src/wallet.rs @@ -0,0 +1,265 @@ +use crate::error::{ErrorKind, Result}; +use crate::secp; + +use grin_api::client; +use grin_api::json_rpc::{build_request, Request, Response}; +use grin_core::core::{ + FeeFields, Input, Inputs, KernelFeatures, Output, Transaction, TransactionBody, TxKernel, +}; +use grin_core::libtx::secp_ser; +use grin_keychain::BlindingFactor; +use grin_util::{ToHex, ZeroingString}; +use grin_wallet_api::{EncryptedRequest, EncryptedResponse, JsonId, Token}; +use secp256k1zkp::{ContextFlag, PublicKey, Secp256k1, SecretKey}; +use serde::{Deserialize, Serialize}; +use serde_json::json; +use std::net::SocketAddr; +use std::sync::Arc; + +pub trait Wallet: Send + Sync { + /// Builds an output for the wallet with the provided amount. + fn build_output(&self, amount: u64) -> Result<(BlindingFactor, Output)>; +} + +/// Builds and verifies a 'Transaction' using the provided components. +pub fn assemble_tx( + wallet: &Arc, + inputs: &Vec, + outputs: &Vec, + fee_base: u64, + total_fee: u64, + excesses: &Vec, +) -> Result { + let secp = Secp256k1::with_caps(ContextFlag::Commit); + let txn_inputs = Inputs::from(inputs.as_slice()); + let mut txn_outputs = outputs.clone(); + let mut txn_excesses = excesses.clone(); + let mut kernel_fee = total_fee; + + // calculate fee required if we add our own output + let fee_required = + TransactionBody::weight_by_iok(inputs.len() as u64, (outputs.len() + 1) as u64, 1) + * fee_base; + + // calculate fee to spend the output to ensure there's enough leftover to cover the fees for spending it + let fee_to_spend = TransactionBody::weight_by_iok(1, 0, 0) * fee_base; + + // collect any leftover fees + if total_fee > fee_required + fee_to_spend { + let amount = total_fee - fee_required; + kernel_fee -= amount; + + let wallet_output = wallet.build_output(amount)?; + txn_outputs.push(wallet_output.1); + + let output_excess = wallet_output + .0 + .secret_key(&secp) + .map_err(|_| ErrorKind::CorruptedData)?; + txn_excesses.push(output_excess); + } + + // generate random transaction offset + let offset = secp::random_secret(); + + // calculate kernel excess + let kern_excess = secp.blind_sum(txn_excesses, vec![offset.clone()])?; + + // build and verify kernel + let mut kernel = TxKernel::with_features(KernelFeatures::Plain { + fee: FeeFields::new(0, kernel_fee).unwrap(), + }); + let msg = kernel.msg_to_sign()?; + kernel.excess = secp::commit(0, &kern_excess)?; + kernel.excess_sig = secp::sign(&kern_excess, &msg)?; + kernel.verify()?; + + // assemble the transaction + let tx = Transaction::new(txn_inputs, &txn_outputs, &[kernel]) + .with_offset(BlindingFactor::from_secret_key(offset)); + Ok(tx) +} + +/// HTTP (JSONRPC) implementation of the 'Wallet' trait. +#[derive(Clone)] +pub struct HttpWallet { + wallet_owner_url: SocketAddr, + wallet_owner_secret: Option, + shared_key: SecretKey, + token: Token, +} + +const ENDPOINT: &str = "/v3/owner"; + +/// Wrapper for ECDH Public keys +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(transparent)] +pub struct ECDHPubkey { + /// public key, flattened + #[serde(with = "secp_ser::pubkey_serde")] + pub ecdh_pubkey: PublicKey, +} + +impl HttpWallet { + /// Calls the 'open_wallet' using the RPC API. + pub fn open_wallet( + wallet_owner_url: &SocketAddr, + wallet_owner_secret: &Option, + wallet_pass: &ZeroingString, + ) -> Result { + println!("Opening wallet at {}", wallet_owner_url); + let shared_key = HttpWallet::init_secure_api(&wallet_owner_url, &wallet_owner_secret)?; + + let open_wallet_params = json!({ + "name": null, + "password": wallet_pass.to_string() + }); + let token: Token = HttpWallet::send_enc_request( + &wallet_owner_url, + &wallet_owner_secret, + "open_wallet", + &open_wallet_params, + &shared_key, + )?; + println!("Connected to wallet"); + + Ok(HttpWallet { + wallet_owner_url: wallet_owner_url.clone(), + wallet_owner_secret: wallet_owner_secret.clone(), + shared_key: shared_key.clone(), + token: token.clone(), + }) + } + + fn init_secure_api( + wallet_owner_url: &SocketAddr, + wallet_owner_secret: &Option, + ) -> Result { + let secp = Secp256k1::new(); + let ephemeral_sk = secp::random_secret(); + let ephemeral_pk = PublicKey::from_secret_key(&secp, &ephemeral_sk)?; + let init_params = json!({ + "ecdh_pubkey": ephemeral_pk.serialize_vec(&secp, true).to_hex() + }); + + let response_pk: ECDHPubkey = HttpWallet::send_json_request( + &wallet_owner_url, + &wallet_owner_secret, + "init_secure_api", + &init_params, + )?; + + let shared_key = { + let mut shared_pubkey = response_pk.ecdh_pubkey.clone(); + shared_pubkey.mul_assign(&secp, &ephemeral_sk)?; + + let x_coord = shared_pubkey.serialize_vec(&secp, true); + SecretKey::from_slice(&secp, &x_coord[1..])? + }; + + Ok(shared_key) + } + + fn send_enc_request( + wallet_owner_url: &SocketAddr, + wallet_owner_secret: &Option, + method: &str, + params: &serde_json::Value, + shared_key: &SecretKey, + ) -> Result { + let url = format!("http://{}{}", wallet_owner_url, ENDPOINT); + let req = json!({ + "method": method, + "params": params, + "id": JsonId::IntId(1), + "jsonrpc": "2.0", + }); + let enc_req = EncryptedRequest::from_json(&JsonId::IntId(1), &req, &shared_key)?; + let res = client::post::( + url.as_str(), + wallet_owner_secret.clone(), + &enc_req, + )?; + let decrypted = res.decrypt(&shared_key)?; + let response: Response = serde_json::from_value(decrypted.clone())?; + let parsed = serde_json::from_value(response.result.unwrap().get("Ok").unwrap().clone())?; + Ok(parsed) + } + + fn send_json_request( + wallet_owner_url: &SocketAddr, + wallet_owner_secret: &Option, + method: &str, + params: &serde_json::Value, + ) -> Result { + let url = format!("http://{}{}", wallet_owner_url, ENDPOINT); + let req = build_request(method, params); + let res = + client::post::(url.as_str(), wallet_owner_secret.clone(), &req)?; + let parsed = res.clone().into_result()?; + Ok(parsed) + } +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct OutputWithBlind { + #[serde( + serialize_with = "secp_ser::as_hex", + deserialize_with = "secp_ser::blind_from_hex" + )] + blind: BlindingFactor, + output: Output, +} + +impl Wallet for HttpWallet { + /// Builds an 'Output' for the wallet using the 'build_output' RPC API. + fn build_output(&self, amount: u64) -> Result<(BlindingFactor, Output)> { + let req_json = json!({ + "token": self.token.keychain_mask.clone().unwrap().0, + "features": "Plain", + "amount": amount + }); + let output: OutputWithBlind = HttpWallet::send_enc_request( + &self.wallet_owner_url, + &self.wallet_owner_secret, + "build_output", + &req_json, + &self.shared_key, + )?; + Ok((output.blind, output.output)) + } +} + +#[cfg(test)] +pub mod mock { + use super::Wallet; + use crate::error::Result; + use crate::secp; + + use grin_core::core::{Output, OutputFeatures}; + use grin_keychain::BlindingFactor; + use secp256k1zkp::Secp256k1; + + /// HTTP (JSONRPC) implementation of the 'Wallet' trait. + #[derive(Clone)] + pub struct MockWallet {} + + impl Wallet for MockWallet { + /// Builds an 'Output' for the wallet using the 'build_output' RPC API. + fn build_output(&self, amount: u64) -> Result<(BlindingFactor, Output)> { + let secp = Secp256k1::new(); + let blind = secp::random_secret(); + let commit = secp::commit(amount, &blind)?; + let proof = secp.bullet_proof( + amount, + blind.clone(), + secp::random_secret(), + secp::random_secret(), + None, + None, + ); + let output = Output::new(OutputFeatures::Plain, commit.clone(), proof); + Ok((BlindingFactor::from_secret_key(blind), output)) + } + } +}