diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd2fef5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +*.swp +.DS_Store +target +*/Cargo.lock +*.iml +.idea/ +.vscode/ diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..043c36f --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1652 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "addr2line" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61f2b7f93d2c7d2b08263acaa4a363b3e276806c68af6134c44f523bf1aacd" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "arc-swap" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dabe5a181f83789739c194cbe5a897dde195078fac08568d09221fd6137a7ba8" + +[[package]] +name = "arrayvec" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06f59fe10306bb78facd90d28c2038ad23ffaaefa85bac43c8a434cde383334f" +dependencies = [ + "nodrop", + "odds", +] + +[[package]] +name = "arrayvec" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" +dependencies = [ + "nodrop", +] + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "backtrace" +version = "0.3.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091bcdf2da9950f96aa522681ce805e6857f6ca8df73833d35736ab2dc78e152" +dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", +] + +[[package]] +name = "base64" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3441f0f7b02788e948e47f457ca01f1d7e6d92c693bc132c22b087d3141c03ff" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "blake2-rfc" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" +dependencies = [ + "arrayvec 0.4.12", + "constant_time_eq", +] + +[[package]] +name = "block-buffer" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "memchr", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cc" +version = "1.0.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chacha20" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01b72a433d0cf2aef113ba70f62634c56fddb0f244e6377185c56a7cadbd8f91" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", +] + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "cipher" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" +dependencies = [ + "generic-array", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "cpufeatures" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crypto-mac" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array", +] + +[[package]] +name = "dtoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56899898ce76aaf4a0f24d914c97ea6ed976d42fec6ad33fcbb0a1103e07b2b0" + +[[package]] +name = "failure" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" +dependencies = [ + "backtrace", + "failure_derive", +] + +[[package]] +name = "failure_derive" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "futures" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" + +[[package]] +name = "futures-executor" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" + +[[package]] +name = "futures-macro" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18e4a4b95cea4b4ccbcf1c5675ca7c4ee4e9e75eb79944d07defde18068f79bb" +dependencies = [ + "autocfg 1.0.1", + "proc-macro-hack", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" + +[[package]] +name = "futures-task" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" + +[[package]] +name = "futures-util" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" +dependencies = [ + "autocfg 1.0.1", + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "proc-macro-hack", + "proc-macro-nested", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "gimli" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" + +[[package]] +name = "globset" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10463d9ff00a2a068db14231982f5132edebad0d7660cd956a1c30292dbcbfbd" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "grin_secp256k1zkp" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3af3c4c4829b3e2e7ee1d9a542833e4244912fbb887fabe44682558159b068a7" +dependencies = [ + "arrayvec 0.3.25", + "cc", + "libc", + "rand 0.5.6", + "rustc-serialize", + "serde", + "serde_json", + "zeroize", +] + +[[package]] +name = "grin_util" +version = "5.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdbab6869a23e2c2b4307f4015aa1f04242aace9ca62fbe34b5ad192f6b9d9e6" +dependencies = [ + "backtrace", + "base64", + "byteorder", + "grin_secp256k1zkp", + "lazy_static", + "log", + "log4rs", + "parking_lot 0.10.2", + "rand 0.6.5", + "serde", + "serde_derive", + "walkdir", + "zeroize", + "zip", +] + +[[package]] +name = "h2" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd819562fcebdac5afc5c113c3ec36f902840b70fd4fc458799c8ce4607ae55" +dependencies = [ + "bytes 1.1.0", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "hmac" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b" +dependencies = [ + "crypto-mac", + "digest", +] + +[[package]] +name = "http" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" +dependencies = [ + "bytes 1.1.0", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes 1.1.0", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" + +[[package]] +name = "httpdate" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + +[[package]] +name = "hyper" +version = "0.14.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b91bb1f221b6ea1f1e4371216b70f40748774c2fb5971b450c07773fb92d26b" +dependencies = [ + "bytes 1.1.0", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg 1.0.1", + "hashbrown", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "jsonrpc-core" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14f7f76aef2d054868398427f6c54943cf3d1caa9a7ec7d0c38d69df97a965eb" +dependencies = [ + "futures", + "futures-executor", + "futures-util", + "log", + "serde", + "serde_derive", + "serde_json", +] + +[[package]] +name = "jsonrpc-derive" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b939a78fa820cdfcb7ee7484466746a7377760970f6f9c6fe19f9edcc8a38d2" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jsonrpc-http-server" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1dea6e07251d9ce6a552abfb5d7ad6bc290a4596c8dcc3d795fae2bbdc1f3ff" +dependencies = [ + "futures", + "hyper", + "jsonrpc-core", + "jsonrpc-server-utils", + "log", + "net2", + "parking_lot 0.11.2", + "unicase", +] + +[[package]] +name = "jsonrpc-server-utils" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4fdea130485b572c39a460d50888beb00afb3e35de23ccd7fad8ff19f0e0d4" +dependencies = [ + "bytes 1.1.0", + "futures", + "globset", + "jsonrpc-core", + "lazy_static", + "log", + "tokio", + "tokio-stream", + "tokio-util", + "unicase", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.105" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "869d572136620d55835903746bcb5cdc54cb2851fd0aeec53220b4bb65ef3013" + +[[package]] +name = "linked-hash-map" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" + +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "lock_api" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", + "serde", +] + +[[package]] +name = "log-mdc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" + +[[package]] +name = "log4rs" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4d8e6e1d5f89acca713132acc6034f30bad09b961d1338161bdb71c08f6e4fa" +dependencies = [ + "arc-swap", + "chrono", + "flate2", + "fnv", + "humantime", + "libc", + "log", + "log-mdc", + "parking_lot 0.10.2", + "serde", + "serde-value", + "serde_derive", + "serde_json", + "serde_yaml", + "thread-id", + "typemap", + "winapi", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg 1.0.1", +] + +[[package]] +name = "mio" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "mwixnet" +version = "0.1.0" +dependencies = [ + "blake2-rfc", + "byteorder", + "bytes 0.5.6", + "chacha20", + "failure", + "futures", + "grin_secp256k1zkp", + "grin_util", + "hmac", + "hyper", + "jsonrpc-core", + "jsonrpc-derive", + "jsonrpc-http-server", + "lazy_static", + "rand 0.8.4", + "serde", + "serde_derive", + "serde_json", + "sha2", + "tokio", +] + +[[package]] +name = "net2" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "391630d12b68002ae1e25e8f974306474966550ad82dac6886fb8910c19568ae" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "winapi", +] + +[[package]] +name = "nodrop" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg 1.0.1", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg 1.0.1", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "object" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" +dependencies = [ + "memchr", +] + +[[package]] +name = "odds" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eae0151b9dacf24fcc170d9995e511669a082856a91f958a2fe380bfab3fb22" + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "opaque-debug" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" + +[[package]] +name = "ordered-float" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3305af35278dd29f46fcdd139e0b1fbfae2153f0e5928b39b035542dd31e37b7" +dependencies = [ + "num-traits", +] + +[[package]] +name = "parking_lot" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +dependencies = [ + "lock_api 0.3.4", + "parking_lot_core 0.7.2", +] + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api 0.4.5", + "parking_lot_core 0.8.5", +] + +[[package]] +name = "parking_lot_core" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +dependencies = [ + "cfg-if 0.1.10", + "cloudabi", + "libc", + "redox_syscall 0.1.57", + "smallvec", + "winapi", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if 1.0.0", + "instant", + "libc", + "redox_syscall 0.2.10", + "smallvec", + "winapi", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "ppv-lite86" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" + +[[package]] +name = "proc-macro-crate" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6ea3c4595b96363c13943497db34af4460fb474a95c43f4446ad341b8c9785" +dependencies = [ + "toml", +] + +[[package]] +name = "proc-macro-hack" +version = "0.5.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5" + +[[package]] +name = "proc-macro-nested" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc881b2c22681370c6a780e47af9840ef841837bc98118431d4e1868bd0c1086" + +[[package]] +name = "proc-macro2" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "winapi", +] + +[[package]] +name = "rand" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d71dacdc3c88c1fde3885a3be3fbab9f35724e6ce99467f7d9c5026132184ca" +dependencies = [ + "autocfg 0.1.7", + "libc", + "rand_chacha 0.1.1", + "rand_core 0.4.2", + "rand_hc 0.1.0", + "rand_isaac", + "rand_jitter", + "rand_os", + "rand_pcg", + "rand_xorshift", + "winapi", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.3", + "rand_hc 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556d3a1ca6600bfcbab7c7c91ccb085ac7fbbcd70e008a98742e7847f4f7bcef" +dependencies = [ + "autocfg 0.1.7", + "rand_core 0.3.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.3", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b40677c7be09ae76218dc623efbf7b18e34bced3f38883af07bb75630a21bc4" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core 0.6.3", +] + +[[package]] +name = "rand_isaac" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded997c9d5f13925be2a6fd7e66bf1872597f759fd9dd93513dd7e92e5a5ee08" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rand_jitter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b" +dependencies = [ + "libc", + "rand_core 0.4.2", + "winapi", +] + +[[package]] +name = "rand_os" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b75f676a1e053fc562eafbb47838d67c84801e38fc1ba459e8f180deabd5071" +dependencies = [ + "cloudabi", + "fuchsia-cprng", + "libc", + "rand_core 0.4.2", + "rdrand", + "winapi", +] + +[[package]] +name = "rand_pcg" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abf9b09b01790cfe0364f52bf32995ea3c39f4d2dd011eac241d2914146d0b44" +dependencies = [ + "autocfg 0.1.7", + "rand_core 0.4.2", +] + +[[package]] +name = "rand_xorshift" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf7e9e623549b0e21f6e97cf8ecf247c1a8fd2e8a992ae265314300b2455d5c" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rustc-demangle" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ef03e0a2b150c7a90d01faf6254c9c48a41e95fb2a8c2ac1c6f0d2b9aefc342" + +[[package]] +name = "rustc-serialize" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a65a7291a8a568adcae4c10a677ebcedbc6c9cec91c054dee2ce40b0e3290eb" +dependencies = [ + "ordered-float", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f690853975602e1bfe1ccbf50504d67174e3bcf340f23b5ea9992e0587a52d8" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8c608a35705a5d3cdc9fbe403147647ff34b921f8e833e49306df898f9b20af" +dependencies = [ + "dtoa", + "indexmap", + "serde", + "yaml-rust", +] + +[[package]] +name = "sha2" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa" +dependencies = [ + "block-buffer", + "cfg-if 1.0.0", + "cpufeatures", + "digest", + "opaque-debug", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e51e73328dc4ac0c7ccbda3a494dfa03df1de2f46018127f60c693f2648455b0" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5" + +[[package]] +name = "smallvec" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" + +[[package]] +name = "socket2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dc90fe6c7be1a323296982db1836d1ea9e47b6839496dde9a541bc496df3516" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "subtle" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" + +[[package]] +name = "syn" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "synstructure" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "unicode-xid", +] + +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread-id" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fbf4c9d56b320106cd64fd024dadfa0be7cb4706725fc44a7d7ce952d820c1" +dependencies = [ + "libc", + "redox_syscall 0.1.57", + "winapi", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "tokio" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc" +dependencies = [ + "autocfg 1.0.1", + "bytes 1.1.0", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot 0.11.2", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2dd85aeaba7b68df939bd357c6afb36c87951be9e80bf9c859f2fc3e9fca0fd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-stream" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50145484efff8818b5ccd256697f36863f587da82cf8b409c53adf1e840798e3" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d3725d3efa29485e87311c5b699de63cde14b00ed4d256b8318aa30ca452cd" +dependencies = [ + "bytes 1.1.0", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "traitobject" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "typemap" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" +dependencies = [ + "unsafe-any", +] + +[[package]] +name = "typenum" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" + +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" +dependencies = [ + "version_check", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "unsafe-any" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30360d7979f5e9c6e6cea48af192ea8fab4afb3cf72597154b8f08935bc9c7f" +dependencies = [ + "traitobject", +] + +[[package]] +name = "version_check" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" + +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.10.2+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "yaml-rust" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +dependencies = [ + "linked-hash-map", +] + +[[package]] +name = "zeroize" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf68b08513768deaa790264a7fac27a58cbf2705cfcdc9448362229217d7e970" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdff2024a851a322b08f179173ae2ba620445aef1e838f0c196820eade4ae0c7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zip" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815" +dependencies = [ + "byteorder", + "crc32fast", + "thiserror", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..491644d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "mwixnet" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +blake2 = { package = "blake2-rfc", version = "0.2"} +byteorder = "1" +bytes = "0.5.6" +chacha20 = "0.8.1" +failure = "0.1.8" +futures = "0.3" +hmac = { version = "0.11.0", features = ["std"]} +hyper = { version = "0.14", features = ["full"] } +jsonrpc-core = "18.0" +jsonrpc-derive = "18.0" +jsonrpc-http-server = "18.0" +lazy_static = "1" +rand = "0.8.4" +serde = { version = "1", features= ["derive"]} +serde_derive = "1" +serde_json = "1" +sha2 = "0.9.8" +tokio = { version = "1", features = ["full"] } +grin_secp256k1zkp = { version = "0.7.11", features = ["bullet-proof-sizing"]} +grin_util = "5" \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..361d3c3 --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# MWixnet +This is an implementation of @tromp's [CoinSwap Proposal](https://forum.grin.mw/t/mimblewimble-coinswap-proposal/8322) with some slight modifications. + +A set of n CoinSwap servers (nodei with i=1...n) are agreed upon in advance. They each have a known public key. + +### SWAP API +The first CoinSwap server (n1) provides the `swap` API, publicly available for use by GRIN wallets. + +**jsonrpc:** `2.0` +**method:** `swap` +**params:** +``` +[{ + "comsig": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f", + "msg": "00010203", + "onion": { + "commit": "0967593792bc958cd73848c0b948ecab2c6e996ab3c550d462fe41359e447b651f", + "data": ["3719e5fba260c71a5a4bcf9d9caa58cd5dc49531388782fae7699c6fa6b30b09fe42"], + "pubkey": "020dd38a220280f14515f6901a3a366cb7b87630814e4b68b3189a32df964961e5" + } +}] +``` + +### Data Provisioning +#### Inputs +* Cin: UTXO commitment to swap +* xin: Blinding factor of Cin +* K1...n: The public keys of all n servers + +#### Procedure +
    +
  1. Choose random xi for each node ni and create a Payload (Pi) for each containing xi
  2. +
  3. Build a rangeproof for Cn=Cin+(Σx1...n)*G and include it in payload Pn
  4. +
  5. Choose random initial ephemeral keypair (r1, R1)
  6. +
  7. Derive remaining ephemeral keypairs such that ri+1=ri*Sha256(Ri||si) where si=ECDH(Ri, Ki)
  8. +
  9. For each node ni, use ChaCha20 stream cipher with key=HmacSha256("MWIXNET"||si) and nonce "NONCE1234567" to encrypt payloads Pi...n
  10. +
+ +### Input Validation + +* Node n1 verifies that Cin is in the current UTXO set +* Node n1 verifies the commitment signature is valid for Cin, proving ownership of the input + +---- + +`Output derivation`, `Output validation`, `Kernel derivation`, and `Aggregation` steps remain unchanged from the [original design](https://forum.grin.mw/t/mimblewimble-coinswap-proposal/8322) \ No newline at end of file diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..034ae27 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,110 @@ +use failure::{self, Context, Fail}; +use std::fmt::{self, Display}; +use std::io; + +/// MWixnet error definition +#[derive(Debug)] +pub struct Error { + inner: Context, +} + +pub type Result = std::result::Result; + +#[derive(Clone, Debug, Eq, Fail, PartialEq)] +/// MWixnet error types +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, +} + +impl std::error::Error for Error { + +} + +impl From for Error { + 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() + } +} + +impl Display for Error { + 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 message(&self) -> String { + format!("{}", self).into() + } +} + +impl From for Error { + fn from(kind: ErrorKind) -> Error { + Error { + inner: Context::new(kind), + } + } +} + +impl From> for Error { + fn from(inner: Context) -> Error { + Error { inner } + } +} + +impl From for Error { + 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 diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..82e890a --- /dev/null +++ b/src/main.rs @@ -0,0 +1,29 @@ +use server::ServerConfig; + +#[macro_use] +extern crate lazy_static; + +mod error; +mod onion; +mod secp; +mod ser; +mod server; +mod types; + +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 + }; + + let shutdown_signal = async move { + // Wait for the CTRL+C signal + tokio::signal::ctrl_c() + .await + .expect("failed to install CTRL+C signal handler"); + }; + + server::listen(&server_config, shutdown_signal) +} \ No newline at end of file diff --git a/src/onion.rs b/src/onion.rs new file mode 100644 index 0000000..8c49e85 --- /dev/null +++ b/src/onion.rs @@ -0,0 +1,167 @@ +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 chacha20::{ChaCha20, Key, Nonce}; +use chacha20::cipher::{NewCipher, StreamCipher}; +use hmac::{Hmac, Mac, NewMac}; +use sha2::{Digest, Sha256}; + +type HmacSha256 = Hmac; + +/// 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) +} + +/// 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)?; + + let mut decrypted_bytes = onion.enc_payloads[0].clone(); + cipher.apply_keystream(&mut decrypted_bytes); + let decrypted_payload = deserialize_payload(&decrypted_bytes)?; + + 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 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 commitment = onion.commit.clone(); + commitment = secp::add_excess(&commitment, &decrypted_payload.excess)?; + + let peeled_onion = Onion{ + ephemeral_pubkey: ephemeral_pubkey, + commit: commitment.clone(), + enc_payloads: 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)?; + + 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 key = Key::from_slice(&mukey[0..32]); + let nonce = Nonce::from_slice(b"NONCE1234567"); + + Ok(ChaCha20::new(&key, &nonce)) +} + +#[cfg(test)] +mod tests { + use super::super::secp; + use super::super::types; + use super::super::onion; + + /// 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 diff --git a/src/secp.rs b/src/secp.rs new file mode 100644 index 0000000..44267be --- /dev/null +++ b/src/secp.rs @@ -0,0 +1,194 @@ +pub use secp256k1zkp::{ContextFlag, Message, Secp256k1, Signature}; +pub use secp256k1zkp::ecdh::SharedSecret; +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}; + +use crate::ser::{Readable, Reader, Writeable, Writer}; +use crate::error::{ErrorKind, Result}; + +use rand::RngCore; +use std::cmp; + +/// 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(()) + } +} + +impl AsRef<[u8]> for ComSignature { + fn as_ref(&self) -> &[u8] { + &self.0 + } +} + +/// Serializes a ComSignature to and from hex +pub mod comsig_serde { + use super::ComSignature; + use serde::{Deserialize, Serializer}; + use grin_util::ToHex; + + /// Serializes a ComSignature as a hex string + pub fn serialize(comsig: &ComSignature, serializer: S) -> Result + where + S: Serializer, + { + serializer.serialize_str(&comsig.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()))) + } +} + +/// 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) +} + +/// 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) +} + +/// 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) +} + +/// 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 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)) + } +} + +impl Writeable for Commitment { + fn write(&self, writer: &mut W) -> Result<()> { + writer.write_fixed_bytes(self) + } +} + +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, + }) + } +} + +impl Writeable for RangeProof { + fn write(&self, writer: &mut W) -> Result<()> { + writer.write_bytes(self) + } +} + +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()) + } +} + +impl Writeable for Signature { + fn write(&self, writer: &mut W) -> Result<()> { + writer.write_fixed_bytes(self) + } +} + +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) + } +} + +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(()) + } +} + +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) + } +} + +impl Writeable for SecretKey { + fn write(&self, writer: &mut W) -> Result<()> { + writer.write_fixed_bytes(self.0)?; + Ok(()) + } +} \ No newline at end of file diff --git a/src/ser.rs b/src/ser.rs new file mode 100644 index 0000000..a86ef2e --- /dev/null +++ b/src/ser.rs @@ -0,0 +1,624 @@ +// 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 new file mode 100644 index 0000000..8e402e6 --- /dev/null +++ b/src/server.rs @@ -0,0 +1,213 @@ +use crate::onion; +use crate::secp::{self, Commitment, ComSignature, SecretKey}; +use crate::ser; +use crate::types::Onion; + +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; + +#[derive(Clone, Debug, PartialEq)] +pub struct ServerConfig { + pub key: SecretKey, + pub addr: SocketAddr, + pub is_first: bool, +} + +pub struct Submission { + pub excess: SecretKey, + pub input_commit: Commitment, + pub onion: Onion, +} + +#[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; +} + +pub struct ServerImpl { + server_key: SecretKey, +} + +impl ServerImpl { + pub fn new(server_key: SecretKey) -> Self { + ServerImpl { server_key } + } +} + +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())) + } + +} + +/// 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()))); + + 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"); + + let close_handle = server.close_handle(); + std::thread::spawn(move || { + futures::executor::block_on(shutdown_signal); + close_handle.close(); + }); + server.wait(); + + Ok(()) +} + +#[cfg(test)] +mod tests { + use crate::{onion, secp, server, types}; + use std::net::TcpListener; + use std::time::Duration; + use std::thread; + + use hyper::{Body, Client, Request, Response}; + use tokio::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_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 + }; + + let threaded_rt = runtime::Runtime::new()?; + let (shutdown_sender, shutdown_receiver) = futures::channel::oneshot::channel(); + let uri = format!("http://{}/v1", server_config.addr); + + // Spawn the server task + threaded_rt.spawn(async move { + server::listen(&server_config, async { shutdown_receiver.await.ok(); }).unwrap() + }); + + // Wait for listener + thread::sleep(Duration::from_millis(500)); + + 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)); + + shutdown_sender.send(()).ok(); + + // Wait for shutdown + thread::sleep(Duration::from_millis(500)); + threaded_rt.shutdown_background(); + + Ok(response_str) + } + + /// 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()?; + + 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 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(()) + } + + #[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(()) + } + + // milestone 2 - add tests to cover invalid comsig's & inputs not in utxo set +} \ No newline at end of file diff --git a/src/types.rs b/src/types.rs new file mode 100644 index 0000000..0f51aae --- /dev/null +++ b/src/types.rs @@ -0,0 +1,166 @@ +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 serde::{Deserialize, Serialize}; +use serde::ser::SerializeStruct; +use std::fmt; +use std::io::Cursor; + +pub type RawBytes = Vec; + +const CURRENT_VERSION : u8 = 0; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Payload { + pub excess: SecretKey, + pub rangeproof: Option, +} + +impl Readable for Payload { + fn read(reader: &mut R) -> Result { + let version = reader.read_u8()?; + if version != CURRENT_VERSION { + return Err(ErrorKind::UnsupportedPayload.into()); + } + + let excess = SecretKey::read(reader)?; + let rangeproof = if reader.read_u8()? == 0 { + None + } else { + Some(RangeProof::read(reader)?) + }; + + let payload = Payload { + excess: excess, + rangeproof: 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)?; + + match &self.rangeproof { + Some(proof) => { + writer.write_u8(1)?; + proof.write(writer)?; + }, + None => writer.write_u8(0)?, + }; + + 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