commit c1cfa0e5a18a8cd2455eb8fb115f71f8841e3168
parent 029cae75e52654c51daa671cec151874ce93a448
Author: Antoine A <>
Date: Thu, 2 Dec 2021 18:26:49 +0100
Refactor to new architecture
Diffstat:
8 files changed, 722 insertions(+), 660 deletions(-)
diff --git a/Cargo.lock b/Cargo.lock
@@ -9,6 +9,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
+name = "aho-corasick"
+version = "0.7.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
name = "argh"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -51,6 +60,17 @@ dependencies = [
]
[[package]]
+name = "async-trait"
+version = "0.1.51"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -74,6 +94,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23ce669cd6c8588f79e15cf450314f9638f967fc5770ff1c7c1deb0925ea7cfa"
[[package]]
+name = "base64"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
+
+[[package]]
name = "base64-compat"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -140,6 +166,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
+name = "block-buffer"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
+dependencies = [
+ "generic-array",
+]
+
+[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -160,11 +195,13 @@ dependencies = [
"bitcoincore-rpc",
"criterion",
"fastrand",
- "owo-colors",
+ "postgres",
"rand",
- "rustyline",
"serde",
"thiserror",
+ "uri-pack",
+ "url",
+ "wire-gateway",
]
[[package]]
@@ -218,14 +255,12 @@ dependencies = [
]
[[package]]
-name = "clipboard-win"
-version = "4.2.2"
+name = "cpufeatures"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3db8340083d28acb43451166543b98c838299b7e0863621be53a338adceea0ed"
+checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
dependencies = [
- "error-code",
- "str-buf",
- "winapi",
+ "libc",
]
[[package]]
@@ -318,6 +353,16 @@ dependencies = [
]
[[package]]
+name = "crypto-mac"
+version = "0.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
+dependencies = [
+ "generic-array",
+ "subtle",
+]
+
+[[package]]
name = "csv"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -375,24 +420,12 @@ dependencies = [
]
[[package]]
-name = "dirs-next"
-version = "2.0.0"
+name = "digest"
+version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1"
+checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
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",
+ "generic-array",
]
[[package]]
@@ -402,20 +435,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
-name = "endian-type"
-version = "0.1.2"
+name = "env_logger"
+version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d"
+checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
+dependencies = [
+ "log",
+ "regex",
+]
[[package]]
-name = "error-code"
-version = "2.3.0"
+name = "fallible-iterator"
+version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5115567ac25674e0043e472be13d14e537f37ea8aa4bdc4aef0c89add1db1ff"
-dependencies = [
- "libc",
- "str-buf",
-]
+checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7"
[[package]]
name = "fastrand"
@@ -427,17 +460,6 @@ dependencies = [
]
[[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"
@@ -466,12 +488,28 @@ dependencies = [
]
[[package]]
+name = "futures"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8cd0210d8c325c245ff06fd95a3b13689a1a276ac8cfa8e8720cb840bfb84b9e"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
name = "futures-channel"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fc8cd39e3dbf865f7340dce6a2d401d24fd37c6fe6c4f0ee0de8bfca2252d27"
dependencies = [
"futures-core",
+ "futures-sink",
]
[[package]]
@@ -481,6 +519,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "629316e42fe7c2a0b9a65b47d159ceaa5453ab14e8f0a3c5eedbb8cd55b4a445"
[[package]]
+name = "futures-executor"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b808bf53348a36cab739d7e04755909b9fcaaa69b7d7e588b37b6ec62704c97"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e481354db6b5c353246ccf6a728b0c5511d752c08da7260546fc0933869daa11"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a89f17b21645bc4ed773c69af9c9a0effd4a3f1a3876eadd453469f8854e7fdd"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "996c6442437b62d21a32cd9906f9c41e7dc1e19a9579843fad948696769305af"
+
+[[package]]
name = "futures-task"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -492,10 +564,26 @@ version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41d22213122356472061ac0f1ab2cee28d2bac8491410fd68c2af53d1cedb83e"
dependencies = [
+ "futures-channel",
"futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
"futures-task",
+ "memchr",
"pin-project-lite",
"pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "generic-array"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
+dependencies = [
+ "typenum",
+ "version_check",
]
[[package]]
@@ -534,6 +622,16 @@ dependencies = [
]
[[package]]
+name = "hmac"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
+dependencies = [
+ "crypto-mac",
+ "digest",
+]
+
+[[package]]
name = "http"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -665,6 +763,15 @@ 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"
@@ -680,6 +787,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]]
+name = "md-5"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5a279bb9607f9f53c22d496eade00d138d1bdcccd07d74650387cf94942a15"
+dependencies = [
+ "block-buffer",
+ "digest",
+ "opaque-debug",
+]
+
+[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -727,28 +845,6 @@ dependencies = [
]
[[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"
@@ -783,10 +879,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
-name = "owo-colors"
-version = "3.1.0"
+name = "opaque-debug"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
+
+[[package]]
+name = "parking_lot"
+version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f9ad6d222cdc2351ccabb7af4f68bfaecd601b33c5f10d410ec89d2a273f6fff"
+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"
@@ -795,6 +916,24 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e"
[[package]]
+name = "phf"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9fc3db1018c4b59d7d582a739436478b6035138b6aecbce989fc91c3e98409f"
+dependencies = [
+ "phf_shared",
+]
+
+[[package]]
+name = "phf_shared"
+version = "0.10.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
+dependencies = [
+ "siphasher",
+]
+
+[[package]]
name = "pin-project-lite"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -835,6 +974,49 @@ dependencies = [
]
[[package]]
+name = "postgres"
+version = "0.19.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb76d6535496f633fa799bb872ffb4790e9cbdedda9d35564ca0252f930c0dd5"
+dependencies = [
+ "bytes",
+ "fallible-iterator",
+ "futures",
+ "log",
+ "tokio",
+ "tokio-postgres",
+]
+
+[[package]]
+name = "postgres-protocol"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b145e6a4ed52cb316a27787fc20fe8a25221cb476479f61e4e0327c15b98d91a"
+dependencies = [
+ "base64",
+ "byteorder",
+ "bytes",
+ "fallible-iterator",
+ "hmac",
+ "md-5",
+ "memchr",
+ "rand",
+ "sha2",
+ "stringprep",
+]
+
+[[package]]
+name = "postgres-types"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04619f94ba0cc80999f4fc7073607cb825bc739a883cb6d20900fc5e009d6b0d"
+dependencies = [
+ "bytes",
+ "fallible-iterator",
+ "postgres-protocol",
+]
+
+[[package]]
name = "ppv-lite86"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -850,22 +1032,34 @@ dependencies = [
]
[[package]]
-name = "quote"
-version = "1.0.10"
+name = "quickcheck"
+version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
+checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6"
+dependencies = [
+ "env_logger",
+ "log",
+ "rand",
+]
+
+[[package]]
+name = "quickcheck_macros"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9"
dependencies = [
"proc-macro2",
+ "quote",
+ "syn",
]
[[package]]
-name = "radix_trie"
-version = "0.2.1"
+name = "quote"
+version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd"
+checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
- "endian-type",
- "nibble_vec",
+ "proc-macro2",
]
[[package]]
@@ -943,21 +1137,13 @@ dependencies = [
]
[[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 = [
+ "aho-corasick",
+ "memchr",
"regex-syntax",
]
@@ -989,30 +1175,6 @@ 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"
@@ -1135,6 +1297,31 @@ dependencies = [
]
[[package]]
+name = "sha2"
+version = "0.9.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
+dependencies = [
+ "block-buffer",
+ "cfg-if",
+ "cpufeatures",
+ "digest",
+ "opaque-debug",
+]
+
+[[package]]
+name = "siphasher"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
+
+[[package]]
+name = "slab"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
+
+[[package]]
name = "smallvec"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1151,10 +1338,14 @@ dependencies = [
]
[[package]]
-name = "str-buf"
-version = "1.0.5"
+name = "stringprep"
+version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d44a3643b4ff9caf57abcee9c2c621d6c03d9135e0d8b589bd9afb5992cb176a"
+checksum = "8ee348cb74b87454fff4b551cbf727025810a004f88aeacae7f85b87f4e9a1c1"
+dependencies = [
+ "unicode-bidi",
+ "unicode-normalization",
+]
[[package]]
name = "strsim"
@@ -1163,6 +1354,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
+name = "subtle"
+version = "2.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
+
+[[package]]
name = "syn"
version = "1.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1256,6 +1453,43 @@ dependencies = [
]
[[package]]
+name = "tokio-postgres"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4b6c8b33df661b548dcd8f9bf87debb8c56c05657ed291122e1188698c2ece95"
+dependencies = [
+ "async-trait",
+ "byteorder",
+ "bytes",
+ "fallible-iterator",
+ "futures",
+ "log",
+ "parking_lot",
+ "percent-encoding",
+ "phf",
+ "pin-project-lite",
+ "postgres-protocol",
+ "postgres-types",
+ "socket2",
+ "tokio",
+ "tokio-util",
+]
+
+[[package]]
+name = "tokio-util"
+version = "0.6.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e99e1983e5d376cd8eb4b66604d2e99e79f5bd988c3055891dcd8c9e2604cc0"
+dependencies = [
+ "bytes",
+ "futures-core",
+ "futures-sink",
+ "log",
+ "pin-project-lite",
+ "tokio",
+]
+
+[[package]]
name = "tower-service"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1288,6 +1522,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642"
[[package]]
+name = "typenum"
+version = "1.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec"
+
+[[package]]
name = "unicode-bidi"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1327,6 +1567,8 @@ dependencies = [
"criterion",
"csv",
"fastrand",
+ "quickcheck",
+ "quickcheck_macros",
"serde_json",
"thiserror",
"url",
@@ -1346,10 +1588,10 @@ dependencies = [
]
[[package]]
-name = "utf8parse"
-version = "0.2.0"
+name = "version_check"
+version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
+checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "walkdir"
@@ -1474,55 +1716,11 @@ 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",
@@ -1531,6 +1729,6 @@ dependencies = [
"serde_with",
"thiserror",
"tokio",
- "uri-pack",
+ "tokio-postgres",
"url",
]
diff --git a/btc-wire/Cargo.toml b/btc-wire/Cargo.toml
@@ -8,8 +8,6 @@ edition = "2021"
bitcoincore-rpc = "0.14.0"
# Cli args
argh = "0.1.6"
-# Readline
-rustyline = "9.0.0"
# Bech32 encoding and decoding
bech32 = "0.8.1"
# Secure random
@@ -18,10 +16,16 @@ rand = { version = "0.8.4", features = ["getrandom"] }
fastrand = "1.5.0"
# Serialization library
serde = { version = "1.0.130", features = ["derive"] }
-# Zero allocation terminal color
-owo-colors = "3.1.0"
# Error macros
thiserror = "1.0.30"
+# Postgres client
+postgres = "0.19.2"
+# Optimized uri binary format
+uri-pack = { path = "../uri-pack" }
+# Url format
+url = { version = "2.2.2", features = ["serde"] }
+# Wire gateway api
+wire-gateway = { path = "../wire-gateway" }
[dev-dependencies]
# statistics-driven micro-benchmarks
diff --git a/btc-wire/src/main.rs b/btc-wire/src/main.rs
@@ -1,238 +1,249 @@
-use std::{collections::HashSet, str::FromStr, time::Duration};
-
-use bitcoincore_rpc::{bitcoin::Amount, RpcApi};
+use bitcoincore_rpc::{
+ bitcoin::{hashes::Hash, Address, Amount as BtcAmount, BlockHash, Txid},
+ json::GetTransactionResultDetailCategory as Category,
+ Client as RPC, RpcApi,
+};
use btc_wire::{
- rpc_utils::{
- common_rpc, dirty_guess_network, network_dir_path, received_since, wallet_rpc, CLIENT, WIRE,
- },
- ClientExtended,
+ rpc_utils::{common_rpc, dirty_guess_network, sender_address, wallet_rpc, WIRE},
+ segwit::DecodeSegWitErr,
+ ClientExtended, GetOpReturnErr, GetSegwitErr,
};
-
-#[derive(argh::FromArgs)]
-/// Bitcoin metadata tester
-struct Args {
- #[argh(subcommand)]
- cmd: Cmd,
+use postgres::{Client, NoTls};
+use std::{
+ collections::HashMap,
+ str::FromStr,
+ time::{Duration, SystemTime},
+};
+use url::Url;
+use wire_gateway::api_common::Amount;
+
+#[repr(u8)]
+enum Status {
+ /// Client have ask for a transaction
+ Proposed = 0,
+ /// Transaction have been announced to the bitcoin network
+ Pending = 1,
+ /// Transaction have been mined
+ Confirmed = 2,
+ /// The wire cannot failed to send this transaction and will try latter
+ Delayed = 3,
}
-#[derive(argh::FromArgs)]
-#[argh(subcommand, name = "mine")]
-/// Mine block for the given wallet
-struct MineCmd {
- #[argh(option, short = 'r')]
- /// repeat every ? ms
- repeat: Option<u64>,
-
- #[argh(positional)]
- /// the wallet name
- wallet: String,
-
- #[argh(positional, default = "1")]
- /// repeat every ? ms
- amount: u64,
+fn btc_payto_url(addr: &Address) -> Url {
+ Url::from_str(&format!("payto://bitcoin/{}", addr.to_string())).unwrap()
}
-#[derive(argh::FromArgs)]
-
-#[argh(subcommand, name = "send")]
-/// Send message
-struct SendRole {
- #[argh(switch, short = 'm')]
- /// mine on send
- mine: bool,
+fn btc_payto_addr(url: &Url) -> Result<Address, String> {
+ if url.domain() != Some("bitcoin") {
+ return Err("".to_string());
+ }
+ let str = url.path().trim_start_matches('/');
+ return Ok(Address::from_str(str).map_err(|_| "".to_string())?);
}
-#[derive(argh::FromArgs)]
+fn btc_amount_to_taler_amount(amount: &BtcAmount) -> Amount {
+ let sat = amount.as_sat();
+ return Amount::new("BTC", sat / 100_000_000, (sat % 100_000_000) as u32);
+}
-/// Receive message
-#[argh(subcommand, name = "receive")]
-struct ReceiveRole {}
+fn taler_amount_to_btc_amount(amount: &Amount) -> Result<BtcAmount, String> {
+ if amount.currency != "BTC" {
+ return Err("Wrong currency".to_string());
+ }
-#[derive(argh::FromArgs)]
-#[argh(subcommand)]
-enum Role {
- Send(SendRole),
- Receive(ReceiveRole),
+ let sat = amount.value * 100_000_000 + amount.fraction as u64;
+ return Ok(BtcAmount::from_sat(sat));
}
-enum Metadata {
- SegWit,
- OpReturn,
+fn encode_info(wtid: &[u8; 32], url: &Url) -> Vec<u8> {
+ let mut buffer = Vec::new();
+ buffer.extend_from_slice(wtid);
+ let parts = format!("{}{}", url.domain().unwrap_or(""), url.path());
+ let packed = uri_pack::pack_uri(&parts).unwrap();
+ buffer.push((url.scheme() == "http:") as u8);
+ buffer.extend_from_slice(&packed);
+ return buffer;
}
-impl ToString for Metadata {
- fn to_string(&self) -> String {
- match self {
- Metadata::SegWit => "SegWit",
- Metadata::OpReturn => "OpReturn",
- }
- .to_string()
+fn decode_info(bytes: &[u8]) -> ([u8; 32], Url) {
+ let mut packed = uri_pack::unpack_uri(&bytes[33..]).unwrap();
+ packed.insert_str(0, "://");
+ if bytes[32] != 0 {
+ packed.insert(0, 's');
}
+ packed.insert_str(0, "http");
+ let url = Url::parse(&packed).unwrap();
+ return (bytes[..32].try_into().unwrap(), url);
}
-impl FromStr for Metadata {
- type Err = String;
-
- fn from_str(s: &str) -> Result<Self, Self::Err> {
- match s {
- "SegWit" | "Segwit" | "segwit" | "seg" | "s" => Ok(Self::SegWit),
- "OpReturn" | "Opreturn" | "opreturn" | "op" | "o" => Ok(Self::OpReturn),
- _ => Err(format!("Unknown")),
+#[cfg(test)]
+mod test {
+ use btc_wire::test::rand_key;
+ use url::Url;
+
+ use crate::{decode_info, encode_info};
+
+ #[test]
+ fn decode_encode_info() {
+ let key = rand_key();
+ let urls = [
+ "https://git.taler.net/",
+ "https://git.taler.net/depolymerization.git/",
+ ];
+
+ for url in urls {
+ let url = Url::parse(url).unwrap();
+ let encode = encode_info(&key, &url);
+ let decode = decode_info(&encode);
+ assert_eq!(key, decode.0);
+ assert_eq!(url, decode.1);
}
}
}
-#[derive(argh::FromArgs)]
-#[argh(subcommand, name = "msg")]
-/// Msg exchange msg using metadata
-struct MsgCmd {
- #[argh(subcommand)]
- role: Role,
-
- #[argh(positional)]
- method: Option<Metadata>,
-}
-
-#[derive(argh::FromArgs)]
-#[argh(subcommand)]
-enum Cmd {
- Mine(MineCmd),
- Msg(MsgCmd),
-}
-
-fn main() {
- // Guess network by trying to connect to a JSON RPC server
- let network = dirty_guess_network();
- {
- let existing_wallets: HashSet<String> =
- std::fs::read_dir(network_dir_path(network).join("wallets"))
- .unwrap()
- .filter_map(|it| it.ok())
- .map(|it| it.file_name().to_string_lossy().to_string())
- .collect();
-
- let rpc = common_rpc(network).expect("Failed to open common client");
- if !existing_wallets.contains(CLIENT) || !existing_wallets.contains(WIRE) {
- println!("Generate new wallets");
- // Create wallets
- rpc.create_wallet(&WIRE, None, None, None, None).unwrap();
- rpc.create_wallet(&CLIENT, None, None, None, None).unwrap();
- // Add 50 BTC to client wallet
- let rpc = wallet_rpc(network, CLIENT);
- let addr = rpc.get_new_address(None, None).unwrap();
- rpc.generate_to_address(101 /* Need 100 blocks to validate */, &addr)
- .unwrap();
- } else {
- rpc.load_wallet(&WIRE).ok();
- rpc.load_wallet(&CLIENT).ok();
+/// Listen for new proposed transactions and announce them on the bitcoin network
+fn sender(rpc: RPC, mut db: Client) -> Result<(), Box<dyn std::error::Error>> {
+ loop {
+ let mut announced = Vec::new();
+ let rows = db.query(
+ "SELECT id, amount, wtid, debit_acc, credit_acc, exchange_url FROM tx_out WHERE status=$1",
+ &[&(Status::Proposed as i16)],
+ )?;
+
+ for row in rows {
+ let result: Result<i32, Box<dyn std::error::Error>> = (|| {
+ let id: i32 = row.get(0);
+ let amount: Amount = Amount::from_str(row.get(1))?;
+ let reserve_pub: &[u8] = row.get(2);
+ let debit_addr: Address = btc_payto_addr(&Url::parse(row.get(3))?)?;
+ let credit_addr: Address = btc_payto_addr(&Url::parse(row.get(4))?)?;
+ let exchange_base_url: Url = Url::parse(row.get(5))?;
+ let metadata = encode_info(reserve_pub.try_into()?, &exchange_base_url);
+ rpc.send_op_return(
+ &credit_addr,
+ taler_amount_to_btc_amount(&amount)?,
+ &metadata,
+ )?;
+ println!("{} >> {} {} PENDING", &debit_addr, &credit_addr, &amount);
+ Ok(id)
+ })();
+ match result {
+ Ok(id) => announced.push(id),
+ Err(err) => println!("sender: {}", err),
+ }
}
+
+ db.execute(
+ "UPDATE tx_out SET status = $1 WHERE id = ANY($2)",
+ &[&(Status::Pending as i16), &announced],
+ )?;
+ std::thread::sleep(Duration::from_millis(300));
}
+}
- let args: Args = argh::from_env();
-
- match args.cmd {
- Cmd::Mine(MineCmd {
- repeat,
- wallet,
- amount,
- }) => {
- let rpc = wallet_rpc(network, &wallet);
- let balance = rpc.get_balance(None, None).unwrap();
- let addr = rpc.get_new_address(None, None).unwrap();
- println!("{} {}", wallet, balance);
- if amount == 0 {
- return;
- }
- loop {
- println!("Mine {} block", amount);
- rpc.generate_to_address(amount, &addr).unwrap();
-
- let balance = rpc.get_balance(None, None).unwrap();
- println!("{} {}", wallet, balance);
-
- if let Some(wait) = repeat {
- std::thread::sleep(Duration::from_millis(wait))
- } else {
- return;
- }
- }
- }
- Cmd::Msg(MsgCmd { role, method }) => {
- println!("Initial state:");
- let client_rpc = wallet_rpc(network, CLIENT);
- let wire_rpc = wallet_rpc(network, WIRE);
- let client_addr = client_rpc.get_new_address(None, None).unwrap();
- let wire_addr = wire_rpc.get_new_address(None, None).unwrap();
- println!("{} {}", WIRE, wire_rpc.get_balance(None, None).unwrap());
- println!("{} {}", CLIENT, client_rpc.get_balance(None, None).unwrap());
- let method = method.unwrap_or(Metadata::SegWit);
- match role {
- Role::Send(SendRole { mine }) => {
- println!("Send message using {}", method.to_string());
- let mut rl = rustyline::Editor::<()>::new();
- loop {
- let rl = rl.readline(">> ");
- match rl {
- Ok(line) => {
- match method {
- Metadata::SegWit => {
- for chunk in line.as_bytes().chunks(32) {
- let mut key = [0; 32];
- key[..chunk.len()].copy_from_slice(&chunk);
- client_rpc
- .send_segwit_key(
- &wire_addr,
- Amount::from_sat(4200),
- &key,
- )
- .unwrap();
- }
- }
- Metadata::OpReturn => {
- for chunk in line.as_bytes().chunks(80) {
- client_rpc
- .send_op_return(
- &wire_addr,
- Amount::from_sat(4200),
- chunk,
- )
- .unwrap();
- }
- }
- }
-
- if mine {
- client_rpc.generate_to_address(1, &client_addr).unwrap();
- }
- }
- Err(_) => break,
- }
+/// Listen for mined block and index confirmed transactions into the database
+fn watcher(rpc: RPC, mut db: Client) -> Result<(), Box<dyn std::error::Error>> {
+ let stored_hash = db
+ .query("SELECT value FROM state WHERE name='last_hash'", &[])
+ .unwrap();
+ let mut last_hash: Option<BlockHash> = if stored_hash.len() == 1 {
+ Some(BlockHash::from_slice(stored_hash[0].get(0)).unwrap())
+ } else {
+ None
+ };
+ let confirmation = 1;
+
+ loop {
+ let list =
+ rpc.list_since_block(last_hash.as_ref(), Some(confirmation), None, Some(true))?;
+
+ // List all confirmed send and receive transactions since last check
+ let txs: HashMap<Txid, Category> = list
+ .transactions
+ .into_iter()
+ .filter_map(|tx| {
+ let cat = tx.detail.category;
+ (tx.info.confirmations >= confirmation as i32
+ && (cat == Category::Send || cat == Category::Receive))
+ .then(|| (tx.info.txid, cat))
+ })
+ .collect();
+
+ for (id, category) in txs {
+ match category {
+ Category::Send => match rpc.get_tx_op_return(&id) {
+ Ok((full, metadata)) => {
+ let (wtid, exchange_base_url) = decode_info(&metadata);
+ let credit_addr = sender_address(&rpc, &full)?;
+ let amount =
+ btc_amount_to_taler_amount(&full.tx.amount.abs().to_unsigned()?.into());
+ let row = db.query_one(
+ "UPDATE tx_out SET status=$1 WHERE wtid=$2 AND exchange_url=$3 RETURNING debit_acc",
+ &[
+ &(Status::Confirmed as i16),
+ &wtid.as_ref(),
+ &exchange_base_url.to_string(),
+ ],
+ )?;
+ let debit_addr = btc_payto_addr(&Url::parse(row.get(0))?)?;
+ println!("{} >> {} {} CONFIRMED", &debit_addr, &credit_addr, &amount);
}
- }
- Role::Receive(_) => {
- println!("Receive message using {}", method.to_string());
- let (_, mut hash) = received_since(&wire_rpc, None).unwrap();
- loop {
- wire_rpc.wait_for_new_block(0).ok();
- println!("new block");
- let (ids, nhash) = received_since(&wire_rpc, Some(&hash)).unwrap();
- hash = nhash;
- for id in ids {
- let msg: String = match method {
- Metadata::SegWit => {
- let (_, decoded) = wire_rpc.get_tx_segwit_key(&id).unwrap();
- String::from_utf8_lossy(&decoded).to_string()
- }
- Metadata::OpReturn => {
- let (_, decoded) = wire_rpc.get_tx_op_return(&id).unwrap();
- String::from_utf8_lossy(&decoded).to_string()
- }
- };
- println!("> {}", msg);
- }
+ Err(err) => match err {
+ GetOpReturnErr::MissingOpReturn => {} // ignore
+ err => println!("send: {} {}", id, err),
+ },
+ },
+ Category::Receive => match rpc.get_tx_segwit_key(&id) {
+ Ok((full, reserve_pub)) => {
+ let debit_addr = sender_address(&rpc, &full)?;
+ let credit_addr = full.tx.details[0].address.as_ref().unwrap();
+ let time = full.tx.info.blocktime.unwrap();
+ let date = SystemTime::UNIX_EPOCH + Duration::from_secs(time);
+ let amount = btc_amount_to_taler_amount(&full.tx.amount.to_unsigned()?);
+ db.execute("INSERT INTO tx_in (_date, amount, reserve_pub, debit_acc, credit_acc) VALUES ($1, $2, $3, $4, $5)", &[
+ &date, &amount.to_string(), &reserve_pub.as_ref(), &btc_payto_url(&debit_addr).to_string(), &btc_payto_url(&credit_addr).to_string()
+ ])?;
+ println!("{} << {} {}", &debit_addr, &credit_addr, &amount);
}
- }
+ Err(err) => match err {
+ GetSegwitErr::Decode(
+ DecodeSegWitErr::MissingSegWitAddress | DecodeSegWitErr::NoMagicIdMatch,
+ ) => {}
+ err => println!("receive: {} {}", id, err),
+ },
+ },
+ Category::Generate | Category::Immature | Category::Orphan => {}
}
}
+ if let Some(_) = last_hash {
+ db.execute(
+ "UPDATE state SET value=$1 WHERE name='last_hash'",
+ &[&list.lastblock.as_ref()],
+ )?;
+ } else {
+ db.execute(
+ "INSERT INTO state (name, value) VALUES ('last_hash', $1)",
+ &[&list.lastblock.as_ref()],
+ )?;
+ };
+ last_hash = Some(list.lastblock);
+ println!("Wait for block");
+ rpc.wait_for_new_block(0).ok();
}
}
+
+fn main() {
+ // Guess network by trying to connect to a JSON RPC server
+ let network = dirty_guess_network();
+ let rpc = common_rpc(network).unwrap();
+ rpc.load_wallet(&WIRE).ok();
+ let rpc_watcher = wallet_rpc(network, "wire");
+ let rpc_sender = wallet_rpc(network, "wire");
+
+ let postgres_config = "postgres://localhost/wire_gateway?user=postgres";
+ let db_watcher = Client::connect(&postgres_config, NoTls).unwrap();
+ let db_sender = Client::connect(&postgres_config, NoTls).unwrap();
+ let join = std::thread::spawn(move || sender(rpc_sender, db_sender).unwrap());
+ watcher(rpc_watcher, db_watcher).unwrap();
+ join.join().unwrap();
+}
diff --git a/script/test_bank.sh b/script/test_bank.sh
@@ -17,11 +17,11 @@ trap cleanup EXIT
echo "OK"
-BANK_ENDPOINT=http://localhost:8080/
+BANK_ENDPOINT=http://172.21.80.1:8080/
echo -n "Making wire transfer to exchange ..."
-btc-wire-cli transfer 0.00004
-btc-wire-cli nblock
+btc-wire-cli.exe transfer 0.00004
+btc-wire-cli.exe nblock
echo " OK"
echo -n "Requesting exchange incoming transaction list ..."
@@ -32,12 +32,12 @@ echo " OK"
echo -n "Making wire transfer from exchange..."
-ADDRESS=`bitcoin-cli -rpcwallet=client getnewaddress`
+ADDRESS=`bitcoin-cli.exe -rpcwallet=client getnewaddress`
taler-exchange-wire-gateway-client \
-b $BANK_ENDPOINT \
-C payto://bitcoin/$ADDRESS \
- -a BTC:0.00002 > /dev/null
-btc-wire-cli nblock
+ la BTC:0.00002 > /dev/null
+btc-wire-cli.exe nblock
echo " OK"
diff --git a/wire-gateway/Cargo.toml b/wire-gateway/Cargo.toml
@@ -5,13 +5,15 @@ edition = "2021"
[dependencies]
# Http library
-hyper = { version = "0.14.15", features = [
- "http1",
- "server",
- "runtime",
-] }
+hyper = { version = "0.14.15", features = ["http1", "server", "runtime"] }
# Async runtime
-tokio = { version = "1.14.0", features = ["net", "macros", "rt-multi-thread", "io-std", "io-util"] }
+tokio = { version = "1.14.0", features = [
+ "net",
+ "macros",
+ "rt-multi-thread",
+ "io-std",
+ "io-util",
+] }
# Serialization framework
serde = { version = "1.0.130", features = ["derive"] }
# Serialization helper
@@ -30,7 +32,5 @@ async-compression = { version = "0.3.8", features = ["tokio", "zlib"] }
rand = { version = "0.8.4", features = ["getrandom"] }
# Url format
url = { version = "2.2.2", features = ["serde"] }
-# Bitcoin taler util
-btc-wire = { path = "../btc-wire" }
-# Optimized uri binary format
-uri-pack = { path = "../uri-pack" }
+# Async postgres client
+tokio-postgres = { version = "0.7.5" }
diff --git a/wire-gateway/db/schema.sql b/wire-gateway/db/schema.sql
@@ -0,0 +1,27 @@
+-- Key value state
+CREATE TABLE state (
+ name TEXT PRIMARY KEY,
+ value BYTEA NOT NULL
+);
+
+-- Incoming transactions
+CREATE TABLE tx_in {
+ id SERIAL PRIMARY KEY,
+ _date TIMESTAMP NOT NULL,
+ amount TEXT NOT NULL,
+ reserve_pub BYTEA NOT NULL,
+ debit_acc TEXT NOT NULL,
+ credit_acc TEXT NOT NULL
+}
+
+-- Outgoing transactions
+CREATE TABLE tx_out {
+ id SERIAL PRIMARY KEY,
+ _date TIMESTAMP NOT NULL,
+ amount TEXT NOT NULL,
+ wtid BYTEA NOT NULL,
+ debit_acc TEXT NOT NULL,
+ credit_acc TEXT NOT NULL,
+ exchange_url TEXT NOT NULL,
+ status SMALLINT NOT NULL
+}
+\ No newline at end of file
diff --git a/wire-gateway/src/lib.rs b/wire-gateway/src/lib.rs
@@ -0,0 +1,3 @@
+pub mod api_common;
+pub mod api_wire;
+pub mod error_codes;
+\ No newline at end of file
diff --git a/wire-gateway/src/main.rs b/wire-gateway/src/main.rs
@@ -1,22 +1,8 @@
-use std::{
- collections::HashMap,
- str::FromStr,
- time::{Duration, SystemTime},
-};
+use std::str::FromStr;
-use api_common::{Amount, SafeUint64, ShortHashCode, Timestamp};
+use api_common::{Amount, SafeUint64, Timestamp, ShortHashCode};
use api_wire::{OutgoingBankTransaction, OutgoingHistory};
use async_compression::tokio::bufread::ZlibDecoder;
-use btc_wire::{
- bitcoincore_rpc::{
- bitcoin::{Address, Amount as BtcAmount, BlockHash, Txid},
- json::GetTransactionResultDetailCategory as Category,
- Client, RpcApi,
- },
- rpc_utils::{common_rpc, dirty_guess_network, sender_address, wallet_rpc},
- segwit::DecodeSegWitErr,
- ClientExtended, GetOpReturnErr, GetSegwitErr,
-};
use error_codes::ErrorCode;
use hyper::{
header,
@@ -24,7 +10,8 @@ use hyper::{
service::{make_service_fn, service_fn},
Body, Error, Method, Request, Response, Server, StatusCode,
};
-use tokio::{io::AsyncReadExt, sync::Mutex};
+use tokio::io::AsyncReadExt;
+use tokio_postgres::{Client, NoTls};
use url::Url;
use crate::{
@@ -36,205 +23,29 @@ use crate::{
mod error_codes;
-fn btc_payto_url(addr: &Address) -> Url {
- Url::from_str(&format!("payto://bitcoin/{}", addr.to_string())).unwrap()
-}
-
-fn btc_payto_addr(url: &Url) -> Result<&str, ServerErr> {
- if url.domain() != Some("bitcoin")
- || url.scheme() != "payto"
- || url.username() != ""
- || url.password().is_some()
- || url.query().is_some()
- || url.fragment().is_some()
- {
- return Err((
- StatusCode::BAD_REQUEST,
- ErrorCode::GENERIC_PAYTO_URI_MALFORMED,
- ));
- }
- return Ok(url.path().trim_start_matches('/'));
-}
-
-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) 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;
- return Ok(Self::from_sat(sat));
- }
-}
-
-fn encode_info(wtid: &[u8; 32], url: &Url) -> Vec<u8> {
- let mut buffer = Vec::new();
- buffer.extend_from_slice(wtid);
- let parts = format!("{}{}", url.domain().unwrap_or(""), url.path());
- let packed = uri_pack::pack_uri(&parts).unwrap();
- buffer.push((url.scheme() == "http:") as u8);
- buffer.extend_from_slice(&packed);
- return buffer;
-}
-
-fn decode_info(bytes: &[u8]) -> ([u8; 32], Url) {
- let mut packed = uri_pack::unpack_uri(&bytes[33..]).unwrap();
- packed.insert_str(0, "://");
- if bytes[32] == 0 {
- packed.insert(0, 's');
- }
- packed.insert_str(0, "http");
- let url = Url::parse(&packed).unwrap();
- return (bytes[..32].try_into().unwrap(), url);
-}
-
-#[cfg(test)]
-mod test {
- use btc_wire::test::rand_key;
- use url::Url;
-
- use crate::{decode_info, encode_info};
-
- #[test]
- fn decode_encode_info() {
- let key = rand_key();
- let urls = [
- "https://git.taler.net/",
- "https://git.taler.net/depolymerization.git/",
- ];
-
- for url in urls {
- let url = Url::parse(url).unwrap();
- let encode = encode_info(&key, &url);
- let decode = decode_info(&encode);
- assert_eq!(key, decode.0);
- assert_eq!(url, decode.1);
- dbg!(encode.len() - 32, urls.len());
- }
- }
+fn check_pay_to(url: &Url) -> bool {
+ return url.domain() == Some("bitcoin")
+ && url.scheme() == "payto"
+ && url.username() == ""
+ && url.password().is_none()
+ && url.query().is_none()
+ && url.fragment().is_none();
}
#[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 result: Result<(), Box<dyn std::error::Error>> = (move || {
- let rpc = wallet_rpc(network, "wire");
- let self_addr = rpc.get_new_address(None, None)?;
- 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))?;
- last_hash = Some(txs.lastblock);
-
- // List all confirmed send and receive transactions since last check
- let txs: HashMap<Txid, Category> = txs
- .transactions
- .into_iter()
- .filter_map(|tx| {
- let cat = tx.detail.category;
- (tx.info.confirmations >= confirmation as i32
- && (cat == Category::Send || cat == Category::Receive))
- .then(|| (tx.info.txid, cat))
- })
- .collect();
+ let (client, connection) =
+ tokio_postgres::connect("postgres://localhost/wire_gateway?user=postgres", NoTls)
+ .await
+ .unwrap();
- for (id, category) in txs {
- match category {
- Category::Send => match rpc.get_tx_op_return(&id) {
- Ok((full, metadata)) => {
- let (wtid, exchange_base_url) = decode_info(&metadata);
- let credit_addr = sender_address(&rpc, &full)?;
- let time = full.tx.info.blocktime.unwrap();
- let date = Timestamp::from(
- SystemTime::UNIX_EPOCH + Duration::from_secs(time),
- );
- let amount = full.tx.amount.abs().to_unsigned()?.into();
- let mut lock = state.outgoing.blocking_lock();
- println!("{} >> {} {}", &self_addr, &credit_addr, &amount);
- let array: [u8; 32] = wtid[..32].try_into()?;
- let wtid = Base32::from(array);
- let row_id = lock.len() as u64 + 1;
- lock.push(OutgoingTransaction {
- row_id: SafeUint64::try_from(row_id)?,
- date,
- amount,
- debit_account: btc_payto_url(&self_addr),
- credit_account: btc_payto_url(&credit_addr),
- wtid,
- exchange_base_url,
- });
- }
- Err(err) => match err {
- GetOpReturnErr::MissingOpReturn => {} // ignore
- err => println!("send: {} {}", id, err),
- },
- },
- Category::Receive => match rpc.get_tx_segwit_key(&id) {
- Ok((full, reserve_pub)) => {
- let debit_addr = sender_address(&rpc, &full)?;
- let credit_addr = full.tx.details[0].address.as_ref().unwrap();
- let time = full.tx.info.blocktime.unwrap();
- let date = Timestamp::from(
- SystemTime::UNIX_EPOCH + Duration::from_secs(time),
- );
- let amount: Amount = full.tx.amount.to_unsigned().unwrap().into();
- dbg!(full.tx.amount.to_unsigned(), amount);
- let amount = full.tx.amount.to_unsigned()?.into();
- let mut lock = state.incoming.blocking_lock();
- println!("{} << {} {}", &debit_addr, &credit_addr, &amount);
- let row_id = lock.len() as u64 + 1;
- lock.push(IncomingTransaction {
- row_id: SafeUint64::try_from(row_id)?,
- date,
- amount,
- reserve_pub: reserve_pub.into(),
- debit_account: btc_payto_url(&debit_addr),
- credit_account: btc_payto_url(credit_addr),
- });
- }
- Err(err) => match err {
- GetSegwitErr::Decode(
- DecodeSegWitErr::MissingSegWitAddress
- | DecodeSegWitErr::NoMagicIdMatch,
- ) => {}
- err => println!("receive: {} {}", id, err),
- },
- },
- Category::Generate | Category::Immature | Category::Orphan => {}
- }
- }
- println!("Wait for block");
- rpc.wait_for_new_block(0).ok();
- }
- })();
- dbg!(result).unwrap();
+ tokio::spawn(async move {
+ if let Err(e) = connection.await {
+ eprintln!("connection error: {}", e);
+ }
});
-
+ let state = ServerState { client };
+ let state: &'static ServerState = Box::leak(Box::new(state));
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 {
@@ -273,29 +84,8 @@ async fn main() {
}
}
-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,
- exchange_base_url: Url,
-}
-
struct ServerState {
- incoming: Mutex<Vec<IncomingTransaction>>,
- outgoing: Mutex<Vec<OutgoingTransaction>>,
- client: Mutex<Client>,
+ client: Client,
}
pub mod api_common;
@@ -352,30 +142,30 @@ async fn router(
"/transfer" => {
assert_method(&parts, Method::POST)?;
let request: TransferRequest = parse_json(&parts, body).await;
- let client = state.client.lock().await;
- let address = btc_payto_addr(&request.credit_account)?;
- let to = Address::from_str(address).map_err(|_| {
- (
+ if !check_pay_to(&request.credit_account) {
+ return Err((
StatusCode::BAD_REQUEST,
- ErrorCode::GENERIC_PARAMETER_MALFORMED,
- )
- })?;
- let amount: BtcAmount = request.amount.try_into().map_err(|_| {
- (
+ ErrorCode::GENERIC_PAYTO_URI_MALFORMED,
+ ));
+ }
+ if request.amount.currency != "BTC" {
+ return Err((
StatusCode::BAD_REQUEST,
ErrorCode::GENERIC_PARAMETER_MALFORMED,
- )
- })?;
- let metadata = encode_info(&request.wtid, &request.exchange_base_url);
- client
- .send_op_return(&to, amount, &metadata)
- .map_err(|_| (StatusCode::INTERNAL_SERVER_ERROR, ErrorCode::INVALID))?;
+ ));
+ }
let timestamp = Timestamp::now();
+ let row = state.client.query_one("INSERT INTO tx_out (_date, amount, wtid, debit_acc, credit_acc, exchange_url, status) VALUES (now(), $1, $2, $3, $4, $5, $6) RETURNING id", &[
+ &request.amount.to_string(), &request.wtid.as_ref(), &"payto://bitcoin/bcrt1qgkgxkjj27g3f7s87mcvjjsghay7gh34cx39prj", &request.credit_account.to_string(), &request.exchange_base_url.to_string(), &0i16
+ ]).await.unwrap();
json_response(
StatusCode::OK,
&TransferResponse {
timestamp,
- row_id: SafeUint64::try_from(0).unwrap(),
+ row_id: {
+ let id: i32 = row.get(0);
+ SafeUint64::try_from(id as u64).unwrap()
+ },
},
)
.await
@@ -384,16 +174,29 @@ async fn router(
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(),
+ let transactions = state
+ .client
+ .query(
+ "SELECT id, _date, amount, reserve_pub, debit_acc, credit_acc FROM tx_in",
+ &[],
+ )
+ .await
+ .unwrap()
+ .into_iter()
+ .map(|row| IncomingBankTransaction::IncomingReserveTransaction {
+ row_id: {
+ let id: i32 = row.get(0);
+ SafeUint64::try_from(id as u64).unwrap()
+ },
+ date: Timestamp::Time(row.get(1)),
+ amount: Amount::from_str(row.get(2)).unwrap(),
+ reserve_pub: {
+ let slice: &[u8] = row.get(3);
+ let array: [u8; 32] = slice.try_into().unwrap();
+ ShortHashCode::from(array)
+ },
+ debit_account: Url::parse(row.get(4)).unwrap(),
+ credit_account: Url::parse(row.get(5)).unwrap(),
})
.collect();
json_response(
@@ -408,17 +211,31 @@ async fn router(
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: tx.exchange_base_url.clone(),
+
+ let transactions = state
+ .client
+ .query(
+ "SELECT id, _date, amount, wtid, debit_acc, credit_acc, exchange_url FROM tx_out",
+ &[],
+ )
+ .await
+ .unwrap()
+ .into_iter()
+ .map(|row| OutgoingBankTransaction {
+ row_id: {
+ let id: i32 = row.get(0);
+ SafeUint64::try_from(id as u64).unwrap()
+ },
+ date: Timestamp::Time(row.get(1)),
+ amount: Amount::from_str(row.get(2)).unwrap(),
+ wtid: {
+ let slice : &[u8] = row.get(3);
+ let array: [u8; 32] = slice.try_into().unwrap();
+ ShortHashCode::from(array)
+ },
+ debit_account: Url::parse(row.get(4)).unwrap(),
+ credit_account: Url::parse(row.get(5)).unwrap(),
+ exchange_base_url: Url::parse(row.get(6)).unwrap(),
})
.collect();
json_response(