depolymerization

wire gateway for Bitcoin/Ethereum
Log | Files | Refs | Submodules | README | LICENSE

commit f0133d33be5b1666d20b640c57a497bc239caba5
parent 491410969b1fec9c0e01def555a970f02c3d76af
Author: Antoine A <>
Date:   Tue, 30 Nov 2021 12:25:14 +0100

Rename refund to bounce and improve error handling

Diffstat:
MCargo.lock | 3173++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mbtc-wire/Cargo.toml | 2++
Mbtc-wire/benches/metadata.rs | 4++--
Mbtc-wire/src/bin/btc-wire-cli.rs | 8++++----
Mbtc-wire/src/bin/test.rs | 70+++++++++++++++++++++++++++++++++++++++++++++++++---------------------
Mbtc-wire/src/lib.rs | 462++++++++++---------------------------------------------------------------------
Mbtc-wire/src/main.rs | 2+-
Abtc-wire/src/rpc_utils.rs | 177+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abtc-wire/src/segwit.rs | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Abtc-wire/src/test.rs | 11+++++++++++
Mscript/test_bank.sh | 8++++----
Mwire-gateway/src/main.rs | 731+++++++++++++++++++++++++++++++++++++++----------------------------------------
12 files changed, 2410 insertions(+), 2387 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock @@ -1,1586 +1,1587 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "argh" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f023c76cd7975f9969f8e29f0e461decbdc7f51048ce43427107a3d192f1c9bf" -dependencies = [ - "argh_derive", - "argh_shared", -] - -[[package]] -name = "argh_derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48ad219abc0c06ca788aface2e3a1970587e3413ab70acd20e54b6ec524c1f8f" -dependencies = [ - "argh_shared", - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "argh_shared" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38de00daab4eac7d753e97697066238d67ce9d7e2d823ab4f72fe14af29f3f33" - -[[package]] -name = "async-compression" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443ccbb270374a2b1055fc72da40e1f237809cd6bb0e97e66d264cd138473a6" -dependencies = [ - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "base32" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" - -[[package]] -name = "base64-compat" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7" -dependencies = [ - "byteorder", -] - -[[package]] -name = "bech32" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" - -[[package]] -name = "bitcoin" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a41df6ad9642c5c15ae312dd3d074de38fd3eb7cc87ad4ce10f90292a83fe4d" -dependencies = [ - "bech32", - "bitcoin_hashes", - "secp256k1", - "serde", -] - -[[package]] -name = "bitcoin_hashes" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006cc91e1a1d99819bc5b8214be3555c1f0611b169f527a1fdc54ed1f2b745b0" -dependencies = [ - "serde", -] - -[[package]] -name = "bitcoincore-rpc" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b8d99d58466295cb2bf72c6959b784d59f8f0d6977458d2ba3eb75c834f36c3" -dependencies = [ - "bitcoincore-rpc-json", - "jsonrpc", - "log", - "serde", - "serde_json", -] - -[[package]] -name = "bitcoincore-rpc-json" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce91de73c61f5776cf938bfa88378c5b404a70e3369b761dacbe6024fea79dd" -dependencies = [ - "bitcoin", - "serde", - "serde_json", -] - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "bstr" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" -dependencies = [ - "lazy_static", - "memchr", - "regex-automata", - "serde", -] - -[[package]] -name = "btc-wire" -version = "0.1.0" -dependencies = [ - "argh", - "bech32", - "bitcoincore-rpc", - "criterion", - "fastrand", - "owo-colors", - "rand", - "rustyline", - "serde", -] - -[[package]] -name = "bumpalo" -version = "3.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "bytes" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" - -[[package]] -name = "cast" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" -dependencies = [ - "rustc_version", -] - -[[package]] -name = "cc" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "clap" -version = "2.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -dependencies = [ - "bitflags", - "textwrap", - "unicode-width", -] - -[[package]] -name = "clipboard-win" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db8340083d28acb43451166543b98c838299b7e0863621be53a338adceea0ed" -dependencies = [ - "error-code", - "str-buf", - "winapi", -] - -[[package]] -name = "crc32fast" -version = "1.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3825b1e8580894917dc4468cb634a1b4e9745fddc854edad72d9c04644c0319f" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "criterion" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" -dependencies = [ - "atty", - "cast", - "clap", - "criterion-plot", - "csv", - "itertools", - "lazy_static", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_cbor", - "serde_derive", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[package]] -name = "criterion-plot" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" -dependencies = [ - "cast", - "itertools", -] - -[[package]] -name = "crossbeam-channel" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" -dependencies = [ - "cfg-if", - "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", - "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", - "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", - "lazy_static", -] - -[[package]] -name = "csv" -version = "1.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" -dependencies = [ - "bstr", - "csv-core", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "csv-core" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" -dependencies = [ - "memchr", -] - -[[package]] -name = "darling" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" -dependencies = [ - "darling_core", - "quote", - "syn", -] - -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - -[[package]] -name = "dirs-sys-next" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] - -[[package]] -name = "either" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" - -[[package]] -name = "endian-type" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" - -[[package]] -name = "error-code" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5115567ac25674e0043e472be13d14e537f37ea8aa4bdc4aef0c89add1db1ff" -dependencies = [ - "libc", - "str-buf", -] - -[[package]] -name = "fastrand" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" -dependencies = [ - "instant", -] - -[[package]] -name = "fd-lock" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfc110fe50727d46a428eed832df40affe9bf74d077cac1bf3f2718e823f14c5" -dependencies = [ - "cfg-if", - "libc", - "windows-sys", -] - -[[package]] -name = "flate2" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" -dependencies = [ - "cfg-if", - "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 = "form_urlencoded" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" -dependencies = [ - "matches", - "percent-encoding", -] - -[[package]] -name = "futures-channel" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27" -dependencies = [ - "futures-core", -] - -[[package]] -name = "futures-core" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445" - -[[package]] -name = "futures-task" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12" - -[[package]] -name = "futures-util" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e" -dependencies = [ - "futures-core", - "futures-task", - "pin-project-lite", - "pin-utils", -] - -[[package]] -name = "getrandom" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "half" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "http" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - -[[package]] -name = "http-body" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" -dependencies = [ - "bytes", - "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.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" - -[[package]] -name = "hyper" -version = "0.14.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436ec0091e4f20e655156a30a0df3770fe2900aa301e548e08446ec794b6953c" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[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 = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "itertools" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" -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" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad24d69a8a0698db8ffb9048e937e8ae3ee3bc45772a5d7b6979b1d2d5b6a9f7" -dependencies = [ - "base64-compat", - "serde", - "serde_derive", - "serde_json", -] - -[[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.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" - -[[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", -] - -[[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 = "memoffset" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" -dependencies = [ - "autocfg", -] - -[[package]] -name = "miniz_oxide" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" -dependencies = [ - "adler", - "autocfg", -] - -[[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 = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - -[[package]] -name = "nix" -version = "0.22.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3bb9a13fa32bc5aeb64150cd3f32d6cf4c748f8f8a417cce5d2eb976a8370ba" -dependencies = [ - "bitflags", - "cc", - "cfg-if", - "libc", - "memoffset", -] - -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi", -] - -[[package]] -name = "num-traits" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" -dependencies = [ - "autocfg", -] - -[[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 = "once_cell" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" - -[[package]] -name = "oorandom" -version = "11.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" - -[[package]] -name = "owo-colors" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9ad6d222cdc2351ccabb7af4f68bfaecd601b33c5f10d410ec89d2a273f6fff" - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[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 = "plotters" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" -dependencies = [ - "num-traits", - "plotters-backend", - "plotters-svg", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "plotters-backend" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" - -[[package]] -name = "plotters-svg" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" -dependencies = [ - "plotters-backend", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" - -[[package]] -name = "proc-macro2" -version = "1.0.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "quote" -version = "1.0.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - -[[package]] -name = "rand" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[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", -] - -[[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.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - -[[package]] -name = "rayon" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" -dependencies = [ - "autocfg", - "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 = "redox_syscall" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" -dependencies = [ - "bitflags", -] - -[[package]] -name = "redox_users" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" -dependencies = [ - "getrandom", - "redox_syscall", -] - -[[package]] -name = "regex" -version = "1.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" -dependencies = [ - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" - -[[package]] -name = "regex-syntax" -version = "0.6.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" - -[[package]] -name = "rustc_version" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" -dependencies = [ - "semver", -] - -[[package]] -name = "rustversion" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" - -[[package]] -name = "rustyline" -version = "9.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "790487c3881a63489ae77126f57048b42d62d3b2bafbf37453ea19eedb6340d6" -dependencies = [ - "bitflags", - "cfg-if", - "clipboard-win", - "dirs-next", - "fd-lock", - "libc", - "log", - "memchr", - "nix", - "radix_trie", - "scopeguard", - "smallvec", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "winapi", -] - -[[package]] -name = "ryu" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568" - -[[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 = "secp256k1" -version = "0.20.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" -dependencies = [ - "secp256k1-sys", - "serde", -] - -[[package]] -name = "secp256k1-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "827cb7cce42533829c792fc51b82fbf18b125b45a702ef2c8be77fce65463a7b" -dependencies = [ - "cc", -] - -[[package]] -name = "semver" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" - -[[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_cbor" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" -dependencies = [ - "half", - "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.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527" -dependencies = [ - "itoa", - "ryu", - "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_with" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad6056b4cb69b6e43e3a0f055def223380baecc99da683884f205bf347f7c4b3" -dependencies = [ - "rustversion", - "serde", - "serde_with_macros", -] - -[[package]] -name = "serde_with_macros" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e47be9471c72889ebafb5e14d5ff930d89ae7a67bbdb5f8abb564f845a927e" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - -[[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 = "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 = "str-buf" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "1.0.82" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[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" -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 = "tinytemplate" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" -dependencies = [ - "serde", - "serde_json", -] - -[[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 = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144" -dependencies = [ - "autocfg", - "bytes", - "libc", - "memchr", - "mio", - "num_cpus", - "once_cell", - "parking_lot", - "pin-project-lite", - "signal-hook-registry", - "tokio-macros", - "winapi", -] - -[[package]] -name = "tokio-macros" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[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", - "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 = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" - -[[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.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" - -[[package]] -name = "uri-pack" -version = "0.1.0" -dependencies = [ - "csv", - "idna", - "percent-encoding", - "serde_json", - "thiserror", - "url", -] - -[[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", - "serde", -] - -[[package]] -name = "utf8parse" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" - -[[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 = "wasm-bindgen" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" -dependencies = [ - "cfg-if", - "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", - "quote", - "syn", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" -dependencies = [ - "quote", - "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", - "quote", - "syn", - "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 = "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 = "windows-sys" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6" -dependencies = [ - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_msvc" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2" - -[[package]] -name = "windows_i686_gnu" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a" - -[[package]] -name = "windows_i686_msvc" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f" - -[[package]] -name = "wire-gateway" -version = "0.1.0" -dependencies = [ - "async-compression", - "base32", - "btc-wire", - "hyper", - "rand", - "serde", - "serde_json", - "serde_urlencoded", - "serde_with", - "thiserror", - "tokio", - "url", -] +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "argh" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f023c76cd7975f9969f8e29f0e461decbdc7f51048ce43427107a3d192f1c9bf" +dependencies = [ + "argh_derive", + "argh_shared", +] + +[[package]] +name = "argh_derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48ad219abc0c06ca788aface2e3a1970587e3413ab70acd20e54b6ec524c1f8f" +dependencies = [ + "argh_shared", + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "argh_shared" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38de00daab4eac7d753e97697066238d67ce9d7e2d823ab4f72fe14af29f3f33" + +[[package]] +name = "async-compression" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443ccbb270374a2b1055fc72da40e1f237809cd6bb0e97e66d264cd138473a6" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base32" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa" + +[[package]] +name = "base64-compat" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a8d4d2746f89841e49230dd26917df1876050f95abafafbe34f47cb534b88d7" +dependencies = [ + "byteorder", +] + +[[package]] +name = "bech32" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9ff0bbfd639f15c74af777d81383cf53efb7c93613f6cab67c6c11e05bbf8b" + +[[package]] +name = "bitcoin" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a41df6ad9642c5c15ae312dd3d074de38fd3eb7cc87ad4ce10f90292a83fe4d" +dependencies = [ + "bech32", + "bitcoin_hashes", + "secp256k1", + "serde", +] + +[[package]] +name = "bitcoin_hashes" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "006cc91e1a1d99819bc5b8214be3555c1f0611b169f527a1fdc54ed1f2b745b0" +dependencies = [ + "serde", +] + +[[package]] +name = "bitcoincore-rpc" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b8d99d58466295cb2bf72c6959b784d59f8f0d6977458d2ba3eb75c834f36c3" +dependencies = [ + "bitcoincore-rpc-json", + "jsonrpc", + "log", + "serde", + "serde_json", +] + +[[package]] +name = "bitcoincore-rpc-json" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce91de73c61f5776cf938bfa88378c5b404a70e3369b761dacbe6024fea79dd" +dependencies = [ + "bitcoin", + "serde", + "serde_json", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "bstr" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "btc-wire" +version = "0.1.0" +dependencies = [ + "argh", + "bech32", + "bitcoincore-rpc", + "criterion", + "fastrand", + "owo-colors", + "rand", + "rustyline", + "serde", + "thiserror", +] + +[[package]] +name = "bumpalo" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "cast" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "cc" +version = "1.0.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + +[[package]] +name = "clipboard-win" +version = "4.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db8340083d28acb43451166543b98c838299b7e0863621be53a338adceea0ed" +dependencies = [ + "error-code", + "str-buf", + "winapi", +] + +[[package]] +name = "crc32fast" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3825b1e8580894917dc4468cb634a1b4e9745fddc854edad72d9c04644c0319f" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "criterion" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if", + "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", + "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", + "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", + "lazy_static", +] + +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "darling" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + +[[package]] +name = "endian-type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" + +[[package]] +name = "error-code" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5115567ac25674e0043e472be13d14e537f37ea8aa4bdc4aef0c89add1db1ff" +dependencies = [ + "libc", + "str-buf", +] + +[[package]] +name = "fastrand" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" +dependencies = [ + "instant", +] + +[[package]] +name = "fd-lock" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfc110fe50727d46a428eed832df40affe9bf74d077cac1bf3f2718e823f14c5" +dependencies = [ + "cfg-if", + "libc", + "windows-sys", +] + +[[package]] +name = "flate2" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" +dependencies = [ + "cfg-if", + "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 = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445" + +[[package]] +name = "futures-task" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dabf1872aaab32c886832f2276d2f5399887e2bd613698a02359e4ea83f8de12" + +[[package]] +name = "futures-util" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "pin-utils", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ff4f84919677303da5f147645dbea6b1881f368d03ac84e1dc09031ebd7b2c6" +dependencies = [ + "bytes", + "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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4a1e36c821dbe04574f602848a19f742f4fb3c98d40449f11bcad18d6b17421" + +[[package]] +name = "hyper" +version = "0.14.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436ec0091e4f20e655156a30a0df3770fe2900aa301e548e08446ec794b6953c" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[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 = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "itertools" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" +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" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad24d69a8a0698db8ffb9048e937e8ae3ee3bc45772a5d7b6979b1d2d5b6a9f7" +dependencies = [ + "base64-compat", + "serde", + "serde_derive", + "serde_json", +] + +[[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.108" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" + +[[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", +] + +[[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 = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[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 = "nibble_vec" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" +dependencies = [ + "smallvec", +] + +[[package]] +name = "nix" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3bb9a13fa32bc5aeb64150cd3f32d6cf4c748f8f8a417cce5d2eb976a8370ba" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "memoffset", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[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 = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "owo-colors" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9ad6d222cdc2351ccabb7af4f68bfaecd601b33c5f10d410ec89d2a273f6fff" + +[[package]] +name = "parking_lot" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216" +dependencies = [ + "cfg-if", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[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 = "plotters" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" + +[[package]] +name = "plotters-svg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed0cfbc8191465bed66e1718596ee0b0b35d5ee1f41c5df2189d0fe8bde535ba" + +[[package]] +name = "proc-macro2" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radix_trie" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" +dependencies = [ + "endian-type", + "nibble_vec", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[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", +] + +[[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.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "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 = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "redox_users" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +dependencies = [ + "getrandom", + "redox_syscall", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "rustversion" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61b3909d758bb75c79f23d4736fac9433868679d3ad2ea7a61e3c25cfda9a088" + +[[package]] +name = "rustyline" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "790487c3881a63489ae77126f57048b42d62d3b2bafbf37453ea19eedb6340d6" +dependencies = [ + "bitflags", + "cfg-if", + "clipboard-win", + "dirs-next", + "fd-lock", + "libc", + "log", + "memchr", + "nix", + "radix_trie", + "scopeguard", + "smallvec", + "unicode-segmentation", + "unicode-width", + "utf8parse", + "winapi", +] + +[[package]] +name = "ryu" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568" + +[[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 = "secp256k1" +version = "0.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d03ceae636d0fed5bae6a7f4f664354c5f4fcedf6eef053fef17e49f837d0a" +dependencies = [ + "secp256k1-sys", + "serde", +] + +[[package]] +name = "secp256k1-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "827cb7cce42533829c792fc51b82fbf18b125b45a702ef2c8be77fce65463a7b" +dependencies = [ + "cc", +] + +[[package]] +name = "semver" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" + +[[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_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "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.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0ffa0837f2dfa6fb90868c2b5468cad482e175f7dad97e7421951e663f2b527" +dependencies = [ + "itoa", + "ryu", + "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_with" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad6056b4cb69b6e43e3a0f055def223380baecc99da683884f205bf347f7c4b3" +dependencies = [ + "rustversion", + "serde", + "serde_with_macros", +] + +[[package]] +name = "serde_with_macros" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12e47be9471c72889ebafb5e14d5ff930d89ae7a67bbdb5f8abb564f845a927e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn", +] + +[[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 = "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 = "str-buf" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.82" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[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" +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 = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[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 = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "once_cell", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "tokio-macros", + "winapi", +] + +[[package]] +name = "tokio-macros" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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", + "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 = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[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.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + +[[package]] +name = "uri-pack" +version = "0.1.0" +dependencies = [ + "csv", + "idna", + "percent-encoding", + "serde_json", + "thiserror", + "url", +] + +[[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", + "serde", +] + +[[package]] +name = "utf8parse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" + +[[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 = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if", + "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", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "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", + "quote", + "syn", + "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 = "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 = "windows-sys" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82ca39602d5cbfa692c4b67e3bcbb2751477355141c1ed434c94da4186836ff6" +dependencies = [ + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_msvc" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52695a41e536859d5308cc613b4a022261a274390b25bd29dfff4bf08505f3c2" + +[[package]] +name = "windows_i686_gnu" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f54725ac23affef038fecb177de6c9bf065787c2f432f79e3c373da92f3e1d8a" + +[[package]] +name = "windows_i686_msvc" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d5158a43cc43623c0729d1ad6647e62fa384a3d135fd15108d37c683461f64" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc31f409f565611535130cfe7ee8e6655d3fa99c1c61013981e491921b5ce954" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f2b8c7cbd3bfdddd9ab98769f9746a7fad1bca236554cd032b78d768bc0e89f" + +[[package]] +name = "wire-gateway" +version = "0.1.0" +dependencies = [ + "async-compression", + "base32", + "btc-wire", + "hyper", + "rand", + "serde", + "serde_json", + "serde_urlencoded", + "serde_with", + "thiserror", + "tokio", + "url", +] diff --git a/btc-wire/Cargo.toml b/btc-wire/Cargo.toml @@ -20,6 +20,8 @@ fastrand = "1.5.0" serde = { version = "1.0.130", features = ["derive"] } # Zero allocation terminal color owo-colors = "3.1.0" +# Error macros +thiserror = "1.0.30" [dev-dependencies] # statistics-driven micro-benchmarks diff --git a/btc-wire/benches/metadata.rs b/btc-wire/benches/metadata.rs @@ -1,6 +1,6 @@ use btc_wire::{ - decode_segwit_msg, encode_segwit_key, - utils::{rand_addresses, rand_key}, + segwit::{decode_segwit_msg, encode_segwit_key, rand_addresses}, + test::rand_key, }; use criterion::{criterion_group, criterion_main, Criterion}; diff --git a/btc-wire/src/bin/btc-wire-cli.rs b/btc-wire/src/bin/btc-wire-cli.rs @@ -3,8 +3,8 @@ use bitcoincore_rpc::{ Client, RpcApi, }; use btc_wire::{ - rpc::{common_rpc, dirty_guess_network, wallet_rpc, Network}, - utils::rand_key, + rpc_utils::{common_rpc, dirty_guess_network, wallet_rpc, Network}, + test::rand_key, ClientExtended, }; @@ -48,7 +48,7 @@ struct TransferCmd { #[argh(subcommand, name = "nblock")] /// Wait or mine the next block struct NextBlockCmd { - #[argh(option, short = 't', default = "String::from(\"reserve\")")] + #[argh(option, short = 't', default = "String::from(\"wire\")")] /// receiver wallet to: String, } @@ -114,7 +114,7 @@ fn main() { match args.cmd { Cmd::Init(_) => app.init(), Cmd::Transfer(TransferCmd { - key, + key: _key, from, to, amount, diff --git a/btc-wire/src/bin/test.rs b/btc-wire/src/bin/test.rs @@ -4,14 +4,19 @@ use std::{collections::HashSet, iter::repeat_with, panic::AssertUnwindSafe}; use bitcoincore_rpc::{ bitcoin::{Address, Amount, Txid}, json::GetTransactionResultDetailCategory as Category, - jsonrpc::serde_json::{json, Value}, + jsonrpc::{ + error::RpcError, + serde_json::{json, Value}, + }, Client, RpcApi, }; use btc_wire::{ - refund, - rpc::{common_rpc, dirty_guess_network, network_dir_path, wallet_rpc, Network, CLIENT, WIRE}, - utils::rand_key, - ClientExtended, + rpc_patch::RpcErrorCode, + rpc_utils::{ + common_rpc, dirty_guess_network, network_dir_path, wallet_rpc, Network, CLIENT, WIRE, + }, + test::rand_key, + BounceErr, ClientExtended, }; use owo_colors::OwoColorize; @@ -215,16 +220,16 @@ pub fn main() { assert_eq!(key, extracted, "Corrupted metadata"); }); - let refund_fee = Amount::from_sat(300); - runner.test("Refund simple", || { + let bounce_fee = Amount::from_sat(300); + runner.test("Bounce simple", || { let before = client_rpc.get_balance(None, None).unwrap(); let send_id = client_rpc .send_to_address(&wire_addr, test_amount, None, None, None, None, None, None) .unwrap(); wait_for_tx(&client_rpc, &[send_id]); - let refund_id = refund(&wire_rpc, &send_id, refund_fee).unwrap(); - wait_for_tx(&wire_rpc, &[refund_id]); - let refund_tx_fee = wire_rpc.get_transaction(&refund_id, None).unwrap().details[0] + let bounce_id = wire_rpc.bounce(&send_id, bounce_fee).unwrap(); + wait_for_tx(&wire_rpc, &[bounce_id]); + let bounce_tx_fee = wire_rpc.get_transaction(&bounce_id, None).unwrap().details[0] .fee .unwrap() .abs() @@ -237,9 +242,10 @@ pub fn main() { .to_unsigned() .unwrap(); let after = client_rpc.get_balance(None, None).unwrap(); - assert_eq!(before - after, refund_tx_fee + refund_fee + send_tx_fee); + assert!(before >= after); + assert_eq!(before - after, bounce_tx_fee + bounce_fee + send_tx_fee); }); - runner.test("Refund minimal amount", || { + runner.test("Bounce minimal amount", || { let send_id = client_rpc .send_to_address( &wire_addr, @@ -253,13 +259,19 @@ pub fn main() { ) .unwrap(); wait_for_tx(&client_rpc, &[send_id]); - assert!(refund(&wire_rpc, &send_id, refund_fee).is_err()); + assert!(match wire_rpc.bounce(&send_id, bounce_fee) { + Ok(_) => false, + Err(err) => match err { + BounceErr::AmountLessThanFee => true, + _ => false, + }, + }); }); - runner.test("Refund too small amount", || { + runner.test("Bounce too small amount", || { let send_id = client_rpc .send_to_address( &wire_addr, - Amount::from_sat(294) + refund_fee, + Amount::from_sat(294) + bounce_fee, None, None, None, @@ -269,9 +281,24 @@ pub fn main() { ) .unwrap(); wait_for_tx(&client_rpc, &[send_id]); - assert!(refund(&wire_rpc, &send_id, refund_fee).is_err()); + + assert!(match wire_rpc.bounce(&send_id, bounce_fee) { + Ok(_) => false, + Err(err) => match err { + BounceErr::RPC(err) => match err { + bitcoincore_rpc::Error::JsonRpc(err) => match err { + bitcoincore_rpc::jsonrpc::Error::Rpc(RpcError { code, .. }) => { + code == RpcErrorCode::RpcWalletInsufficientFunds as i32 + } + _ => false, + }, + _ => false, + }, + _ => false, + }, + }); }); - runner.test("Refund complex", || { + runner.test("Bounce complex", || { // Generate 6 new addresses let addresses: Vec<Address> = repeat_with(|| client_rpc.get_new_address(None, None).unwrap()) @@ -332,10 +359,10 @@ pub fn main() { .unwrap(); let send_id = client_rpc.send_raw_transaction(&signed.hex).unwrap(); wait_for_tx(&client_rpc, &[send_id]); - let refund_id = refund(&wire_rpc, &send_id, refund_fee).unwrap(); - wait_for_tx(&wire_rpc, &[refund_id]); + let bounce_id = wire_rpc.bounce(&send_id, bounce_fee).unwrap(); + wait_for_tx(&wire_rpc, &[bounce_id]); let after = client_rpc.get_balance(None, None).unwrap(); - let refund_tx_fee = wire_rpc.get_transaction(&refund_id, None).unwrap().details[0] + let bounce_tx_fee = wire_rpc.get_transaction(&bounce_id, None).unwrap().details[0] .fee .unwrap() .abs() @@ -347,7 +374,8 @@ pub fn main() { .abs() .to_unsigned() .unwrap(); - assert_eq!(before - after, refund_tx_fee + refund_fee + send_tx_fee); + assert!(before >= after); + assert_eq!(before - after, bounce_tx_fee + bounce_fee + send_tx_fee); }); runner.conclude(); diff --git a/btc-wire/src/lib.rs b/btc-wire/src/lib.rs @@ -1,4 +1,4 @@ -use bech32::{u5, FromBase32, ToBase32, Variant}; +pub use bitcoincore_rpc; use bitcoincore_rpc::{ bitcoin::{ hashes::hex::{FromHex, ToHex}, @@ -8,217 +8,43 @@ use bitcoincore_rpc::{ jsonrpc::serde_json::{json, Value}, Client, RpcApi, }; -use rand::{rngs::OsRng, RngCore}; use rpc_patch::{ClientPatched, GetTransactionFull}; - -use crate::{ - rpc_patch::{rpc_error, RpcErrorCode}, - utils::rand_key, -}; +use rpc_utils::{segwit_min_amount, send_many, sender_address}; +use segwit::{decode_segwit_msg, encode_segwit_key}; pub mod rpc_patch; -pub use bitcoincore_rpc; - -/// Minimum dust amount to perform a transaction to a segwit address -fn segwit_min_amount() -> Amount { - // https://github.com/bitcoin/bitcoin/blob/master/src/policy/policy.cpp - return Amount::from_sat(294); -} - -/// Encode metadata into a segwit address -fn encode_segwit_addr(hrp: &str, metada: &[u8; 20]) -> String { - // We use the version 0 with bech32 encoding - let mut buf = vec![u5::try_from_u8(0).unwrap()]; - buf.extend_from_slice(&metada.to_base32()); - bech32::encode(hrp, buf, Variant::Bech32).unwrap() -} - -/// Encode half of a 32B key into a segwit address -fn encode_segwit_key_half( - hrp: &str, - is_first: bool, - magic_id: &[u8; 4], - key_half: &[u8; 16], -) -> String { - // Combine magic_it and the key half - let mut buf = [0u8; 20]; - buf[..4].copy_from_slice(magic_id); - buf[4..].copy_from_slice(key_half); - // Toggle first bit for ordering - if is_first { - buf[0] &= 0b0111_1111 // Unset first bit - } else { - buf[0] |= 0b1000_0000 // Set first bit - } - // Encode into an fake segwit address - encode_segwit_addr(hrp, &buf) -} - -/// Encode a 32B key into two segwit adresses -pub fn encode_segwit_key(hrp: &str, msg: &[u8; 32]) -> [String; 2] { - // Generate a random magic identifier - let mut magic_id = [0; 4]; - OsRng.fill_bytes(&mut magic_id); - // Split key in half; - let split: (&[u8; 16], &[u8; 16]) = - (msg[..16].try_into().unwrap(), msg[16..].try_into().unwrap()); - [ - encode_segwit_key_half(hrp, true, &magic_id, &split.0), - encode_segwit_key_half(hrp, false, &magic_id, &split.1), - ] +pub mod rpc_utils; +pub mod segwit; +pub mod test; + +#[derive(Debug, thiserror::Error)] +pub enum BounceErr { + #[error("Expected 'receive' transaction")] + NotAReceiveTransaction, + #[error("Transaction amount less than bounce fee")] + AmountLessThanFee, + #[error(transparent)] + RPC(#[from] bitcoincore_rpc::Error), } -#[derive(Debug, Clone)] -pub enum DecodeError { - TooManyAddress, - MissingSeqWitAddress, - MagicIdCollision, +#[derive(Debug, thiserror::Error)] +pub enum GetSegwitErr { + #[error(transparent)] + Decode(#[from] segwit::DecodeSegWitErr), + #[error(transparent)] + RPC(#[from] bitcoincore_rpc::Error), } -/// Decode a 32B key into from adresses -pub fn decode_segwit_msg(segwit_addrs: &[impl AsRef<str>]) -> Result<[u8; 32], DecodeError> { - if segwit_addrs.len() < 2 { - return Err(DecodeError::MissingSeqWitAddress); - } - - if segwit_addrs.len() > 4 { - return Err(DecodeError::TooManyAddress); - } - - // Extract parts from every addresses - let decoded: Vec<(bool, [u8; 4], [u8; 16])> = segwit_addrs - .into_iter() - .filter_map(|addr| { - bech32::decode(addr.as_ref()).ok().and_then(|(_, wp, _)| { - // Skip version - let pg: Vec<u8> = Vec::from_base32(&wp[1..]).unwrap(); - if pg.len() == 20 { - let mut magic_id: [u8; 4] = pg[..4].try_into().unwrap(); - let key_half: [u8; 16] = pg[4..].try_into().unwrap(); - let is_first = !pg[0] & 0b1000_0000 == 0; - // Clear first bit - magic_id[0] &= 0b0111_1111; - Some((is_first, magic_id, key_half)) - } else { - None - } - }) - }) - .collect(); - - if decoded.len() < 2 { - return Err(DecodeError::MissingSeqWitAddress); - } - // Keep only the addresses with duplicated magic id - let matches: Vec<&(bool, [u8; 4], [u8; 16])> = decoded - .iter() - .filter(|(_, magic, _)| { - decoded - .iter() - .filter(|(_, other, _)| other == magic) - .count() - > 1 - }) - .collect(); - - if matches.len() != 2 { - println!("Magic id collision"); - return Ok(rand_key()); - } - assert_eq!(matches.len(), 2, "Magic ID collision"); - - let mut key = [0; 32]; - for (is_first, _, half) in matches { - key[*is_first as usize * 16..][..16].copy_from_slice(half); - } - Ok(key) -} - -/// Send transaction to multiple recipients -fn send_many( - client: &Client, - recipients: Vec<(String, Amount)>, - fee_from: Vec<String>, -) -> bitcoincore_rpc::Result<Txid> { - let amounts = Value::Object( - recipients - .into_iter() - .map(|(addr, amount)| (addr, amount.as_btc().into())) - .collect(), - ); - client.call( - "sendmany", - &[ - "".into(), // dummy - amounts, // amounts - 0.into(), // minconf - "".into(), // comment - fee_from.into(), // substractfeefrom - false.into(), // replaceable - Value::Null, // conf_target - Value::Null, // estimate mode - 1.into(), // fee rate - false.into(), // verbose - ], - ) -} - -/// Get the first sender address from a raw transaction -pub fn sender_address(rpc: &Client, full: &GetTransactionFull) -> bitcoincore_rpc::Result<Address> { - let first = &full.decoded.vin[0]; - let tx = rpc.get_raw_tx(&first.txid.unwrap())?; - Ok(tx - .vout - .into_iter() - .find(|it| it.n == first.vout.unwrap()) - .unwrap() - .script_pub_key - .address - .unwrap()) -} - -/// Refund a transaction -/// -/// There is no reliable way to refund a transaction as you cannot know if the addresses used are shared -/// -/// This is not a best-effort solution but a reasonable one -pub fn refund(rpc: &Client, id: &Txid, refund_fee: Amount) -> bitcoincore_rpc::Result<Txid> { - // TODO handle insufficient_funds error - let full = rpc.get_transaction_full(id)?; - let detail = &full.tx.details[0]; - assert_eq!(detail.category, Category::Receive); - - let amount = detail.amount.to_unsigned().unwrap(); - if amount <= refund_fee { - return Err(rpc_error( - RpcErrorCode::RpcWalletInsufficientFunds, - "The refund amount is inferior to the refund fee".to_string(), - )); - } - - // List all addresses - let mut addresses = Vec::new(); - for vin in full.decoded.vin { - if let Some(id) = &vin.txid { - let tx = rpc.get_raw_tx(id)?; - let it = tx.vout.iter().find(|it| it.n == vin.vout.unwrap()).unwrap(); - addresses.push(it.script_pub_key.address.as_ref().unwrap().to_string()); - } - } - - // Split received amount (minus refund fee) equally to each addresses - let split = (amount - refund_fee) / addresses.len() as u64; - let recipients: Vec<(String, Amount)> = - addresses.iter().map(|addr| (addr.clone(), split)).collect(); - // Send refund making recipient pay the transaction fees - let id = send_many(rpc, recipients, addresses)?; - Ok(id) +#[derive(Debug, thiserror::Error)] +pub enum GetOpReturnErr { + #[error("Missing opreturn")] + MissingOpReturn, + #[error(transparent)] + RPC(#[from] bitcoincore_rpc::Error), } /// An extended bitcoincore JSON-RPC api client who can send and retrieve metadata with their transaction pub trait ClientExtended { - // TODO error handling for get functions - /// Send a transaction with a 32B key as metadata encoded using fake segwit addresses fn send_segwit_key( &self, @@ -228,10 +54,7 @@ pub trait ClientExtended { ) -> bitcoincore_rpc::Result<Txid>; /// Get detailed information about an in-wallet transaction and it's 32B metadata key encoded using fake segwit addresses - fn get_tx_segwit_key( - &self, - id: &Txid, - ) -> bitcoincore_rpc::Result<(GetTransactionFull, [u8; 32])>; + fn get_tx_segwit_key(&self, id: &Txid) -> Result<(GetTransactionFull, [u8; 32]), GetSegwitErr>; /// Send a transaction with metadata encoded using OP_RETURN fn send_op_return( @@ -242,8 +65,14 @@ pub trait ClientExtended { ) -> bitcoincore_rpc::Result<Txid>; /// Get detailed information about an in-wallet transaction and its op_return metadata - fn get_tx_op_return(&self, id: &Txid) - -> bitcoincore_rpc::Result<(GetTransactionFull, Vec<u8>)>; + fn get_tx_op_return(&self, id: &Txid) -> Result<(GetTransactionFull, Vec<u8>), GetOpReturnErr>; + + /// Bounce a transaction bask to its sender + /// + /// There is no reliable way to bounce a transaction as you cannot know if the addresses + /// used are shared or come from a third-party service. We only send back to the first input + /// address as a best-effort gesture. + fn bounce(&self, id: &Txid, bounce_fee: Amount) -> Result<Txid, BounceErr>; } impl ClientExtended for Client { @@ -268,10 +97,7 @@ impl ClientExtended for Client { send_many(self, recipients, Vec::new()) } - fn get_tx_segwit_key( - &self, - id: &Txid, - ) -> bitcoincore_rpc::Result<(GetTransactionFull, [u8; 32])> { + fn get_tx_segwit_key(&self, id: &Txid) -> Result<(GetTransactionFull, [u8; 32]), GetSegwitErr> { let full = self.get_transaction_full(id)?; let addresses: Vec<String> = full @@ -286,8 +112,7 @@ impl ClientExtended for Client { }) .collect(); - // TODO error handling - let metadata = decode_segwit_msg(&addresses).unwrap(); + let metadata = decode_segwit_msg(&addresses)?; Ok((full, metadata)) } @@ -320,10 +145,7 @@ impl ClientExtended for Client { self.send_raw_transaction(&signed.hex) } - fn get_tx_op_return( - &self, - id: &Txid, - ) -> bitcoincore_rpc::Result<(GetTransactionFull, Vec<u8>)> { + fn get_tx_op_return(&self, id: &Txid) -> Result<(GetTransactionFull, Vec<u8>), GetOpReturnErr> { let full = self.get_transaction_full(id)?; let op_return_out = full @@ -331,201 +153,35 @@ impl ClientExtended for Client { .vout .iter() .find(|it| it.script_pub_key.type_ == ScriptPubkeyType::NullData) - // TODO error handling - .unwrap(); + .ok_or(GetOpReturnErr::MissingOpReturn)?; + let hex = op_return_out.script_pub_key.asm.split_once(' ').unwrap().1; + // Op return payload is always encoded in hexadecimal let metadata = Vec::from_hex(hex).unwrap(); Ok((full, metadata)) } -} - -pub mod utils { - use crate::{encode_segwit_addr, encode_segwit_key}; - pub fn rand_key() -> [u8; 32] { - let mut key = [0; 32]; - key.fill_with(|| fastrand::u8(..)); - key - } - - pub fn rand_data() -> [u8; 20] { - let mut key = [0; 20]; - key.fill_with(|| fastrand::u8(..)); - key - } - - pub fn rand_addresses(hrp: &str, key: &[u8; 32]) -> Vec<String> { - let mut rng_address: Vec<String> = - std::iter::repeat_with(|| encode_segwit_addr(hrp, &rand_data())) - .take(2) - .collect(); - - let mut addresses = encode_segwit_key(hrp, &key).to_vec(); - addresses.append(&mut rng_address); - fastrand::shuffle(&mut addresses); - addresses - } -} - -pub mod rpc { - use std::{path::PathBuf, str::FromStr}; - - use bitcoincore_rpc::{ - bitcoin::{BlockHash, Txid}, - json::{GetTransactionResultDetailCategory, ListTransactionResult}, - Auth, Client, RpcApi, - }; - - pub const CLIENT: &str = "client"; - pub const WIRE: &str = "wire"; - - /// Bitcoin networks - #[derive(Debug, Clone, Copy, PartialEq, Eq)] - pub enum Network { - MainNet, - TestNet, - RegTest, - } - - impl Network { - pub fn dir(&self) -> &'static str { - match self { - Network::MainNet => "main", - Network::TestNet => "testnet3", - Network::RegTest => "regtest", - } - } - } - - pub fn rpc_url(network: Network) -> String { - let port = match network { - Network::MainNet => 8332, - Network::TestNet => 18332, - Network::RegTest => 18443, - }; - format!("https://127.0.0.1:{}", port) - } - - pub fn data_dir_path() -> PathBuf { - // https://github.com/bitcoin/bitcoin/blob/master/doc/bitcoin-conf.md#:~:text=By%20default%2C%20the%20configuration%20file,file%3E%20option%20in%20the%20bitcoin. - if cfg!(target_os = "windows") { - PathBuf::from_str(&std::env::var("APPDATA").unwrap()) - .unwrap() - .join("Bitcoin") - } else if cfg!(target_os = "linux") { - PathBuf::from_str(&std::env::var("HOME").unwrap()) - .unwrap() - .join(".bitcoin") - } else if cfg!(target_os = "macos") { - PathBuf::from_str(&std::env::var("HOME").unwrap()) - .unwrap() - .join("Library/Application Support/Bitcoin") - } else { - unimplemented!("Only windows, linux or macos") - } - } - - pub fn network_dir_path(network: Network) -> PathBuf { - data_dir_path().join(network.dir()) - } - - pub fn common_rpc(network: Network) -> bitcoincore_rpc::Result<Client> { - Client::new( - &rpc_url(network), - Auth::CookieFile(network_dir_path(network).join(".cookie")), - ) - } - - pub fn wallet_rpc(network: Network, wallet: &str) -> Client { - Client::new( - &format!("{}/wallet/{}", rpc_url(network), wallet), - Auth::CookieFile(network_dir_path(network).join(".cookie")), - ) - .expect(&format!("Failed to open wallet '{}' client", wallet)) - } - - pub fn last_transaction(rpc: &Client) -> bitcoincore_rpc::Result<Txid> { - Ok(rpc - .list_transactions(None, None, None, None)? - .last() - .unwrap() - .info - .txid) - } - - pub fn received_since( - rpc: &Client, - hash: Option<&BlockHash>, - ) -> bitcoincore_rpc::Result<(Vec<Txid>, BlockHash)> { - let result = rpc.list_since_block(hash, Some(1), None, None)?; - let mut received: Vec<&ListTransactionResult> = result - .transactions - .iter() - .filter(|it| { - it.info.confirmations > 0 - && it.detail.category == GetTransactionResultDetailCategory::Receive - }) - .collect(); - received.sort_unstable_by_key(|it| it.info.time); - received.reverse(); - Ok(( - received.into_iter().map(|it| it.info.txid).collect(), - result.lastblock, - )) - } - - pub fn dirty_guess_network() -> Network { - let result_reg = common_rpc(Network::RegTest).and_then(|rpc| rpc.get_network_info()); - if result_reg.is_ok() { - return Network::RegTest; - } - let result_test = common_rpc(Network::TestNet).and_then(|rpc| rpc.get_network_info()); - if result_test.is_ok() { - return Network::TestNet; + fn bounce(&self, id: &Txid, bounce_fee: Amount) -> Result<Txid, BounceErr> { + let full = self.get_transaction_full(id)?; + let detail = &full.tx.details[0]; + if detail.category != Category::Receive { + return Err(BounceErr::NotAReceiveTransaction); } - let result_main = common_rpc(Network::MainNet).and_then(|rpc| rpc.get_network_info()); - if result_main.is_ok() { - return Network::MainNet; + let amount = detail.amount.to_unsigned().unwrap(); + if amount <= bounce_fee { + return Err(BounceErr::AmountLessThanFee); } - unreachable!( - "Failed to connect to any chain\nreg: {:?}\ntest: {:?}\nmain: {:?}", - result_reg, result_main, result_test - ); - } -} - -#[cfg(test)] -mod test { - use crate::{ - decode_segwit_msg, encode_segwit_key, - utils::{rand_addresses, rand_key}, - }; - - #[test] - fn test_shuffle() { - for _ in 0..1000 { - let key = rand_key(); - let mut addresses = encode_segwit_key("test", &key); - fastrand::shuffle(&mut addresses); - let decoded = - decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>()) - .unwrap(); - assert_eq!(key, decoded); - } - } - - #[test] - fn test_shuffle_many() { - for _ in 0..1000 { - let key = rand_key(); - let addresses = rand_addresses("test", &key); - let decoded = - decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>()) - .unwrap(); - assert_eq!(key, decoded); - } + let sender = sender_address(self, &full)?; + let bounce_amount = amount - bounce_fee; + // Send refund making recipient pay the transaction fees + let id = send_many( + self, + vec![(sender.to_string(), bounce_amount)], + vec![sender.to_string()], + )?; + Ok(id) } } diff --git a/btc-wire/src/main.rs b/btc-wire/src/main.rs @@ -2,7 +2,7 @@ use std::{collections::HashSet, str::FromStr, time::Duration}; use bitcoincore_rpc::{bitcoin::Amount, RpcApi}; use btc_wire::{ - rpc::{ + rpc_utils::{ common_rpc, dirty_guess_network, network_dir_path, received_since, wallet_rpc, CLIENT, WIRE, }, ClientExtended, diff --git a/btc-wire/src/rpc_utils.rs b/btc-wire/src/rpc_utils.rs @@ -0,0 +1,177 @@ +use std::{path::PathBuf, str::FromStr}; + +use bitcoincore_rpc::{ + bitcoin::{BlockHash, Txid, Amount, Address}, + json::{GetTransactionResultDetailCategory, ListTransactionResult}, + Auth, Client, RpcApi, jsonrpc::serde_json::Value, +}; + +use crate::rpc_patch::{GetTransactionFull, ClientPatched}; + +pub const CLIENT: &str = "client"; +pub const WIRE: &str = "wire"; + +/// Bitcoin networks +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Network { + MainNet, + TestNet, + RegTest, +} + +impl Network { + pub fn dir(&self) -> &'static str { + match self { + Network::MainNet => "main", + Network::TestNet => "testnet3", + Network::RegTest => "regtest", + } + } +} + +pub fn rpc_url(network: Network) -> String { + let port = match network { + Network::MainNet => 8332, + Network::TestNet => 18332, + Network::RegTest => 18443, + }; + format!("https://127.0.0.1:{}", port) +} + +pub fn data_dir_path() -> PathBuf { + // https://github.com/bitcoin/bitcoin/blob/master/doc/bitcoin-conf.md#:~:text=By%20default%2C%20the%20configuration%20file,file%3E%20option%20in%20the%20bitcoin. + if cfg!(target_os = "windows") { + PathBuf::from_str(&std::env::var("APPDATA").unwrap()) + .unwrap() + .join("Bitcoin") + } else if cfg!(target_os = "linux") { + PathBuf::from_str(&std::env::var("HOME").unwrap()) + .unwrap() + .join(".bitcoin") + } else if cfg!(target_os = "macos") { + PathBuf::from_str(&std::env::var("HOME").unwrap()) + .unwrap() + .join("Library/Application Support/Bitcoin") + } else { + unimplemented!("Only windows, linux or macos") + } +} + +pub fn network_dir_path(network: Network) -> PathBuf { + data_dir_path().join(network.dir()) +} + +pub fn common_rpc(network: Network) -> bitcoincore_rpc::Result<Client> { + Client::new( + &rpc_url(network), + Auth::CookieFile(network_dir_path(network).join(".cookie")), + ) +} + +pub fn wallet_rpc(network: Network, wallet: &str) -> Client { + Client::new( + &format!("{}/wallet/{}", rpc_url(network), wallet), + Auth::CookieFile(network_dir_path(network).join(".cookie")), + ) + .expect(&format!("Failed to open wallet '{}' client", wallet)) +} + +pub fn last_transaction(rpc: &Client) -> bitcoincore_rpc::Result<Txid> { + Ok(rpc + .list_transactions(None, None, None, None)? + .last() + .unwrap() + .info + .txid) +} + +pub fn received_since( + rpc: &Client, + hash: Option<&BlockHash>, +) -> bitcoincore_rpc::Result<(Vec<Txid>, BlockHash)> { + let result = rpc.list_since_block(hash, Some(1), None, None)?; + let mut received: Vec<&ListTransactionResult> = result + .transactions + .iter() + .filter(|it| { + it.info.confirmations > 0 + && it.detail.category == GetTransactionResultDetailCategory::Receive + }) + .collect(); + received.sort_unstable_by_key(|it| it.info.time); + received.reverse(); + Ok(( + received.into_iter().map(|it| it.info.txid).collect(), + result.lastblock, + )) +} + +pub fn dirty_guess_network() -> Network { + let result_reg = common_rpc(Network::RegTest).and_then(|rpc| rpc.get_network_info()); + if result_reg.is_ok() { + return Network::RegTest; + } + let result_test = common_rpc(Network::TestNet).and_then(|rpc| rpc.get_network_info()); + if result_test.is_ok() { + return Network::TestNet; + } + + let result_main = common_rpc(Network::MainNet).and_then(|rpc| rpc.get_network_info()); + if result_main.is_ok() { + return Network::MainNet; + } + + unreachable!( + "Failed to connect to any chain\nreg: {:?}\ntest: {:?}\nmain: {:?}", + result_reg, result_main, result_test + ); +} + +/// Minimum dust amount to perform a transaction to a segwit address +pub fn segwit_min_amount() -> Amount { + // https://github.com/bitcoin/bitcoin/blob/master/src/policy/policy.cpp + return Amount::from_sat(294); +} + +/// Send transaction to multiple recipients +pub fn send_many( + client: &Client, + recipients: Vec<(String, Amount)>, + fee_from: Vec<String>, +) -> bitcoincore_rpc::Result<Txid> { + let amounts = Value::Object( + recipients + .into_iter() + .map(|(addr, amount)| (addr, amount.as_btc().into())) + .collect(), + ); + client.call( + "sendmany", + &[ + "".into(), // dummy + amounts, // amounts + 0.into(), // minconf + "".into(), // comment + fee_from.into(), // substractfeefrom + false.into(), // replaceable + Value::Null, // conf_target + Value::Null, // estimate mode + 1.into(), // fee rate + false.into(), // verbose + ], + ) +} + +/// Get the first sender address from a raw transaction +pub fn sender_address(rpc: &Client, full: &GetTransactionFull) -> bitcoincore_rpc::Result<Address> { + let first = &full.decoded.vin[0]; + let tx = rpc.get_raw_tx(&first.txid.unwrap())?; + Ok(tx + .vout + .into_iter() + .find(|it| it.n == first.vout.unwrap()) + .unwrap() + .script_pub_key + .address + .unwrap()) +} diff --git a/btc-wire/src/segwit.rs b/btc-wire/src/segwit.rs @@ -0,0 +1,149 @@ +use bech32::{u5, ToBase32, Variant, FromBase32}; +use rand::{rngs::OsRng, RngCore}; + +use crate::test::rand_data; + +/// Encode metadata into a segwit address +fn encode_segwit_addr(hrp: &str, metada: &[u8; 20]) -> String { + // We use the version 0 with bech32 encoding + let mut buf = vec![u5::try_from_u8(0).unwrap()]; + buf.extend_from_slice(&metada.to_base32()); + bech32::encode(hrp, buf, Variant::Bech32).unwrap() +} + +/// Encode half of a 32B key into a segwit address +fn encode_segwit_key_half( + hrp: &str, + is_first: bool, + magic_id: &[u8; 4], + key_half: &[u8; 16], +) -> String { + // Combine magic_it and the key half + let mut buf = [0u8; 20]; + buf[..4].copy_from_slice(magic_id); + buf[4..].copy_from_slice(key_half); + // Toggle first bit for ordering + if is_first { + buf[0] &= 0b0111_1111 // Unset first bit + } else { + buf[0] |= 0b1000_0000 // Set first bit + } + // Encode into an fake segwit address + encode_segwit_addr(hrp, &buf) +} + +/// Encode a 32B key into two segwit adresses +pub fn encode_segwit_key(hrp: &str, msg: &[u8; 32]) -> [String; 2] { + // Generate a random magic identifier + let mut magic_id = [0; 4]; + OsRng.fill_bytes(&mut magic_id); + // Split key in half; + let split: (&[u8; 16], &[u8; 16]) = + (msg[..16].try_into().unwrap(), msg[16..].try_into().unwrap()); + [ + encode_segwit_key_half(hrp, true, &magic_id, &split.0), + encode_segwit_key_half(hrp, false, &magic_id, &split.1), + ] +} + +#[derive(Debug, Clone, thiserror::Error)] +pub enum DecodeSegWitErr { + #[error("There is less than 2 segwit addresses")] + MissingSegWitAddress, + #[error("More than two addresses are sharing the same magic id")] + MagicIdCollision, +} + +/// Decode a 32B key into from adresses +pub fn decode_segwit_msg(segwit_addrs: &[impl AsRef<str>]) -> Result<[u8; 32], DecodeSegWitErr> { + // Extract parts from every addresses + let decoded: Vec<(bool, [u8; 4], [u8; 16])> = segwit_addrs + .into_iter() + .filter_map(|addr| { + bech32::decode(addr.as_ref()).ok().and_then(|(_, wp, _)| { + // Skip version + let pg: Vec<u8> = Vec::from_base32(&wp[1..]).unwrap(); + if pg.len() == 20 { + let mut magic_id: [u8; 4] = pg[..4].try_into().unwrap(); + let key_half: [u8; 16] = pg[4..].try_into().unwrap(); + let is_first = !pg[0] & 0b1000_0000 == 0; + // Clear first bit + magic_id[0] &= 0b0111_1111; + Some((is_first, magic_id, key_half)) + } else { + None + } + }) + }) + .collect(); + + if decoded.len() < 2 { + return Err(DecodeSegWitErr::MissingSegWitAddress); + } + // Keep only the addresses with duplicated magic id + let matches: Vec<&(bool, [u8; 4], [u8; 16])> = decoded + .iter() + .filter(|(_, magic, _)| { + decoded + .iter() + .filter(|(_, other, _)| other == magic) + .count() + > 1 + }) + .collect(); + + if matches.len() != 2 { + return Err(DecodeSegWitErr::MagicIdCollision); + } + + let mut key = [0; 32]; + for (is_first, _, half) in matches { + key[*is_first as usize * 16..][..16].copy_from_slice(half); + } + Ok(key) +} + +pub fn rand_addresses(hrp: &str, key: &[u8; 32]) -> Vec<String> { + let mut rng_address: Vec<String> = + std::iter::repeat_with(|| encode_segwit_addr(hrp, &rand_data())) + .take(2) + .collect(); + + let mut addresses = encode_segwit_key(hrp, &key).to_vec(); + addresses.append(&mut rng_address); + fastrand::shuffle(&mut addresses); + addresses +} + +#[cfg(test)] +mod test { + use crate::{ + segwit::{decode_segwit_msg, encode_segwit_key, rand_addresses}, + test::rand_key, + }; + + #[test] + fn test_shuffle() { + for _ in 0..1000 { + let key = rand_key(); + let mut addresses = encode_segwit_key("test", &key); + fastrand::shuffle(&mut addresses); + let decoded = + decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>()) + .unwrap(); + assert_eq!(key, decoded); + } + } + + #[test] + fn test_shuffle_many() { + for _ in 0..1000 { + let key = rand_key(); + let addresses = rand_addresses("test", &key); + let decoded = + decode_segwit_msg(&addresses.iter().map(|s| s.as_str()).collect::<Vec<&str>>()) + .unwrap(); + assert_eq!(key, decoded); + } + } +} diff --git a/btc-wire/src/test.rs b/btc-wire/src/test.rs @@ -0,0 +1,11 @@ +pub fn rand_key() -> [u8; 32] { + let mut key = [0; 32]; + key.fill_with(|| fastrand::u8(..)); + key +} + +pub fn rand_data() -> [u8; 20] { + let mut key = [0; 20]; + key.fill_with(|| fastrand::u8(..)); + key +} diff --git a/script/test_bank.sh b/script/test_bank.sh @@ -20,13 +20,13 @@ echo "OK" BANK_ENDPOINT=http://localhost:8080/ echo -n "Making wire transfer to exchange ..." -btc-wire-cli transfer 0.0004 +btc-wire-cli transfer 0.00004 btc-wire-cli nblock echo " OK" echo -n "Requesting exchange incoming transaction list ..." -taler-exchange-wire-gateway-client -b $BANK_ENDPOINT -i | grep BTC:0.0004 > /dev/null +taler-exchange-wire-gateway-client -b $BANK_ENDPOINT -i | grep BTC:0.00004 > /dev/null echo " OK" @@ -36,14 +36,14 @@ ADDRESS=`bitcoin-cli -rpcwallet=wire getnewaddress` taler-exchange-wire-gateway-client \ -b $BANK_ENDPOINT \ -C payto://bitcoin/$ADDRESS \ - -a BTC:0.0002 > /dev/null + -a BTC:0.00002 > /dev/null btc-wire-cli nblock echo " OK" echo -n "Requesting exchange's outgoing transaction list..." -taler-exchange-wire-gateway-client -b $BANK_ENDPOINT -o | grep BTC:0.0002 > /dev/null +taler-exchange-wire-gateway-client -b $BANK_ENDPOINT -o | grep BTC:0.00002 > /dev/null echo " OK" echo "All tests passed" diff --git a/wire-gateway/src/main.rs b/wire-gateway/src/main.rs @@ -1,366 +1,365 @@ -use std::{ - str::FromStr, - time::{Duration, SystemTime}, -}; - -use api_common::{Amount, SafeUint64, ShortHashCode, Timestamp}; -use api_wire::{OutgoingBankTransaction, OutgoingHistory}; -use async_compression::tokio::bufread::ZlibDecoder; -use btc_wire::{ - bitcoincore_rpc::{ - bitcoin::{Address, Amount as BtcAmount, BlockHash}, - json::GetTransactionResultDetailCategory as Category, - Client, RpcApi, - }, - rpc::{common_rpc, dirty_guess_network, wallet_rpc}, - sender_address, ClientExtended, -}; -use error_codes::ErrorCode; -use hyper::{ - header, - http::request::Parts, - service::{make_service_fn, service_fn}, - Body, Error, Method, Request, Response, Server, StatusCode, -}; -use tokio::{io::AsyncReadExt, sync::Mutex}; -use url::Url; - -use crate::{ - api_common::{Base32, ErrorDetail}, - api_wire::{ - AddIncomingRequest, AddIncomingResponse, HistoryParams, IncomingBankTransaction, - IncomingHistory, TransferRequest, TransferResponse, - }, -}; - -mod error_codes; - -fn bitcoin_payto(addr: &Address) -> Url { - Url::from_str(&format!("payto://bitcoin/{}", addr.to_string())).unwrap() -} - -impl Into<Amount> for BtcAmount { - fn into(self) -> Amount { - let sat = self.as_sat(); - return Amount::new("BTC", sat / 100_000_000, ((sat % 100_000_000) / 10) as u32); - } -} - -impl TryFrom<Amount> for BtcAmount { - type Error = String; - - fn try_from(value: Amount) -> Result<Self, Self::Error> { - if value.currency != "BTC" { - return Err("Wrong currency".to_string()); - } - - let sat = value.value * 100_000_000 + value.fraction as u64 * 10; - return Ok(Self::from_sat(sat)); - } -} - -#[tokio::main] -async fn main() { - let network = dirty_guess_network(); - { - let common = common_rpc(network).unwrap(); - common.load_wallet("wire").ok(); - } - let state = ServerState { - incoming: Mutex::new(Vec::new()), - outgoing: Mutex::new(Vec::new()), - client: Mutex::new(wallet_rpc(network, "wire")), - }; - let state: &'static ServerState = Box::leak(Box::new(state)); - - // BTC worker thread - - std::thread::spawn(move || { - let rpc = wallet_rpc(network, "wire"); - let mut last_hash: Option<BlockHash> = None; - let confirmation = 1; - - loop { - let txs = rpc - .list_since_block(last_hash.as_ref(), Some(confirmation), None, Some(true)) - .unwrap(); - let self_addr = rpc.get_new_address(None, None).unwrap(); - last_hash = Some(txs.lastblock); - - for tx in txs - .transactions - .into_iter() - .filter(|tx| tx.info.confirmations >= confirmation as i32) - { - let id = &tx.info.txid; - match tx.detail.category { - Category::Send => match rpc.get_tx_op_return(&id) { - Ok((full, wtid)) => { - let sender = sender_address(&rpc, &full).unwrap(); - let credit_account = bitcoin_payto(&sender); - let debit_account = bitcoin_payto(&self_addr); - let time = tx.info.blocktime.unwrap(); - let date = - Timestamp::from(SystemTime::UNIX_EPOCH + Duration::from_secs(time)); - let amount = (tx.detail.amount * -1).to_unsigned().unwrap().into(); - let mut lock = state.outgoing.blocking_lock(); - println!( - "Send {} {} {:?} {}", - &debit_account, &credit_account, &date, &amount - ); - let array: [u8; 32] = wtid[..32].try_into().unwrap(); - let wtid = Base32::from(array); - let row_id = lock.len() as u64 + 1; - lock.push(OutgoingTransaction { - row_id: SafeUint64::try_from(row_id).unwrap(), - date, - amount, - debit_account, - credit_account, - wtid, - }); - } - Err(err) => println!("receive: {}", err), - }, - Category::Receive => match rpc.get_tx_segwit_key(&id) { - Ok((full, reserve_pub)) => { - let sender = sender_address(&rpc, &full).unwrap(); - let debit_account = bitcoin_payto(&sender); - let credit_account = bitcoin_payto(tx.detail.address.as_ref().unwrap()); - let time = tx.info.blocktime.unwrap(); - let date = - Timestamp::from(SystemTime::UNIX_EPOCH + Duration::from_secs(time)); - let amount = tx.detail.amount.to_unsigned().unwrap().into(); - let mut lock = state.incoming.blocking_lock(); - println!( - "Receive {} {} {:?} {}", - &debit_account, &credit_account, &date, &amount - ); - let row_id = lock.len() as u64 + 1; - lock.push(IncomingTransaction { - row_id: SafeUint64::try_from(row_id).unwrap(), - date, - amount, - reserve_pub: reserve_pub.into(), - debit_account, - credit_account, - }); - } - Err(err) => println!("receive: {}", err), - }, - Category::Generate | Category::Immature | Category::Orphan => {} - } - } - - rpc.wait_for_new_block(0).ok(); - } - }); - - let addr = ([0, 0, 0, 0], 8080).into(); - let make_service = make_service_fn(move |_| async move { - Ok::<_, Error>(service_fn(move |req| async move { - let response = match router(req, state).await { - Ok(resp) => resp, - Err((status, err)) => { - json_response( - status, - &ErrorDetail { - code: err as i64, - hint: None, - detail: None, - parameter: None, - path: None, - offset: None, - index: None, - object: None, - currency: None, - type_expected: None, - type_actual: None, - }, - ) - .await - } - }; - Ok::<Response<Body>, Error>(response) - })) - }); - - let server = Server::bind(&addr).serve(make_service); - - println!("Server listening on http://{}", addr); - - if let Err(e) = server.await { - eprintln!("server error: {}", e); - } -} - -struct IncomingTransaction { - row_id: SafeUint64, - date: Timestamp, - amount: Amount, - reserve_pub: ShortHashCode, - debit_account: Url, - credit_account: Url, -} - -struct OutgoingTransaction { - row_id: SafeUint64, - date: Timestamp, - amount: Amount, - wtid: ShortHashCode, - debit_account: Url, - credit_account: Url, -} - -struct ServerState { - incoming: Mutex<Vec<IncomingTransaction>>, - outgoing: Mutex<Vec<OutgoingTransaction>>, - client: Mutex<Client>, -} - -pub mod api_common; -pub mod api_wire; - -async fn parse_json<J: serde::de::DeserializeOwned>(parts: &Parts, body: Body) -> J { - let bytes = hyper::body::to_bytes(body).await.unwrap(); - let mut buf = Vec::new(); - let decompressed = if parts - .headers - .get(header::CONTENT_ENCODING) - .map(|it| it == "deflate") - .unwrap_or(false) - { - let mut decoder = ZlibDecoder::new(bytes.as_ref()); - decoder.read_to_end(&mut buf).await.unwrap(); - &buf - } else { - bytes.as_ref() - }; - - serde_json::from_slice(&decompressed).unwrap() -} - -async fn json_response<J: serde::Serialize>(status: StatusCode, json: &J) -> Response<Body> { - let json = serde_json::to_vec(json).unwrap(); - /* let mut encoder = ZlibEncoder::new(Vec::new()); - encoder.write_all(&json).await.unwrap(); - encoder.shutdown().await.unwrap(); - - let encoded = encoder.into_inner();*/ - Response::builder() - .status(status) - .header(header::CONTENT_TYPE, "application/json") - //.header(header::CONTENT_ENCODING, "deflate") - .body(Body::from(json)) - .unwrap() -} - -type ServerErr = (StatusCode, ErrorCode); - -fn assert_method(parts: &Parts, method: Method) -> Result<(), ServerErr> { - if parts.method == method { - Ok(()) - } else { - Err(( - StatusCode::METHOD_NOT_ALLOWED, - ErrorCode::GENERIC_METHOD_INVALID, - )) - } -} - -async fn router( - req: Request<Body>, - state: &'static ServerState, -) -> Result<Response<Body>, (StatusCode, ErrorCode)> { - let (parts, body) = req.into_parts(); - let response = match parts.uri.path() { - "/transfer" => { - assert_method(&parts, Method::POST)?; - let request: TransferRequest = parse_json(&parts, body).await; - let client = state.client.lock().await; - let address = request.credit_account.path().trim_start_matches('/'); - dbg!(address); - let to = Address::from_str(address).unwrap(); - let amount: BtcAmount = request.amount.try_into().unwrap(); - client - .send_op_return(&to, amount, request.wtid.as_ref()) - .unwrap(); - let timestamp = Timestamp::now(); - json_response( - StatusCode::OK, - &TransferResponse { - timestamp, - row_id: SafeUint64::try_from(0).unwrap(), - }, - ) - .await - } - "/history/incoming" => { - assert_method(&parts, Method::GET)?; - let params: HistoryParams = - serde_urlencoded::from_str(parts.uri.query().unwrap_or("")).unwrap(); - let guard = state.incoming.lock().await; - let transactions: Vec<IncomingBankTransaction> = guard - .iter() - .map(|tx| IncomingBankTransaction::IncomingReserveTransaction { - row_id: tx.row_id, - date: tx.date, - amount: tx.amount.clone(), - credit_account: tx.credit_account.clone(), - debit_account: tx.debit_account.clone(), - reserve_pub: tx.reserve_pub.clone(), - }) - .collect(); - json_response( - StatusCode::OK, - &IncomingHistory { - incoming_transactions: transactions, - }, - ) - .await - } - "/history/outgoing" => { - assert_method(&parts, Method::GET)?; - let params: HistoryParams = - serde_urlencoded::from_str(parts.uri.query().unwrap_or("")).unwrap(); - let guard = state.outgoing.lock().await; - let transactions: Vec<OutgoingBankTransaction> = guard - .iter() - .map(|tx| OutgoingBankTransaction { - row_id: tx.row_id, - date: tx.date, - amount: tx.amount.clone(), - credit_account: tx.credit_account.clone(), - wtid: tx.wtid.clone(), - debit_account: tx.debit_account.clone(), - exchange_base_url: Url::parse("http://localhost:8080").unwrap(), - }) - .collect(); - json_response( - StatusCode::OK, - &OutgoingHistory { - outgoing_transactions: transactions, - }, - ) - .await - } - "/admin/add-incoming" => { - assert_method(&parts, Method::POST)?; - let request: AddIncomingRequest = parse_json(&parts, body).await; - let mut guard = state.incoming.lock().await; - let row_id = SafeUint64::try_from(guard.len() as u64 + 1).unwrap(); - let timestamp = Timestamp::now(); - guard.push(IncomingTransaction { - row_id, - date: timestamp, - amount: request.amount, - reserve_pub: request.reserve_pub, - debit_account: request.debit_account, - credit_account: Url::parse("payto://bitcoin").unwrap(), - }); - json_response(StatusCode::OK, &AddIncomingResponse { timestamp, row_id }).await - } - _ => return Err((StatusCode::NOT_FOUND, ErrorCode::GENERIC_ENDPOINT_UNKNOWN)), - }; - return Ok(response); -} +use std::{ + str::FromStr, + time::{Duration, SystemTime}, +}; + +use api_common::{Amount, SafeUint64, ShortHashCode, Timestamp}; +use api_wire::{OutgoingBankTransaction, OutgoingHistory}; +use async_compression::tokio::bufread::ZlibDecoder; +use btc_wire::{ + bitcoincore_rpc::{ + bitcoin::{Address, Amount as BtcAmount, BlockHash}, + json::GetTransactionResultDetailCategory as Category, + Client, RpcApi, + }, + rpc_utils::{common_rpc, dirty_guess_network, sender_address, wallet_rpc}, + ClientExtended, +}; +use error_codes::ErrorCode; +use hyper::{ + header, + http::request::Parts, + service::{make_service_fn, service_fn}, + Body, Error, Method, Request, Response, Server, StatusCode, +}; +use tokio::{io::AsyncReadExt, sync::Mutex}; +use url::Url; + +use crate::{ + api_common::{Base32, ErrorDetail}, + api_wire::{ + AddIncomingRequest, AddIncomingResponse, HistoryParams, IncomingBankTransaction, + IncomingHistory, TransferRequest, TransferResponse, + }, +}; + +mod error_codes; + +fn bitcoin_payto(addr: &Address) -> Url { + Url::from_str(&format!("payto://bitcoin/{}", addr.to_string())).unwrap() +} + +impl Into<Amount> for BtcAmount { + fn into(self) -> Amount { + let sat = self.as_sat(); + return Amount::new("BTC", sat / 100_000_000, ((sat % 100_000_000) / 10) as u32); + } +} + +impl TryFrom<Amount> for BtcAmount { + type Error = String; + + fn try_from(value: Amount) -> Result<Self, Self::Error> { + if value.currency != "BTC" { + return Err("Wrong currency".to_string()); + } + + let sat = value.value * 100_000_000 + value.fraction as u64 * 10; + return Ok(Self::from_sat(sat)); + } +} + +#[tokio::main] +async fn main() { + let network = dirty_guess_network(); + { + let common = common_rpc(network).unwrap(); + common.load_wallet("wire").ok(); + } + let state = ServerState { + incoming: Mutex::new(Vec::new()), + outgoing: Mutex::new(Vec::new()), + client: Mutex::new(wallet_rpc(network, "wire")), + }; + let state: &'static ServerState = Box::leak(Box::new(state)); + + // BTC worker thread + + std::thread::spawn(move || { + let rpc = wallet_rpc(network, "wire"); + let mut last_hash: Option<BlockHash> = None; + let confirmation = 1; + + loop { + let txs = rpc + .list_since_block(last_hash.as_ref(), Some(confirmation), None, Some(true)) + .unwrap(); + let self_addr = rpc.get_new_address(None, None).unwrap(); + last_hash = Some(txs.lastblock); + + for tx in txs + .transactions + .into_iter() + .filter(|tx| tx.info.confirmations >= confirmation as i32) + { + let id = &tx.info.txid; + match tx.detail.category { + Category::Send => match rpc.get_tx_op_return(&id) { + Ok((full, wtid)) => { + let sender = sender_address(&rpc, &full).unwrap(); + let credit_account = bitcoin_payto(&sender); + let debit_account = bitcoin_payto(&self_addr); + let time = tx.info.blocktime.unwrap(); + let date = + Timestamp::from(SystemTime::UNIX_EPOCH + Duration::from_secs(time)); + let amount = tx.detail.amount.abs().to_unsigned().unwrap().into(); + let mut lock = state.outgoing.blocking_lock(); + println!( + "Send {} {} {:?} {}", + &debit_account, &credit_account, &date, &amount + ); + let array: [u8; 32] = wtid[..32].try_into().unwrap(); + let wtid = Base32::from(array); + let row_id = lock.len() as u64 + 1; + lock.push(OutgoingTransaction { + row_id: SafeUint64::try_from(row_id).unwrap(), + date, + amount, + debit_account, + credit_account, + wtid, + }); + } + Err(err) => println!("receive: {}", err), + }, + Category::Receive => match rpc.get_tx_segwit_key(&id) { + Ok((full, reserve_pub)) => { + let sender = sender_address(&rpc, &full).unwrap(); + let debit_account = bitcoin_payto(&sender); + let credit_account = bitcoin_payto(tx.detail.address.as_ref().unwrap()); + let time = tx.info.blocktime.unwrap(); + let date = + Timestamp::from(SystemTime::UNIX_EPOCH + Duration::from_secs(time)); + let amount = tx.detail.amount.to_unsigned().unwrap().into(); + let mut lock = state.incoming.blocking_lock(); + println!( + "Receive {} {} {:?} {}", + &debit_account, &credit_account, &date, &amount + ); + let row_id = lock.len() as u64 + 1; + lock.push(IncomingTransaction { + row_id: SafeUint64::try_from(row_id).unwrap(), + date, + amount, + reserve_pub: reserve_pub.into(), + debit_account, + credit_account, + }); + } + Err(err) => println!("receive: {}", err), + }, + Category::Generate | Category::Immature | Category::Orphan => {} + } + } + + rpc.wait_for_new_block(0).ok(); + } + }); + + let addr = ([0, 0, 0, 0], 8080).into(); + let make_service = make_service_fn(move |_| async move { + Ok::<_, Error>(service_fn(move |req| async move { + let response = match router(req, state).await { + Ok(resp) => resp, + Err((status, err)) => { + json_response( + status, + &ErrorDetail { + code: err as i64, + hint: None, + detail: None, + parameter: None, + path: None, + offset: None, + index: None, + object: None, + currency: None, + type_expected: None, + type_actual: None, + }, + ) + .await + } + }; + Ok::<Response<Body>, Error>(response) + })) + }); + + let server = Server::bind(&addr).serve(make_service); + + println!("Server listening on http://{}", addr); + + if let Err(e) = server.await { + eprintln!("server error: {}", e); + } +} + +struct IncomingTransaction { + row_id: SafeUint64, + date: Timestamp, + amount: Amount, + reserve_pub: ShortHashCode, + debit_account: Url, + credit_account: Url, +} + +struct OutgoingTransaction { + row_id: SafeUint64, + date: Timestamp, + amount: Amount, + wtid: ShortHashCode, + debit_account: Url, + credit_account: Url, +} + +struct ServerState { + incoming: Mutex<Vec<IncomingTransaction>>, + outgoing: Mutex<Vec<OutgoingTransaction>>, + client: Mutex<Client>, +} + +pub mod api_common; +pub mod api_wire; + +async fn parse_json<J: serde::de::DeserializeOwned>(parts: &Parts, body: Body) -> J { + let bytes = hyper::body::to_bytes(body).await.unwrap(); + let mut buf = Vec::new(); + let decompressed = if parts + .headers + .get(header::CONTENT_ENCODING) + .map(|it| it == "deflate") + .unwrap_or(false) + { + let mut decoder = ZlibDecoder::new(bytes.as_ref()); + decoder.read_to_end(&mut buf).await.unwrap(); + &buf + } else { + bytes.as_ref() + }; + + serde_json::from_slice(&decompressed).unwrap() +} + +async fn json_response<J: serde::Serialize>(status: StatusCode, json: &J) -> Response<Body> { + let json = serde_json::to_vec(json).unwrap(); + /* let mut encoder = ZlibEncoder::new(Vec::new()); + encoder.write_all(&json).await.unwrap(); + encoder.shutdown().await.unwrap(); + + let encoded = encoder.into_inner();*/ + Response::builder() + .status(status) + .header(header::CONTENT_TYPE, "application/json") + //.header(header::CONTENT_ENCODING, "deflate") + .body(Body::from(json)) + .unwrap() +} + +type ServerErr = (StatusCode, ErrorCode); + +fn assert_method(parts: &Parts, method: Method) -> Result<(), ServerErr> { + if parts.method == method { + Ok(()) + } else { + Err(( + StatusCode::METHOD_NOT_ALLOWED, + ErrorCode::GENERIC_METHOD_INVALID, + )) + } +} + +async fn router( + req: Request<Body>, + state: &'static ServerState, +) -> Result<Response<Body>, (StatusCode, ErrorCode)> { + let (parts, body) = req.into_parts(); + let response = match parts.uri.path() { + "/transfer" => { + assert_method(&parts, Method::POST)?; + let request: TransferRequest = parse_json(&parts, body).await; + let client = state.client.lock().await; + let address = request.credit_account.path().trim_start_matches('/'); + let to = Address::from_str(address).unwrap(); + let amount: BtcAmount = request.amount.try_into().unwrap(); + client + .send_op_return(&to, amount, request.wtid.as_ref()) + .unwrap(); + let timestamp = Timestamp::now(); + json_response( + StatusCode::OK, + &TransferResponse { + timestamp, + row_id: SafeUint64::try_from(0).unwrap(), + }, + ) + .await + } + "/history/incoming" => { + assert_method(&parts, Method::GET)?; + let params: HistoryParams = + serde_urlencoded::from_str(parts.uri.query().unwrap_or("")).unwrap(); + let guard = state.incoming.lock().await; + let transactions: Vec<IncomingBankTransaction> = guard + .iter() + .map(|tx| IncomingBankTransaction::IncomingReserveTransaction { + row_id: tx.row_id, + date: tx.date, + amount: tx.amount.clone(), + credit_account: tx.credit_account.clone(), + debit_account: tx.debit_account.clone(), + reserve_pub: tx.reserve_pub.clone(), + }) + .collect(); + json_response( + StatusCode::OK, + &IncomingHistory { + incoming_transactions: transactions, + }, + ) + .await + } + "/history/outgoing" => { + assert_method(&parts, Method::GET)?; + let params: HistoryParams = + serde_urlencoded::from_str(parts.uri.query().unwrap_or("")).unwrap(); + let guard = state.outgoing.lock().await; + let transactions: Vec<OutgoingBankTransaction> = guard + .iter() + .map(|tx| OutgoingBankTransaction { + row_id: tx.row_id, + date: tx.date, + amount: tx.amount.clone(), + credit_account: tx.credit_account.clone(), + wtid: tx.wtid.clone(), + debit_account: tx.debit_account.clone(), + exchange_base_url: Url::parse("http://localhost:8080").unwrap(), + }) + .collect(); + json_response( + StatusCode::OK, + &OutgoingHistory { + outgoing_transactions: transactions, + }, + ) + .await + } + "/admin/add-incoming" => { + assert_method(&parts, Method::POST)?; + let request: AddIncomingRequest = parse_json(&parts, body).await; + let mut guard = state.incoming.lock().await; + let row_id = SafeUint64::try_from(guard.len() as u64 + 1).unwrap(); + let timestamp = Timestamp::now(); + guard.push(IncomingTransaction { + row_id, + date: timestamp, + amount: request.amount, + reserve_pub: request.reserve_pub, + debit_account: request.debit_account, + credit_account: Url::parse("payto://bitcoin").unwrap(), + }); + json_response(StatusCode::OK, &AddIncomingResponse { timestamp, row_id }).await + } + _ => return Err((StatusCode::NOT_FOUND, ErrorCode::GENERIC_ENDPOINT_UNKNOWN)), + }; + return Ok(response); +}