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:
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);
+}