WIP: Replace bitcoind wallet with bdk wallet

Remove bitcoin-harness.
Create and pair bitcoind and electrs testcontainers using the same docker network and volume.
Happy path passing.
This commit is contained in:
rishflab 2021-02-08 15:53:05 +11:00
parent 24de0dcda3
commit 123e0cc69b
14 changed files with 864 additions and 231 deletions

294
Cargo.lock generated
View File

@ -66,7 +66,7 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -75,7 +75,7 @@ version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -132,7 +132,7 @@ dependencies = [
"polling",
"vec-arena",
"waker-fn",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -163,11 +163,11 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb4401f0a3622dad2e0763fa79e0eb328bc70fb7dccfdd645341f00d671247d6"
dependencies = [
"bytes",
"bytes 1.0.1",
"futures-sink",
"futures-util",
"memchr",
"pin-project-lite",
"pin-project-lite 0.2.4",
]
[[package]]
@ -187,7 +187,7 @@ checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -212,7 +212,7 @@ dependencies = [
"instant",
"pin-project 1.0.4",
"rand 0.8.2",
"tokio",
"tokio 1.0.2",
]
[[package]]
@ -230,6 +230,15 @@ dependencies = [
"keccak-hash 0.1.2",
]
[[package]]
name = "base64"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e"
dependencies = [
"byteorder",
]
[[package]]
name = "base64"
version = "0.12.3"
@ -242,6 +251,37 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "bdk"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2fd4c84e2baef750794e7c3f317e37c0c611ef7b29c9a9f18c7e51940dbfdb5"
dependencies = [
"async-trait",
"bdk-macros",
"bitcoin",
"electrum-client",
"js-sys",
"log",
"miniscript",
"rand 0.7.3",
"serde",
"serde_json",
"sled",
"tokio 0.2.25",
]
[[package]]
name = "bdk-macros"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f62874901df222eb0fc3bad6e425bc2a935287b8110be0d1ad6d729af86cf6e1"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "bech32"
version = "0.7.2"
@ -283,7 +323,7 @@ dependencies = [
"serde_json",
"testcontainers",
"thiserror",
"tokio",
"tokio 1.0.2",
"tracing",
"url",
]
@ -428,6 +468,12 @@ version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b"
[[package]]
name = "bytes"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4cec68f03f32e44924783795810fa50a7035d8c8ebe78580ad7e6c703fba38"
[[package]]
name = "bytes"
version = "1.0.1"
@ -442,9 +488,9 @@ checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba"
[[package]]
name = "cc"
version = "1.0.66"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c0496836a84f8d0495758516b8621a622beb77c0fed418570e50764093ced48"
checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40"
[[package]]
name = "cfg-if"
@ -757,7 +803,7 @@ checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
dependencies = [
"libc",
"redox_users",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -806,6 +852,22 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "electrum-client"
version = "0.5.0-beta.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aedfb48f66ab17ba3b2c69f8ff32f68d8b5dbc7839c0ca4e94237b835ca608dd"
dependencies = [
"bitcoin",
"log",
"rustls",
"serde",
"serde_json",
"socks",
"webpki",
"webpki-roots",
]
[[package]]
name = "encode_unicode"
version = "0.3.6"
@ -952,7 +1014,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
dependencies = [
"libc",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1021,7 +1083,7 @@ dependencies = [
"futures-io",
"memchr",
"parking",
"pin-project-lite",
"pin-project-lite 0.2.4",
"waker-fn",
]
@ -1071,7 +1133,7 @@ dependencies = [
"futures-sink",
"futures-task",
"memchr",
"pin-project-lite",
"pin-project-lite 0.2.4",
"pin-utils",
"proc-macro-hack",
"proc-macro-nested",
@ -1119,8 +1181,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if 1.0.0",
"js-sys",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
@ -1150,7 +1214,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b67e66362108efccd8ac053abafc8b7a8d86a37e6e48fc4f6f7485eb5e9e6a5"
dependencies = [
"bytes",
"bytes 1.0.1",
"fnv",
"futures-core",
"futures-sink",
@ -1158,7 +1222,7 @@ dependencies = [
"http",
"indexmap",
"slab",
"tokio",
"tokio 1.0.2",
"tokio-util",
"tracing",
"tracing-futures",
@ -1185,7 +1249,7 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1274,7 +1338,7 @@ version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7245cd7449cc792608c3c8a9eaf69bd4eabbabf802713748fd739c98b82f0747"
dependencies = [
"bytes",
"bytes 1.0.1",
"fnv",
"itoa",
]
@ -1285,7 +1349,7 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2861bd27ee074e5ee891e8b539837a9430012e249d7f0ca2d795650f579c1994"
dependencies = [
"bytes",
"bytes 1.0.1",
"http",
]
@ -1307,7 +1371,7 @@ version = "0.14.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12219dc884514cb4a6a03737f4413c0e01c23a1b059b0156004b23f1e19dccbe"
dependencies = [
"bytes",
"bytes 1.0.1",
"futures-channel",
"futures-core",
"futures-util",
@ -1319,7 +1383,7 @@ dependencies = [
"itoa",
"pin-project 1.0.4",
"socket2",
"tokio",
"tokio 1.0.2",
"tower-service",
"tracing",
"want",
@ -1331,10 +1395,10 @@ version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905"
dependencies = [
"bytes",
"bytes 1.0.1",
"hyper",
"native-tls",
"tokio",
"tokio 1.0.2",
"tokio-native-tls",
]
@ -1357,7 +1421,7 @@ checksum = "28538916eb3f3976311f5dfbe67b5362d0add1293d0a9cad17debf86f8e3aa48"
dependencies = [
"if-addrs-sys",
"libc",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1383,7 +1447,7 @@ dependencies = [
"ipnet",
"libc",
"log",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1505,7 +1569,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d5133112ce42be9482f6a87be92a605dd6bbc9e93c297aee77d172ff06908f3a"
dependencies = [
"atomic",
"bytes",
"bytes 1.0.1",
"futures",
"lazy_static",
"libp2p-core",
@ -1586,7 +1650,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2705dc94b01ab9e3779b42a09bbf3712e637ed213e875c30face247291a85af0"
dependencies = [
"asynchronous-codec",
"bytes",
"bytes 1.0.1",
"futures",
"libp2p-core",
"log",
@ -1603,7 +1667,7 @@ version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4aca322b52a0c5136142a7c3971446fb1e9964923a526c9cc6ef3b7c94e57778"
dependencies = [
"bytes",
"bytes 1.0.1",
"curve25519-dalek 3.0.2",
"futures",
"lazy_static",
@ -1626,7 +1690,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d37637a4b33b5390322ccc068a33897d0aa541daf4fec99f6a7efbf37295346e"
dependencies = [
"async-trait",
"bytes",
"bytes 1.0.1",
"futures",
"libp2p-core",
"libp2p-swarm",
@ -1671,7 +1735,7 @@ dependencies = [
"libp2p-core",
"log",
"socket2",
"tokio",
"tokio 1.0.2",
]
[[package]]
@ -1818,7 +1882,7 @@ dependencies = [
"log",
"miow",
"ntapi",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1828,7 +1892,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
dependencies = [
"socket2",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1862,7 +1926,7 @@ dependencies = [
"serde_json",
"spectral",
"testcontainers",
"tokio",
"tokio 1.0.2",
"tracing",
"tracing-log",
"tracing-subscriber",
@ -1908,7 +1972,7 @@ version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10ddc0eb0117736f19d556355464fc87efc8ad98b29e3fd84f02531eb6e90840"
dependencies = [
"bytes",
"bytes 1.0.1",
"futures",
"log",
"pin-project 1.0.4",
@ -1941,7 +2005,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8123a81538e457d44b933a02faf885d3fe8408806b23fa700e8f01c6c3a98998"
dependencies = [
"libc",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -1956,7 +2020,7 @@ version = "0.3.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -2156,7 +2220,7 @@ dependencies = [
"libc",
"redox_syscall 0.1.57",
"smallvec",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -2226,6 +2290,12 @@ dependencies = [
"syn",
]
[[package]]
name = "pin-project-lite"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c917123afa01924fc84bb20c4c03f004d9c38e5127e3c039bbf7f4b9c76a2f6b"
[[package]]
name = "pin-project-lite"
version = "0.2.4"
@ -2254,7 +2324,7 @@ dependencies = [
"libc",
"log",
"wepoll-sys",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -2375,7 +2445,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e6984d2f1a23009bd270b8bb56d0926810a3d483f59c987d77969e9d8e840b2"
dependencies = [
"bytes",
"bytes 1.0.1",
"prost-derive",
]
@ -2385,7 +2455,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32d3ebd75ac2679c2af3a92246639f9fcc8a442ee420719cc4fe195b98dd5fa3"
dependencies = [
"bytes",
"bytes 1.0.1",
"heck",
"itertools",
"log",
@ -2416,7 +2486,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b518d7cdd93dab1d1122cf07fa9a60771836c668dde9d9e2a139f957f0d9f1bb"
dependencies = [
"bytes",
"bytes 1.0.1",
"prost",
]
@ -2445,7 +2515,7 @@ dependencies = [
"libc",
"rand_core 0.3.1",
"rdrand",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -2458,7 +2528,7 @@ dependencies = [
"fuchsia-cprng",
"libc",
"rand_core 0.3.1",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -2477,7 +2547,7 @@ dependencies = [
"rand_os",
"rand_pcg",
"rand_xorshift",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -2612,7 +2682,7 @@ checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
dependencies = [
"libc",
"rand_core 0.4.2",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -2626,7 +2696,7 @@ dependencies = [
"libc",
"rand_core 0.4.2",
"rdrand",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -2714,7 +2784,7 @@ version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -2724,7 +2794,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd281b1030aa675fb90aa994d07187645bb3c8fc756ca766e7c3070b439de9de"
dependencies = [
"base64 0.13.0",
"bytes",
"bytes 1.0.1",
"encoding_rs",
"futures-core",
"futures-util",
@ -2739,11 +2809,11 @@ dependencies = [
"mime",
"native-tls",
"percent-encoding",
"pin-project-lite",
"pin-project-lite 0.2.4",
"serde",
"serde_json",
"serde_urlencoded",
"tokio",
"tokio 1.0.2",
"tokio-native-tls",
"url",
"wasm-bindgen",
@ -2764,7 +2834,7 @@ dependencies = [
"spin",
"untrusted",
"web-sys",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -2810,6 +2880,19 @@ dependencies = [
"semver",
]
[[package]]
name = "rustls"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e"
dependencies = [
"base64 0.10.1",
"log",
"ring",
"sct",
"webpki",
]
[[package]]
name = "rw-stream-sink"
version = "0.2.1"
@ -2834,7 +2917,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
dependencies = [
"lazy_static",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -2843,6 +2926,16 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "sct"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e3042af939fca8c3453b7af0f1c66e533a15a86169e39de2657310ade8f98d3c"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "secp256k1"
version = "0.19.0"
@ -3095,7 +3188,19 @@ checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e"
dependencies = [
"cfg-if 1.0.0",
"libc",
"winapi",
"winapi 0.3.9",
]
[[package]]
name = "socks"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30f86c7635fadf2814201a4f67efefb0007588ae7422ce299f354ab5c97f61ae"
dependencies = [
"byteorder",
"libc",
"winapi 0.2.8",
"ws2_32-sys",
]
[[package]]
@ -3266,6 +3371,7 @@ dependencies = [
"atty",
"backoff",
"base64 0.12.3",
"bdk",
"bitcoin",
"bitcoin-harness",
"conquer-once",
@ -3301,7 +3407,7 @@ dependencies = [
"testcontainers",
"thiserror",
"time",
"tokio",
"tokio 1.0.2",
"tracing",
"tracing-core",
"tracing-futures",
@ -3346,7 +3452,7 @@ dependencies = [
"rand 0.8.2",
"redox_syscall 0.2.4",
"remove_dir_all",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -3357,7 +3463,7 @@ checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
dependencies = [
"byteorder",
"dirs",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -3426,7 +3532,7 @@ dependencies = [
"stdweb",
"time-macros",
"version_check",
"winapi",
"winapi 0.3.9",
]
[[package]]
@ -3476,6 +3582,17 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
[[package]]
name = "tokio"
version = "0.2.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6703a273949a90131b290be1fe7b039d0fc884aa1935860dfcbe056f28cd8092"
dependencies = [
"bytes 0.5.6",
"pin-project-lite 0.1.11",
"slab",
]
[[package]]
name = "tokio"
version = "1.0.2"
@ -3483,12 +3600,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ca04cec6ff2474c638057b65798f60ac183e5e79d3448bb7163d36a39cff6ec"
dependencies = [
"autocfg 1.0.1",
"bytes",
"bytes 1.0.1",
"libc",
"memchr",
"mio",
"num_cpus",
"pin-project-lite",
"pin-project-lite 0.2.4",
"tokio-macros",
]
@ -3510,7 +3627,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
dependencies = [
"native-tls",
"tokio",
"tokio 1.0.2",
]
[[package]]
@ -3520,8 +3637,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "76066865172052eb8796c686f0b441a93df8b08d40a950b062ffb9a426f00edd"
dependencies = [
"futures-core",
"pin-project-lite",
"tokio",
"pin-project-lite 0.2.4",
"tokio 1.0.2",
]
[[package]]
@ -3530,12 +3647,12 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ae4751faa60b9f96dd8344d74592e5a17c0c9a220413dbc6942d14139bbfcc"
dependencies = [
"bytes",
"bytes 1.0.1",
"futures-core",
"futures-sink",
"log",
"pin-project-lite",
"tokio",
"pin-project-lite 0.2.4",
"tokio 1.0.2",
"tokio-stream",
]
@ -3561,7 +3678,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f47026cdc4080c07e49b37087de021820269d996f581aac150ef9e5583eefe3"
dependencies = [
"cfg-if 1.0.0",
"pin-project-lite",
"pin-project-lite 0.2.4",
"tracing-attributes",
"tracing-core",
]
@ -3720,7 +3837,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "35581ff83d4101e58b582e607120c7f5ffb17e632a980b1f38334d76b36908b2"
dependencies = [
"asynchronous-codec",
"bytes",
"bytes 1.0.1",
"futures-io",
"futures-util",
]
@ -3904,6 +4021,25 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "webpki"
version = "0.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8e38c0608262c46d4a56202ebabdeb094cef7e560ca7a226c6bf055188aa4ea"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "webpki-roots"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8eff4b7516a57307f9349c64bf34caa34b940b66fed4b2fb3136cb7386e5739"
dependencies = [
"webpki",
]
[[package]]
name = "wepoll-sys"
version = "3.0.1"
@ -3923,6 +4059,12 @@ dependencies = [
"thiserror",
]
[[package]]
name = "winapi"
version = "0.2.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
[[package]]
name = "winapi"
version = "0.3.9"
@ -3933,6 +4075,12 @@ dependencies = [
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-build"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
@ -3951,7 +4099,17 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
dependencies = [
"winapi",
"winapi 0.3.9",
]
[[package]]
name = "ws2_32-sys"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
dependencies = [
"winapi 0.2.8",
"winapi-build",
]
[[package]]

View File

@ -1 +1 @@
nightly-2020-08-13
nightly-2021-02-01

View File

@ -14,6 +14,7 @@ backoff = { git = "https://github.com/ihrwein/backoff", rev = "9d03992a83dfdc596
base64 = "0.12"
bitcoin = { version = "0.25", features = ["rand", "use-serde"] }
bitcoin-harness = { git = "https://github.com/coblox/bitcoin-harness-rs", rev = "ae2f6cd547496e680941c0910018bbe884128799" }
bdk = { version = "0.3"}
conquer-once = "0.3"
cross-curve-dleq = { git = "https://github.com/comit-network/cross-curve-dleq", rev = "eddcdea1d1f16fa33ef581d1744014ece535c920", features = ["serde"] }
curve25519-dalek = "2"

View File

@ -231,7 +231,6 @@ pub trait GetRawTransaction {
async fn get_raw_transaction(&self, txid: Txid) -> Result<Transaction>;
}
#[async_trait]
pub trait GetNetwork {
fn get_network(&self) -> Network;
}

View File

@ -1,4 +1,4 @@
use std::ops::Add;
use std::ops::{Add, Sub};
use serde::{Deserialize, Serialize};
@ -49,6 +49,14 @@ impl Add<Timelock> for BlockHeight {
}
}
impl Sub<BlockHeight> for BlockHeight {
type Output = BlockHeight;
fn sub(self, rhs: BlockHeight) -> Self::Output {
BlockHeight(self.0 - rhs.0)
}
}
#[derive(Debug, Clone, Copy)]
pub enum ExpiredTimelocks {
None,

View File

@ -14,7 +14,7 @@ use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct TxLock {
inner: Transaction,
inner: PartiallySignedTransaction,
output_descriptor: Descriptor<::bitcoin::PublicKey>,
}
@ -31,35 +31,33 @@ impl TxLock {
// We construct a psbt for convenience
let psbt = wallet.build_tx_lock_psbt(address, amount).await?;
// We don't take advantage of psbt functionality yet, instead we convert to a
// raw transaction
let inner = psbt.extract_tx();
Ok(Self {
inner,
inner: psbt,
output_descriptor: lock_output_descriptor,
})
}
pub fn lock_amount(&self) -> Amount {
Amount::from_sat(self.inner.output[self.lock_output_vout()].value)
Amount::from_sat(self.inner.clone().extract_tx().output[self.lock_output_vout()].value)
}
pub fn txid(&self) -> Txid {
self.inner.txid()
self.inner.clone().extract_tx().txid()
}
pub fn as_outpoint(&self) -> OutPoint {
// This is fine because a transaction that has that many outputs is not
// realistic
#[allow(clippy::cast_possible_truncation)]
OutPoint::new(self.inner.txid(), self.lock_output_vout() as u32)
OutPoint::new(self.txid(), self.lock_output_vout() as u32)
}
/// Retreive the index of the locked output in the transaction outputs
/// vector
fn lock_output_vout(&self) -> usize {
self.inner
.clone()
.extract_tx()
.output
.iter()
.position(|output| {
@ -83,7 +81,7 @@ impl TxLock {
};
let tx_out = TxOut {
value: self.inner.output[self.lock_output_vout()].value - TX_FEE,
value: self.inner.clone().extract_tx().output[self.lock_output_vout()].value - TX_FEE,
script_pubkey: spend_address.script_pubkey(),
};
@ -98,7 +96,7 @@ impl TxLock {
impl From<TxLock> for PartiallySignedTransaction {
fn from(from: TxLock) -> Self {
PartiallySignedTransaction::from_unsigned_tx(from.inner).expect("to be unsigned")
from.inner
}
}

View File

@ -7,55 +7,93 @@ use crate::{
config::Config,
};
use ::bitcoin::{util::psbt::PartiallySignedTransaction, Txid};
use anyhow::{Context, Result};
use anyhow::{anyhow, Result};
use async_trait::async_trait;
use backoff::{backoff::Constant as ConstantBackoff, tokio::retry};
use bitcoin_harness::{bitcoind_rpc::PsbtBase64, BitcoindRpcApi};
use reqwest::Url;
use std::time::Duration;
use tokio::time::interval;
use bdk::{
blockchain::{noop_progress, Blockchain, ElectrumBlockchain},
electrum_client::{Client, ElectrumApi},
keys::GeneratableDefaultOptions,
FeeRate,
};
use reqwest::{Method, Url};
use serde::{Deserialize, Serialize};
use std::{path::Path, sync::Arc, time::Duration};
use tokio::{sync::Mutex, time::interval};
use tracing::debug;
#[derive(Debug)]
pub struct Wallet {
pub inner: bitcoin_harness::Wallet,
pub inner: Arc<Mutex<bdk::Wallet<ElectrumBlockchain, bdk::sled::Tree>>>,
pub network: bitcoin::Network,
pub http_url: Url,
pub rpc_url: Url,
}
impl Wallet {
pub async fn new(name: &str, url: Url, network: bitcoin::Network) -> Result<Self> {
let wallet = bitcoin_harness::Wallet::new(name, url).await?;
pub async fn new(
_name: &str,
rpc_url: Url,
http_url: Url,
network: bitcoin::Network,
datadir: &Path,
) -> Result<Self> {
let client = Client::new(rpc_url.as_str()).expect("Failed to init electrum rpc client");
let db = bdk::sled::open(datadir).expect("could not open sled db");
let db = db.open_tree("default_tree").expect("could not open tree");
// todo: make key generation configurable using a descriptor
let p_key = ::bitcoin::PrivateKey::generate_default()?;
let bdk_wallet = bdk::Wallet::new(
bdk::template::P2WPKH(p_key),
None,
network,
db,
ElectrumBlockchain::from(client),
)?;
Ok(Self {
inner: wallet,
inner: Arc::new(Mutex::new(bdk_wallet)),
network,
http_url,
rpc_url,
})
}
pub async fn balance(&self) -> Result<Amount> {
let balance = self.inner.balance().await?;
Ok(balance)
self.inner.lock().await.sync(noop_progress(), None)?;
let balance = self.inner.lock().await.get_balance()?;
Ok(Amount::from_sat(balance))
}
pub async fn new_address(&self) -> Result<Address> {
self.inner.new_address().await.map_err(Into::into)
self.inner
.lock()
.await
.get_new_address()
.map_err(Into::into)
}
pub async fn get_tx(&self, txid: Txid) -> Result<Option<Transaction>> {
let tx = self.inner.lock().await.client().get_tx(&txid)?;
Ok(tx)
}
pub async fn transaction_fee(&self, txid: Txid) -> Result<Amount> {
let fee = self
self.inner.lock().await.sync(noop_progress(), None)?;
let fees = self
.inner
.get_wallet_transaction(txid)
.lock()
.await
.map(|res| {
res.fee.map(|signed_amount| {
signed_amount
.abs()
.to_unsigned()
.expect("Absolute value is always positive")
})
.list_transactions(true)?
.iter()
.find(|tx| tx.txid == txid)
.ok_or_else(|| {
anyhow!("Could not find tx in bdk wallet when trying to determine fees")
})?
.context("Rpc response did not contain a fee")?;
.fees;
Ok(fee)
Ok(Amount::from_sat(fees))
}
}
@ -66,11 +104,19 @@ impl BuildTxLockPsbt for Wallet {
output_address: Address,
output_amount: Amount,
) -> Result<PartiallySignedTransaction> {
let psbt = self.inner.fund_psbt(output_address, output_amount).await?;
let as_hex = base64::decode(psbt)?;
let psbt = bitcoin::consensus::deserialize(&as_hex)?;
debug!("syncing wallet");
self.inner.lock().await.sync(noop_progress(), None)?;
debug!("building tx lock");
let (psbt, _details) = self.inner.lock().await.create_tx(
bdk::TxBuilder::with_recipients(vec![(
output_address.script_pubkey(),
output_amount.as_sat(),
)])
// todo: get actual fee
.fee_rate(FeeRate::from_sat_per_vb(5.0)),
)?;
debug!("tx lock built");
Ok(psbt)
}
}
@ -78,22 +124,15 @@ impl BuildTxLockPsbt for Wallet {
#[async_trait]
impl SignTxLock for Wallet {
async fn sign_tx_lock(&self, tx_lock: TxLock) -> Result<Transaction> {
debug!("syncing wallet");
self.inner.lock().await.sync(noop_progress(), None)?;
debug!("signing tx lock");
let psbt = PartiallySignedTransaction::from(tx_lock);
let psbt = bitcoin::consensus::serialize(&psbt);
let as_base64 = base64::encode(psbt);
let psbt = self
.inner
.wallet_process_psbt(PsbtBase64(as_base64))
.await?;
let PsbtBase64(signed_psbt) = PsbtBase64::from(psbt);
let as_hex = base64::decode(signed_psbt)?;
let psbt: PartiallySignedTransaction = bitcoin::consensus::deserialize(&as_hex)?;
let tx = psbt.extract_tx();
let (signed_psbt, finalized) = self.inner.lock().await.sign(psbt, None)?;
assert!(finalized);
let tx = signed_psbt.extract_tx();
debug!("signed tx lock");
Ok(tx)
}
}
@ -101,19 +140,24 @@ impl SignTxLock for Wallet {
#[async_trait]
impl BroadcastSignedTransaction for Wallet {
async fn broadcast_signed_transaction(&self, transaction: Transaction) -> Result<Txid> {
let txid = self.inner.send_raw_transaction(transaction).await?;
tracing::info!("Bitcoin tx broadcasted! TXID = {}", txid);
Ok(txid)
debug!("attempting to broadcast tx");
self.inner.lock().await.broadcast(transaction.clone())?;
tracing::info!("Bitcoin tx broadcasted! TXID = {}", transaction.txid());
Ok(transaction.txid())
}
}
// TODO: For retry, use `backoff::ExponentialBackoff` in production as opposed
// to `ConstantBackoff`.
#[async_trait]
impl WatchForRawTransaction for Wallet {
async fn watch_for_raw_transaction(&self, txid: Txid) -> Transaction {
// debug!("syncing wallet");
// self.inner.lock().await.sync(noop_progress(), None).unwrap();
debug!("watching for tx: {}", txid);
retry(ConstantBackoff::new(Duration::from_secs(1)), || async {
Ok(self.inner.get_raw_transaction(txid).await?)
let client = Client::new(self.rpc_url.as_ref())?;
let tx = client.transaction_get(&txid)?;
debug!("found tx: {}", txid);
Ok(tx)
})
.await
.expect("transient errors to be retried")
@ -122,9 +166,10 @@ impl WatchForRawTransaction for Wallet {
#[async_trait]
impl GetRawTransaction for Wallet {
// todo: potentially replace with option
async fn get_raw_transaction(&self, txid: Txid) -> Result<Transaction> {
Ok(self.inner.get_raw_transaction(txid).await?)
self.get_tx(txid)
.await?
.ok_or_else(|| anyhow!("Could not get raw tx with id: {}", txid))
}
}
@ -132,7 +177,7 @@ impl GetRawTransaction for Wallet {
impl GetBlockHeight for Wallet {
async fn get_block_height(&self) -> BlockHeight {
let height = retry(ConstantBackoff::new(Duration::from_secs(1)), || async {
Ok(self.inner.client.getblockcount().await?)
Ok(self.inner.lock().await.client().get_height()?)
})
.await
.expect("transient errors to be retried");
@ -144,21 +189,39 @@ impl GetBlockHeight for Wallet {
#[async_trait]
impl TransactionBlockHeight for Wallet {
async fn transaction_block_height(&self, txid: Txid) -> BlockHeight {
#[derive(Serialize, Deserialize, Debug, Copy, Clone)]
struct TransactionStatus {
block_height: Option<u32>,
}
#[derive(Debug)]
enum Error {
Io,
NotYetMined,
JsonDeserialisation,
UrlDeserialisation,
}
let height = retry(ConstantBackoff::new(Duration::from_secs(1)), || async {
let block_height = self
.inner
.transaction_block_height(txid)
let path = &format!("/tx/{}/status", txid);
let url = self
.http_url
.clone()
.join(path)
.map_err(|_| backoff::Error::Transient(Error::UrlDeserialisation))?;
let resp = reqwest::Client::new()
.request(Method::GET, url)
.send()
.await
.map_err(|_| backoff::Error::Transient(Error::Io))?;
let block_height =
block_height.ok_or_else(|| backoff::Error::Transient(Error::NotYetMined))?;
let tx_status: TransactionStatus = resp
.json()
.await
.map_err(|_| backoff::Error::Permanent(Error::JsonDeserialisation))?;
let block_height = tx_status
.block_height
.ok_or_else(|| backoff::Error::Transient(Error::NotYetMined))?;
Result::<_, backoff::Error<Error>>::Ok(block_height)
})
@ -172,18 +235,22 @@ impl TransactionBlockHeight for Wallet {
#[async_trait]
impl WaitForTransactionFinality for Wallet {
async fn wait_for_transaction_finality(&self, txid: Txid, config: Config) -> Result<()> {
// TODO(Franck): This assumes that bitcoind runs with txindex=1
debug!("waiting for tx finality: {}", txid);
// TODO(Franck): This assumes that bitcoind runs with txindex=1
// Divide by 4 to not check too often yet still be aware of the new block early
// on.
let mut interval = interval(config.bitcoin_avg_block_time / 4);
loop {
let tx = self.inner.client.get_raw_transaction_verbose(txid).await?;
if let Some(confirmations) = tx.confirmations {
if confirmations >= config.bitcoin_finality_confirmations {
break;
}
debug!("syncing wallet");
self.inner.lock().await.sync(noop_progress(), None)?;
let tx_block_height = self.transaction_block_height(txid).await;
let block_height = self.get_block_height().await;
let confirmations = block_height - tx_block_height;
debug!("confirmations: {:?}", confirmations);
if confirmations >= BlockHeight::new(config.bitcoin_finality_confirmations) {
break;
}
interval.tick().await;
}
@ -192,7 +259,10 @@ impl WaitForTransactionFinality for Wallet {
}
}
#[async_trait]
impl GetNetwork for Wallet {
// todo: Get this from the electrum and instead of storing it in the Wallet
// struct
fn get_network(&self) -> bitcoin::Network {
self.network
}

View File

@ -232,8 +232,16 @@ async fn setup_wallets(
monero_wallet_rpc_url: url::Url,
config: Config,
) -> Result<(bitcoin::Wallet, monero::Wallet)> {
let bitcoin_wallet =
bitcoin::Wallet::new(bitcoin_wallet_name, bitcoind_url, config.bitcoin_network).await?;
// todo: use electrum rpc url for the rpc url
// todo: pass datadir path as arg
let bitcoin_wallet = bitcoin::Wallet::new(
bitcoin_wallet_name,
bitcoind_url.clone(),
bitcoind_url,
config.bitcoin_network,
unimplemented!(),
)
.await?;
let bitcoin_balance = bitcoin_wallet.balance().await?;
info!(
"Connection to Bitcoin wallet succeeded, balance: {}",

View File

@ -216,6 +216,7 @@ impl Builder {
}
}
#[allow(clippy::large_enum_variant)]
#[derive(Debug)]
pub enum OutEvent {
ConnectionEstablished(PeerId),

View File

@ -7,7 +7,6 @@ use crate::{
TxRefund, WatchForRawTransaction,
},
monero,
monero::CreateWalletForOutput,
protocol::{alice, alice::TransferProof, bob, bob::EncryptedSignature, SwapAmounts},
};
use anyhow::{anyhow, Context, Result};
@ -300,36 +299,6 @@ pub struct State3 {
}
impl State3 {
pub async fn watch_for_lock_btc<W>(self, bitcoin_wallet: &W) -> Result<State4>
where
W: bitcoin::WatchForRawTransaction,
{
tracing::info!("watching for lock btc with txid: {}", self.tx_lock.txid());
let tx = bitcoin_wallet
.watch_for_raw_transaction(self.tx_lock.txid())
.await;
tracing::info!("tx lock seen with txid: {}", tx.txid());
Ok(State4 {
a: self.a,
B: self.B,
s_a: self.s_a,
S_b_monero: self.S_b_monero,
S_b_bitcoin: self.S_b_bitcoin,
v: self.v,
xmr: self.xmr,
cancel_timelock: self.cancel_timelock,
punish_timelock: self.punish_timelock,
refund_address: self.refund_address,
redeem_address: self.redeem_address,
punish_address: self.punish_address,
tx_lock: self.tx_lock,
tx_punish_sig_bob: self.tx_punish_sig_bob,
tx_cancel_sig_bob: self.tx_cancel_sig_bob,
})
}
pub async fn wait_for_cancel_timelock_to_expire<W>(&self, bitcoin_wallet: &W) -> Result<()>
where
W: WatchForRawTransaction + TransactionBlockHeight + GetBlockHeight,
@ -497,41 +466,6 @@ impl State5 {
lock_xmr_fee: self.lock_xmr_fee,
}
}
// watch for refund on btc, recover s_b and refund xmr
pub async fn refund_xmr<B, M>(self, bitcoin_wallet: &B, monero_wallet: &M) -> Result<()>
where
B: WatchForRawTransaction,
M: CreateWalletForOutput,
{
let tx_cancel =
bitcoin::TxCancel::new(&self.tx_lock, self.cancel_timelock, self.a.public(), self.B);
let tx_refund = bitcoin::TxRefund::new(&tx_cancel, &self.refund_address);
let tx_refund_encsig = self.a.encsign(self.S_b_bitcoin, tx_refund.digest());
let tx_refund_candidate = bitcoin_wallet
.watch_for_raw_transaction(tx_refund.txid())
.await;
let tx_refund_sig =
tx_refund.extract_signature_by_key(tx_refund_candidate, self.a.public())?;
let s_b = bitcoin::recover(self.S_b_bitcoin, tx_refund_sig, tx_refund_encsig)?;
let s_b = monero::private_key_from_secp256k1_scalar(s_b.into());
let s = s_b.scalar + self.s_a.into_ed25519();
// TODO: Optimized rescan height should be passed for refund as well.
// NOTE: This actually generates and opens a new wallet, closing the currently
// open one.
monero_wallet
.create_and_load_wallet_for_output(monero::PrivateKey::from_scalar(s), self.v, None)
.await?;
Ok(())
}
}
#[derive(Clone, Debug, Deserialize, Serialize)]

View File

@ -1,8 +1,14 @@
pub mod testutils;
use swap::protocol::{alice, bob};
use testutils::SlowCancelConfig;
use testcontainers::clients::Cli;
use testutils::{init_tracing, SlowCancelConfig};
use tokio::join;
use futures::future;
use bdk::keys::GeneratableDefaultOptions;
use bdk::database::MemoryDatabase;
use bdk::blockchain::noop_progress;
use url::Url;
/// Run the following tests with RUST_MIN_STACK=10000000
@ -22,3 +28,52 @@ async fn happy_path() {
})
.await;
}
//
// const ELECTRUM_RPC_PORT: u16 = 60401;
//
// #[tokio::test]
// async fn happy_path() {
// let cli = Cli::default();
//
// let _guard = init_tracing();
//
// // let config = C::get_config();
//
// let _c = testutils::init_electrs_container(&cli).await;
//
//
// let bdk_url = {
// let input = format!("tcp://@localhost:{}", ELECTRUM_RPC_PORT);
// Url::parse(&input).unwrap()
// };
//
// let client = bdk::electrum_client::Client::new(bdk_url.as_str()).unwrap();
//
// let blockchain = bdk::blockchain::ElectrumBlockchain::from(client);
//
// // let bdk_wallet = bdk::Wallet::new(
// // "wpkh(tprv8ZgxMBicQKsPdpkqS7Eair4YxjcuuvDPNYmKX3sCniCf16tHEVrjjiSXEkFRnUH77yXc6ZcwHHcLNfjdi5qUvw3VDfgYiH5mNsj5izuiu2N/0/0/*)",
// // None,
// // Network::Regtest,
// // sled::open("/tmp/bdk").expect("could not create sled db").open_tree("default_tree").expect("could not open tree"),
// // blockchain).unwrap();
//
// let p_key = ::bitcoin::PrivateKey::generate_default().expect("could not generate priv key");
// let bdk_wallet = bdk::Wallet::new(
// bdk::template::P2WPKH(p_key),
// None,
// ::bitcoin::Network::Regtest,
// MemoryDatabase::default(),
// blockchain,
// ).unwrap();
//
//
// let address = bdk_wallet.get_new_address().unwrap();
// println!("funded address: {}", address);
//
// bdk_wallet.sync(noop_progress(), None).unwrap();
//
// let balance = bdk_wallet.get_balance().unwrap();
// assert_eq!(0, balance);
// //future::pending::<()>().await;
// }

View File

@ -0,0 +1,139 @@
use bitcoin::Network;
use std::collections::HashMap;
use testcontainers::{
core::{Container, Docker, Port, WaitForMessage},
Image,
};
#[derive(Debug)]
pub struct Bitcoind {
tag: String,
args: BitcoindArgs,
ports: Option<Vec<Port>>,
entrypoint: Option<String>,
volume: String,
}
impl Image for Bitcoind {
type Args = BitcoindArgs;
type EnvVars = HashMap<String, String>;
type Volumes = HashMap<String, String>;
type EntryPoint = str;
fn descriptor(&self) -> String {
format!("coblox/bitcoin-core:{}", self.tag)
}
fn wait_until_ready<D: Docker>(&self, container: &Container<'_, D, Self>) {
container
.logs()
.stdout
.wait_for_message(&"init message: Done loading")
.unwrap();
}
fn args(&self) -> <Self as Image>::Args {
self.args.clone()
}
fn volumes(&self) -> Self::Volumes {
let mut volumes = HashMap::new();
volumes.insert(self.volume.clone(), "/home/bdk-test".to_owned());
volumes
}
fn env_vars(&self) -> Self::EnvVars {
HashMap::new()
}
fn ports(&self) -> Option<Vec<Port>> {
self.ports.clone()
}
fn with_args(self, args: <Self as Image>::Args) -> Self {
Bitcoind { args, ..self }
}
fn with_entrypoint(self, entrypoint: &Self::EntryPoint) -> Self {
Self {
entrypoint: Some(entrypoint.to_string()),
..self
}
}
fn entrypoint(&self) -> Option<String> {
self.entrypoint.to_owned()
}
}
impl Default for Bitcoind {
fn default() -> Self {
Bitcoind {
tag: "v0.19.1".into(),
args: BitcoindArgs::default(),
ports: None,
entrypoint: Some("/usr/bin/bitcoind".into()),
volume: uuid::Uuid::new_v4().to_string(),
}
}
}
impl Bitcoind {
pub fn with_tag(self, tag_str: &str) -> Self {
Bitcoind {
tag: tag_str.to_string(),
..self
}
}
pub fn with_mapped_port<P: Into<Port>>(mut self, port: P) -> Self {
let mut ports = self.ports.unwrap_or_default();
ports.push(port.into());
self.ports = Some(ports);
self
}
pub fn with_volume(mut self, volume: String) -> Self {
self.volume = volume;
self
}
}
#[derive(Debug, Clone)]
pub struct BitcoindArgs;
/// Sane defaults for a mainnet regtest instance.
impl Default for BitcoindArgs {
fn default() -> Self {
BitcoindArgs
}
}
impl IntoIterator for BitcoindArgs {
type Item = String;
type IntoIter = ::std::vec::IntoIter<String>;
fn into_iter(self) -> <Self as IntoIterator>::IntoIter {
let mut args = Vec::new();
args.push("-server".to_string());
args.push("-regtest".to_string());
args.push("-txindex=1".to_string());
args.push("-listen=1".to_string());
args.push("-prune=0".to_string());
args.push("-rpcallowip=0.0.0.0/0".to_string());
args.push("-rpcbind=0.0.0.0".to_string());
args.push("-rpcuser=admin".to_string());
args.push("-rpcpassword=123".to_string());
args.push("-printtoconsole".to_string());
args.push("-rest".to_string());
args.push("-fallbackfee=0.0002".to_string());
args.push("-datadir=/home/bdk-test".to_string());
args.push("-rpcport=18443".to_string());
args.push("-port=18886".to_string());
args.push("-rest".to_string());
args.into_iter()
}
}

View File

@ -0,0 +1,164 @@
use bitcoin::Network;
use std::collections::HashMap;
use testcontainers::{
core::{Container, Docker, Port, WaitForMessage},
Image,
};
// pub const ELECTRS_CONTAINER_NAME: &str = "electrs";
// pub const ELECTRS_DEFAULT_NETWORK: &str = "electrs_network";
// pub const ELECTRS_RPC_PORT: u16 = 48081;
// pub const ELECTRS_HTTP_PORT: u16 = 48083;
#[derive(Debug)]
pub struct Electrs {
tag: String,
args: ElectrsArgs,
ports: Option<Vec<Port>>,
entrypoint: Option<String>,
wait_for_message: String,
volume: String,
bitcoind_container_name: String,
}
impl Image for Electrs {
type Args = ElectrsArgs;
type EnvVars = HashMap<String, String>;
type Volumes = HashMap<String, String>;
type EntryPoint = str;
fn descriptor(&self) -> String {
format!("vulpemventures/electrs:{}", self.tag)
}
fn wait_until_ready<D: Docker>(&self, container: &Container<'_, D, Self>) {
container
.logs()
.stderr
.wait_for_message(&self.wait_for_message)
.unwrap();
}
fn args(&self) -> <Self as Image>::Args {
self.args.clone()
}
fn volumes(&self) -> Self::Volumes {
let mut volumes = HashMap::new();
volumes.insert(self.volume.clone(), "/home/bdk-test".to_owned());
volumes
}
fn env_vars(&self) -> Self::EnvVars {
HashMap::new()
}
fn ports(&self) -> Option<Vec<Port>> {
self.ports.clone()
}
fn with_args(self, args: <Self as Image>::Args) -> Self {
Electrs { args, ..self }
}
fn with_entrypoint(self, entrypoint: &Self::EntryPoint) -> Self {
Self {
entrypoint: Some(entrypoint.to_string()),
..self
}
}
fn entrypoint(&self) -> Option<String> {
self.entrypoint.to_owned()
}
}
impl Default for Electrs {
fn default() -> Self {
Electrs {
tag: "v0.16.0.3".into(),
args: ElectrsArgs::default(),
ports: None,
entrypoint: Some("/build/electrs".into()),
wait_for_message: "Running accept thread".to_string(),
volume: uuid::Uuid::new_v4().to_string(),
bitcoind_container_name: uuid::Uuid::new_v4().to_string()
}
}
}
impl Electrs {
pub fn with_tag(self, tag_str: &str) -> Self {
Electrs {
tag: tag_str.to_string(),
..self
}
}
pub fn with_mapped_port<P: Into<Port>>(mut self, port: P) -> Self {
let mut ports = self.ports.unwrap_or_default();
ports.push(port.into());
self.ports = Some(ports);
self
}
pub fn with_volume(mut self, volume: String) -> Self {
self.volume = volume;
self
}
pub fn with_daemon_rpc_addr(mut self, name: String) -> Self {
self.args.daemon_rpc_addr = name;
self
}
}
#[derive(Debug, Clone)]
pub struct ElectrsArgs {
pub network: Network,
pub daemon_dir: String,
pub daemon_rpc_addr: String,
pub cookie: String,
pub http_addr: String,
pub electrum_rpc_addr: String,
pub cors: String,
}
/// Sane defaults for a mainnet regtest instance.
impl Default for ElectrsArgs {
fn default() -> Self {
ElectrsArgs {
network: Network::Regtest,
daemon_dir: "/home/bdk-test/".to_string(),
daemon_rpc_addr: "0.0.0.0:18443".to_string(),
cookie: "admin:123".to_string(),
http_addr: "0.0.0.0:3002".to_string(),
electrum_rpc_addr: "0.0.0.0:60401".to_string(),
cors: "*".to_string(),
}
}
}
impl IntoIterator for ElectrsArgs {
type Item = String;
type IntoIter = ::std::vec::IntoIter<String>;
fn into_iter(self) -> <Self as IntoIterator>::IntoIter {
let mut args = Vec::new();
match self.network {
Network::Testnet => args.push("--network=testnet".to_string()),
Network::Regtest => args.push("--network=regtest".to_string()),
Network::Bitcoin => {}
}
args.push(format!("-vvvvv"));
args.push(format!("--daemon-dir=={}", self.daemon_dir.as_str()));
args.push(format!("--daemon-rpc-addr={}", self.daemon_rpc_addr));
args.push(format!("--cookie={}", self.cookie));
args.push(format!("--http-addr={}", self.http_addr));
args.push(format!("--electrum-rpc-addr={}", self.electrum_rpc_addr));
args.push(format!("--cors={}", self.cors));
args.into_iter()
}
}

View File

@ -1,3 +1,6 @@
mod electrs;
mod bitcoind;
use crate::testutils;
use anyhow::Result;
use bitcoin_harness::{BitcoindRpcApi, Client};
@ -5,7 +8,11 @@ use futures::Future;
use get_port::get_port;
use libp2p::{core::Multiaddr, PeerId};
use monero_harness::{image, Monero};
use std::{path::PathBuf, sync::Arc, time::Duration};
use std::{
path::{Path, PathBuf},
sync::Arc,
time::Duration,
};
use swap::{
bitcoin,
bitcoin::Timelock,
@ -16,12 +23,13 @@ use swap::{
seed::Seed,
};
use tempfile::tempdir;
use testcontainers::{clients::Cli, Container};
use testcontainers::{clients::Cli, core::Port, Container, Docker, RunArgs};
use tokio::task::JoinHandle;
use tracing_core::dispatcher::DefaultGuard;
use tracing_log::LogTracer;
use url::Url;
use uuid::Uuid;
use bdk::blockchain::noop_progress;
const TEST_WALLET_NAME: &str = "testwallet";
@ -59,7 +67,7 @@ impl AliceParams {
}
}
#[derive(Debug, Clone)]
#[derive(Clone)]
struct BobParams {
seed: Seed,
db_path: PathBuf,
@ -344,6 +352,7 @@ where
&monero,
alice_starting_balances.clone(),
config,
tempdir().unwrap().path(),
)
.await;
@ -368,6 +377,7 @@ where
&monero,
bob_starting_balances.clone(),
config,
tempdir().unwrap().path(),
)
.await;
@ -397,20 +407,87 @@ where
testfn(test).await
}
fn random_prefix() -> String {
use rand::Rng;
const CHARSET: &[u8] = b"abcdefghijklmnopqrstuvwxyz";
const LEN: usize = 8;
let mut rng = rand::thread_rng();
let prefix: String = (0..LEN)
.map(|_| {
let idx = rng.gen_range(0, CHARSET.len());
CHARSET[idx] as char
})
.collect();
prefix
}
async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) {
let bitcoind_url = init_bitcoind_container().await.unwrap();
let bitcoind_name = format!("{}{}", random_prefix(), "bitcoind");
let volume = random_prefix();
let (bitcoind, bitcoind_url) = init_bitcoind_container(&cli, volume.clone(), bitcoind_name.clone(), volume.clone())
.await
.expect("could not init bitcoind");
let electrs = init_electrs_container(&cli, volume.clone(), bitcoind_name, volume).await;
let (monero, monerods) = init_monero_container(&cli).await;
(monero, Containers {
bitcoind_url,
bitcoind,
monerods,
electrs,
})
}
async fn init_bitcoind_container() -> Result<Url> {
// let bitcoind = Bitcoind::new(&cli, "0.19.1").unwrap();
let node_url: Url = "".parse()?;
init_bitcoind(node_url.clone(), 5).await?;
Ok(node_url)
async fn init_bitcoind_container<'c>(cli: &'c Cli, volume: String, name: String, network: String) -> Result<(Container<'c, Cli, bitcoind::Bitcoind>, Url)> {
let image = bitcoind::Bitcoind::default()
.with_mapped_port(Port {
local: 7041,
internal: 18443,
})
.with_mapped_port(Port {
local: 7042,
internal: 18886,
})
.with_volume(volume)
.with_tag("0.19.1");
let run_args = RunArgs::default()
.with_name(name)
.with_network(network);
let docker = cli.run_with_args(image, run_args);
let bitcoind_url = {
let input = format!("http://{}:{}@localhost:{}", "admin", "123", 7041);
Url::parse(&input).unwrap()
};
init_bitcoind(bitcoind_url.clone(), 5).await?;
Ok((docker, bitcoind_url.clone()))
}
pub async fn init_electrs_container<'c>(cli: &'c Cli, volume: String, bitcoind_container_name: String, network: String) -> Container<'c, Cli, electrs::Electrs> {
let bitcoind_rpc_addr = format!("{}:18443", bitcoind_container_name);
let image = electrs::Electrs::default()
.with_mapped_port(Port {
local: 3012,
internal: 3002,
})
.with_mapped_port(Port {
local: 60401,
internal: 60401,
})
.with_volume(volume)
.with_daemon_rpc_addr(bitcoind_rpc_addr)
.with_tag("latest");
let run_args = RunArgs::default()
.with_network(network);
let docker = cli.run_with_args(image, run_args);
docker
}
async fn mine(bitcoind_client: Client, reward_address: bitcoin::Address) -> Result<()> {
@ -481,6 +558,7 @@ async fn init_wallets(
monero: &Monero,
starting_balances: StartingBalances,
config: Config,
datadir: &Path,
) -> (Arc<bitcoin::Wallet>, Arc<monero::Wallet>) {
monero
.init(vec![(name, starting_balances.xmr.as_piconero())])
@ -492,20 +570,36 @@ async fn init_wallets(
network: config.monero_network,
});
// todo: make these configurable
let electrum_rpc_url = {
let input = format!("tcp://@localhost:{}", 60401);
Url::parse(&input).unwrap()
};
let electrum_http_url = {
let input = format!("http://@localhost:{}", 3012);
Url::parse(&input).unwrap()
};
let btc_wallet = Arc::new(
swap::bitcoin::Wallet::new(name, bitcoind_url.clone(), config.bitcoin_network)
.await
.unwrap(),
swap::bitcoin::Wallet::new(
name,
electrum_rpc_url,
electrum_http_url,
config.bitcoin_network,
datadir,
)
.await
.expect("could not init btc wallet"),
);
if starting_balances.btc != bitcoin::Amount::ZERO {
mint(
bitcoind_url,
btc_wallet.inner.new_address().await.unwrap(),
btc_wallet.new_address().await.unwrap(),
starting_balances.btc,
)
.await
.unwrap();
.expect("could not mint btc starting balance");
}
(btc_wallet, xmr_wallet)
@ -515,7 +609,9 @@ async fn init_wallets(
#[allow(dead_code)]
struct Containers<'a> {
bitcoind_url: Url,
bitcoind: Container<'a, Cli, bitcoind::Bitcoind>,
monerods: Vec<Container<'a, Cli, image::Monero>>,
electrs: Container<'a, Cli, electrs::Electrs>
}
/// Utility function to initialize logging in the test environment.
@ -524,7 +620,7 @@ struct Containers<'a> {
/// ```rust
/// let _guard = init_tracing();
/// ```
fn init_tracing() -> DefaultGuard {
pub fn init_tracing() -> DefaultGuard {
// converts all log records into tracing events
// Note: Make sure to initialize without unwrapping, otherwise this causes
// trouble when running multiple tests.
@ -535,16 +631,18 @@ fn init_tracing() -> DefaultGuard {
let xmr_btc_filter = tracing::Level::DEBUG;
let monero_harness_filter = tracing::Level::INFO;
let bitcoin_harness_filter = tracing::Level::INFO;
let testcontainers_filter = tracing::Level::DEBUG;
use tracing_subscriber::util::SubscriberInitExt as _;
tracing_subscriber::fmt()
.with_env_filter(format!(
"{},swap={},xmr_btc={},monero_harness={},bitcoin_harness={}",
"{},swap={},xmr_btc={},monero_harness={},bitcoin_harness={},testcontainers={}",
global_filter,
swap_filter,
xmr_btc_filter,
monero_harness_filter,
bitcoin_harness_filter,
testcontainers_filter
))
.set_default()
}