mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-10-01 01:45:40 -04:00
Merge #178
178: Replace bitcoind wallet with bdk wallet r=rishflab a=da-kami Create and pair bitcoind and electrs testcontainers using the same docker network and volume. Follow-up to be fixed on top: - [ ] Remove bitcoin-harness dependency and directly depend on https://github.com/thomaseizinger/rust-jsonrpc-client for the RPC client. Co-authored-by: rishflab <rishflab@hotmail.com> Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
This commit is contained in:
commit
7a9569ffd4
298
Cargo.lock
generated
298
Cargo.lock
generated
@ -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"
|
||||
@ -553,7 +599,7 @@ dependencies = [
|
||||
"regex",
|
||||
"terminal_size",
|
||||
"unicode-width",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
@ -807,7 +853,7 @@ checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users 0.3.5",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -818,7 +864,7 @@ checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"redox_users 0.4.0",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -867,6 +913,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"
|
||||
@ -1013,7 +1075,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1082,7 +1144,7 @@ dependencies = [
|
||||
"futures-io",
|
||||
"memchr",
|
||||
"parking",
|
||||
"pin-project-lite",
|
||||
"pin-project-lite 0.2.4",
|
||||
"waker-fn",
|
||||
]
|
||||
|
||||
@ -1132,7 +1194,7 @@ dependencies = [
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-project-lite 0.2.4",
|
||||
"pin-utils",
|
||||
"proc-macro-hack",
|
||||
"proc-macro-nested",
|
||||
@ -1180,8 +1242,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]]
|
||||
@ -1211,7 +1275,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",
|
||||
@ -1219,7 +1283,7 @@ dependencies = [
|
||||
"http",
|
||||
"indexmap",
|
||||
"slab",
|
||||
"tokio",
|
||||
"tokio 1.0.2",
|
||||
"tokio-util",
|
||||
"tracing",
|
||||
"tracing-futures",
|
||||
@ -1246,7 +1310,7 @@ version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1335,7 +1399,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",
|
||||
]
|
||||
@ -1346,7 +1410,7 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2861bd27ee074e5ee891e8b539837a9430012e249d7f0ca2d795650f579c1994"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.0.1",
|
||||
"http",
|
||||
]
|
||||
|
||||
@ -1368,7 +1432,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",
|
||||
@ -1380,7 +1444,7 @@ dependencies = [
|
||||
"itoa",
|
||||
"pin-project 1.0.4",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tokio 1.0.2",
|
||||
"tower-service",
|
||||
"tracing",
|
||||
"want",
|
||||
@ -1392,10 +1456,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",
|
||||
]
|
||||
|
||||
@ -1418,7 +1482,7 @@ checksum = "28538916eb3f3976311f5dfbe67b5362d0add1293d0a9cad17debf86f8e3aa48"
|
||||
dependencies = [
|
||||
"if-addrs-sys",
|
||||
"libc",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1444,7 +1508,7 @@ dependencies = [
|
||||
"ipnet",
|
||||
"libc",
|
||||
"log",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1579,7 +1643,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",
|
||||
@ -1669,7 +1733,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",
|
||||
@ -1686,7 +1750,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",
|
||||
@ -1709,7 +1773,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",
|
||||
@ -1754,7 +1818,7 @@ dependencies = [
|
||||
"libp2p-core",
|
||||
"log",
|
||||
"socket2",
|
||||
"tokio",
|
||||
"tokio 1.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1901,7 +1965,7 @@ dependencies = [
|
||||
"log",
|
||||
"miow",
|
||||
"ntapi",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1911,7 +1975,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a33c1b55807fbed163481b5ba66db4b2fa6cde694a5027be10fb724206c5897"
|
||||
dependencies = [
|
||||
"socket2",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1945,7 +2009,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"spectral",
|
||||
"testcontainers",
|
||||
"tokio",
|
||||
"tokio 1.0.2",
|
||||
"tracing",
|
||||
"tracing-log",
|
||||
"tracing-subscriber",
|
||||
@ -1991,7 +2055,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",
|
||||
@ -2024,7 +2088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8123a81538e457d44b933a02faf885d3fe8408806b23fa700e8f01c6c3a98998"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2050,7 +2114,7 @@ version = "0.3.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2250,7 +2314,7 @@ dependencies = [
|
||||
"libc",
|
||||
"redox_syscall 0.1.57",
|
||||
"smallvec",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2320,6 +2384,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"
|
||||
@ -2348,7 +2418,7 @@ dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wepoll-sys",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2469,7 +2539,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",
|
||||
]
|
||||
|
||||
@ -2479,7 +2549,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",
|
||||
@ -2510,7 +2580,7 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b518d7cdd93dab1d1122cf07fa9a60771836c668dde9d9e2a139f957f0d9f1bb"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"bytes 1.0.1",
|
||||
"prost",
|
||||
]
|
||||
|
||||
@ -2539,7 +2609,7 @@ dependencies = [
|
||||
"libc",
|
||||
"rand_core 0.3.1",
|
||||
"rdrand",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2552,7 +2622,7 @@ dependencies = [
|
||||
"fuchsia-cprng",
|
||||
"libc",
|
||||
"rand_core 0.3.1",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2571,7 +2641,7 @@ dependencies = [
|
||||
"rand_os",
|
||||
"rand_pcg",
|
||||
"rand_xorshift",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2706,7 +2776,7 @@ checksum = "1166d5c91dc97b88d1decc3285bb0a99ed84b05cfd0bc2341bdf2d43fc41e39b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_core 0.4.2",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2720,7 +2790,7 @@ dependencies = [
|
||||
"libc",
|
||||
"rand_core 0.4.2",
|
||||
"rdrand",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2818,7 +2888,7 @@ version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2828,7 +2898,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",
|
||||
@ -2843,11 +2913,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",
|
||||
@ -2868,7 +2938,7 @@ dependencies = [
|
||||
"spin",
|
||||
"untrusted",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2915,6 +2985,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"
|
||||
@ -2939,7 +3022,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2948,6 +3031,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"
|
||||
@ -3200,7 +3293,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]]
|
||||
@ -3371,6 +3476,7 @@ dependencies = [
|
||||
"atty",
|
||||
"backoff",
|
||||
"base64 0.12.3",
|
||||
"bdk",
|
||||
"bitcoin",
|
||||
"bitcoin-harness",
|
||||
"config",
|
||||
@ -3410,7 +3516,7 @@ dependencies = [
|
||||
"testcontainers",
|
||||
"thiserror",
|
||||
"time",
|
||||
"tokio",
|
||||
"tokio 1.0.2",
|
||||
"toml",
|
||||
"tracing",
|
||||
"tracing-core",
|
||||
@ -3456,7 +3562,7 @@ dependencies = [
|
||||
"rand 0.8.2",
|
||||
"redox_syscall 0.2.4",
|
||||
"remove_dir_all",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3467,7 +3573,7 @@ checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"dirs",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3477,7 +3583,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3546,7 +3652,7 @@ dependencies = [
|
||||
"stdweb",
|
||||
"time-macros",
|
||||
"version_check",
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3596,6 +3702,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"
|
||||
@ -3603,12 +3720,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",
|
||||
]
|
||||
|
||||
@ -3630,7 +3747,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b"
|
||||
dependencies = [
|
||||
"native-tls",
|
||||
"tokio",
|
||||
"tokio 1.0.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3640,8 +3757,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]]
|
||||
@ -3650,12 +3767,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",
|
||||
]
|
||||
|
||||
@ -3681,7 +3798,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",
|
||||
]
|
||||
@ -3840,7 +3957,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",
|
||||
]
|
||||
@ -4025,6 +4142,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"
|
||||
@ -4044,6 +4180,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"
|
||||
@ -4054,6 +4196,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"
|
||||
@ -4066,7 +4214,7 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
|
||||
dependencies = [
|
||||
"winapi",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -4081,7 +4229,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]]
|
||||
|
@ -18,6 +18,7 @@ async-trait = "0.1"
|
||||
atty = "0.2"
|
||||
backoff = { git = "https://github.com/ihrwein/backoff", rev = "9d03992a83dfdc596be26276d4e5c5254a4b11a2", features = ["tokio"] }
|
||||
base64 = "0.12"
|
||||
bdk = { version = "0.3" }
|
||||
bitcoin = { version = "0.25", features = ["rand", "use-serde"] }
|
||||
bitcoin-harness = { git = "https://github.com/coblox/bitcoin-harness-rs", rev = "ae2f6cd547496e680941c0910018bbe884128799" }
|
||||
config = { version = "0.10", default-features = false, features = ["toml"] }
|
||||
|
@ -15,7 +15,7 @@
|
||||
use anyhow::{Context, Result};
|
||||
use log::LevelFilter;
|
||||
use prettytable::{row, Table};
|
||||
use std::sync::Arc;
|
||||
use std::{path::Path, sync::Arc};
|
||||
use structopt::StructOpt;
|
||||
use swap::{
|
||||
bitcoin,
|
||||
@ -70,9 +70,13 @@ async fn main() -> Result<()> {
|
||||
config.data.dir.display()
|
||||
);
|
||||
|
||||
let db = Database::open(config.data.dir.join("database").as_path())
|
||||
let db_path = config.data.dir.join("database");
|
||||
|
||||
let db = Database::open(config.data.dir.join(db_path).as_path())
|
||||
.context("Could not open database")?;
|
||||
|
||||
let wallet_data_dir = config.data.dir.join("wallet");
|
||||
|
||||
match opt.cmd {
|
||||
Command::Start => {
|
||||
let seed = Seed::from_file_or_generate(&config.data.dir)
|
||||
@ -80,7 +84,17 @@ async fn main() -> Result<()> {
|
||||
|
||||
let execution_params = execution_params::Testnet::get_execution_params();
|
||||
|
||||
let (bitcoin_wallet, monero_wallet) = init_wallets(config.clone()).await?;
|
||||
let (bitcoin_wallet, monero_wallet) = init_wallets(
|
||||
config.clone(),
|
||||
&wallet_data_dir,
|
||||
seed.extended_private_key(BITCOIN_NETWORK)?.private_key,
|
||||
)
|
||||
.await?;
|
||||
|
||||
info!(
|
||||
"BTC deposit address: {}",
|
||||
bitcoin_wallet.new_address().await?
|
||||
);
|
||||
|
||||
let (mut event_loop, _) = EventLoop::new(
|
||||
config.network.listen,
|
||||
@ -113,11 +127,17 @@ async fn main() -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn init_wallets(config: Config) -> Result<(bitcoin::Wallet, monero::Wallet)> {
|
||||
async fn init_wallets(
|
||||
config: Config,
|
||||
bitcoin_wallet_data_dir: &Path,
|
||||
private_key: ::bitcoin::PrivateKey,
|
||||
) -> Result<(bitcoin::Wallet, monero::Wallet)> {
|
||||
let bitcoin_wallet = bitcoin::Wallet::new(
|
||||
config.bitcoin.wallet_name.as_str(),
|
||||
config.bitcoin.bitcoind_url,
|
||||
config.bitcoin.electrum_rpc_url,
|
||||
config.bitcoin.electrum_http_url,
|
||||
BITCOIN_NETWORK,
|
||||
bitcoin_wallet_data_dir,
|
||||
private_key,
|
||||
)
|
||||
.await?;
|
||||
let bitcoin_balance = bitcoin_wallet.balance().await?;
|
||||
|
@ -15,7 +15,7 @@
|
||||
use anyhow::{Context, Result};
|
||||
use log::LevelFilter;
|
||||
use prettytable::{row, Table};
|
||||
use std::sync::Arc;
|
||||
use std::{path::Path, sync::Arc};
|
||||
use structopt::StructOpt;
|
||||
use swap::{
|
||||
bitcoin,
|
||||
@ -75,6 +75,7 @@ async fn main() -> Result<()> {
|
||||
let db = Database::open(config.data.dir.join("database").as_path())
|
||||
.context("Could not open database")?;
|
||||
|
||||
let wallet_data_dir = config.data.dir.join("wallet");
|
||||
let seed =
|
||||
Seed::from_file_or_generate(&config.data.dir).expect("Could not retrieve/initialize seed");
|
||||
|
||||
@ -89,8 +90,14 @@ async fn main() -> Result<()> {
|
||||
alice_addr,
|
||||
send_bitcoin,
|
||||
} => {
|
||||
let (bitcoin_wallet, monero_wallet) =
|
||||
init_wallets(config, bitcoin_network, monero_network).await?;
|
||||
let (bitcoin_wallet, monero_wallet) = init_wallets(
|
||||
config,
|
||||
bitcoin_network,
|
||||
&wallet_data_dir,
|
||||
monero_network,
|
||||
seed,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let swap_id = Uuid::new_v4();
|
||||
|
||||
@ -99,6 +106,11 @@ async fn main() -> Result<()> {
|
||||
send_bitcoin, swap_id
|
||||
);
|
||||
|
||||
info!(
|
||||
"BTC deposit address: {}",
|
||||
bitcoin_wallet.new_address().await?
|
||||
);
|
||||
|
||||
let bob_factory = Builder::new(
|
||||
seed,
|
||||
db,
|
||||
@ -131,8 +143,14 @@ async fn main() -> Result<()> {
|
||||
alice_peer_id,
|
||||
alice_addr,
|
||||
}) => {
|
||||
let (bitcoin_wallet, monero_wallet) =
|
||||
init_wallets(config, bitcoin_network, monero_network).await?;
|
||||
let (bitcoin_wallet, monero_wallet) = init_wallets(
|
||||
config,
|
||||
bitcoin_network,
|
||||
&wallet_data_dir,
|
||||
monero_network,
|
||||
seed,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let bob_factory = Builder::new(
|
||||
seed,
|
||||
@ -156,8 +174,14 @@ async fn main() -> Result<()> {
|
||||
force,
|
||||
}) => {
|
||||
// TODO: Optimization: Only init the Bitcoin wallet, Monero wallet unnecessary
|
||||
let (bitcoin_wallet, monero_wallet) =
|
||||
init_wallets(config, bitcoin_network, monero_network).await?;
|
||||
let (bitcoin_wallet, monero_wallet) = init_wallets(
|
||||
config,
|
||||
bitcoin_network,
|
||||
&wallet_data_dir,
|
||||
monero_network,
|
||||
seed,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let bob_factory = Builder::new(
|
||||
seed,
|
||||
@ -200,8 +224,14 @@ async fn main() -> Result<()> {
|
||||
alice_addr,
|
||||
force,
|
||||
}) => {
|
||||
let (bitcoin_wallet, monero_wallet) =
|
||||
init_wallets(config, bitcoin_network, monero_network).await?;
|
||||
let (bitcoin_wallet, monero_wallet) = init_wallets(
|
||||
config,
|
||||
bitcoin_network,
|
||||
&wallet_data_dir,
|
||||
monero_network,
|
||||
seed,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// TODO: Optimize to only use the Bitcoin wallet, Monero wallet is unnecessary
|
||||
let bob_factory = Builder::new(
|
||||
@ -235,14 +265,24 @@ async fn main() -> Result<()> {
|
||||
async fn init_wallets(
|
||||
config: Config,
|
||||
bitcoin_network: bitcoin::Network,
|
||||
bitcoin_wallet_data_dir: &Path,
|
||||
monero_network: monero::Network,
|
||||
seed: Seed,
|
||||
) -> Result<(bitcoin::Wallet, monero::Wallet)> {
|
||||
let bitcoin_wallet = bitcoin::Wallet::new(
|
||||
config.bitcoin.wallet_name.as_str(),
|
||||
config.bitcoin.bitcoind_url,
|
||||
config.bitcoin.electrum_rpc_url,
|
||||
config.bitcoin.electrum_http_url,
|
||||
bitcoin_network,
|
||||
bitcoin_wallet_data_dir,
|
||||
seed.extended_private_key(bitcoin_network)?.private_key,
|
||||
)
|
||||
.await?;
|
||||
|
||||
bitcoin_wallet
|
||||
.sync_wallet()
|
||||
.await
|
||||
.expect("Could not sync btc wallet");
|
||||
|
||||
let bitcoin_balance = bitcoin_wallet.balance().await?;
|
||||
info!(
|
||||
"Connection to Bitcoin wallet succeeded, balance: {}",
|
||||
|
@ -211,7 +211,7 @@ pub trait BroadcastSignedTransaction {
|
||||
|
||||
#[async_trait]
|
||||
pub trait WatchForRawTransaction {
|
||||
async fn watch_for_raw_transaction(&self, txid: Txid) -> Transaction;
|
||||
async fn watch_for_raw_transaction(&self, txid: Txid) -> Result<Transaction>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -225,17 +225,12 @@ pub trait WaitForTransactionFinality {
|
||||
|
||||
#[async_trait]
|
||||
pub trait GetBlockHeight {
|
||||
async fn get_block_height(&self) -> BlockHeight;
|
||||
async fn get_block_height(&self) -> Result<BlockHeight>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait TransactionBlockHeight {
|
||||
async fn transaction_block_height(&self, txid: Txid) -> BlockHeight;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
pub trait WaitForBlockHeight {
|
||||
async fn wait_for_block_height(&self, height: BlockHeight);
|
||||
async fn transaction_block_height(&self, txid: Txid) -> Result<BlockHeight>;
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
@ -245,7 +240,7 @@ pub trait GetRawTransaction {
|
||||
|
||||
#[async_trait]
|
||||
pub trait GetNetwork {
|
||||
fn get_network(&self) -> Network;
|
||||
async fn get_network(&self) -> Network;
|
||||
}
|
||||
|
||||
pub fn recover(S: PublicKey, sig: Signature, encsig: EncryptedSignature) -> Result<SecretKey> {
|
||||
@ -259,13 +254,14 @@ pub fn recover(S: PublicKey, sig: Signature, encsig: EncryptedSignature) -> Resu
|
||||
Ok(s)
|
||||
}
|
||||
|
||||
pub async fn poll_until_block_height_is_gte<B>(client: &B, target: BlockHeight)
|
||||
pub async fn poll_until_block_height_is_gte<B>(client: &B, target: BlockHeight) -> Result<()>
|
||||
where
|
||||
B: GetBlockHeight,
|
||||
{
|
||||
while client.get_block_height().await < target {
|
||||
while client.get_block_height().await? < target {
|
||||
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn current_epoch<W>(
|
||||
@ -277,8 +273,8 @@ pub async fn current_epoch<W>(
|
||||
where
|
||||
W: WatchForRawTransaction + TransactionBlockHeight + GetBlockHeight,
|
||||
{
|
||||
let current_block_height = bitcoin_wallet.get_block_height().await;
|
||||
let lock_tx_height = bitcoin_wallet.transaction_block_height(lock_tx_id).await;
|
||||
let current_block_height = bitcoin_wallet.get_block_height().await?;
|
||||
let lock_tx_height = bitcoin_wallet.transaction_block_height(lock_tx_id).await?;
|
||||
let cancel_timelock_height = lock_tx_height + cancel_timelock;
|
||||
let punish_timelock_height = cancel_timelock_height + punish_timelock;
|
||||
|
||||
@ -300,9 +296,9 @@ pub async fn wait_for_cancel_timelock_to_expire<W>(
|
||||
where
|
||||
W: WatchForRawTransaction + TransactionBlockHeight + GetBlockHeight,
|
||||
{
|
||||
let tx_lock_height = bitcoin_wallet.transaction_block_height(lock_tx_id).await;
|
||||
let tx_lock_height = bitcoin_wallet.transaction_block_height(lock_tx_id).await?;
|
||||
|
||||
poll_until_block_height_is_gte(bitcoin_wallet, tx_lock_height + cancel_timelock).await;
|
||||
poll_until_block_height_is_gte(bitcoin_wallet, tx_lock_height + cancel_timelock).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct TxLock {
|
||||
inner: Transaction,
|
||||
inner: PartiallySignedTransaction,
|
||||
pub(in crate::bitcoin) output_descriptor: Descriptor<::bitcoin::PublicKey>,
|
||||
}
|
||||
|
||||
@ -20,41 +20,38 @@ impl TxLock {
|
||||
{
|
||||
let lock_output_descriptor = build_shared_output_descriptor(A.0, B.0);
|
||||
let address = lock_output_descriptor
|
||||
.address(wallet.get_network(), NullCtx)
|
||||
.address(wallet.get_network().await, NullCtx)
|
||||
.expect("can derive address from descriptor");
|
||||
|
||||
// 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| {
|
||||
@ -78,7 +75,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(),
|
||||
};
|
||||
|
||||
@ -93,6 +90,6 @@ impl TxLock {
|
||||
|
||||
impl From<TxLock> for PartiallySignedTransaction {
|
||||
fn from(from: TxLock) -> Self {
|
||||
PartiallySignedTransaction::from_unsigned_tx(from.inner).expect("to be unsigned")
|
||||
from.inner
|
||||
}
|
||||
}
|
||||
|
@ -18,11 +18,16 @@ impl BlockHeight {
|
||||
pub const fn new(block_height: u32) -> Self {
|
||||
Self(block_height)
|
||||
}
|
||||
pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
|
||||
match self.0.checked_sub(rhs.0) {
|
||||
Some(result) => Some(BlockHeight(result)),
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Add<u32> for BlockHeight {
|
||||
type Output = BlockHeight;
|
||||
|
||||
fn add(self, rhs: u32) -> Self::Output {
|
||||
BlockHeight(self.0 + rhs)
|
||||
}
|
||||
|
@ -7,55 +7,107 @@ use crate::{
|
||||
execution_params::ExecutionParams,
|
||||
};
|
||||
use ::bitcoin::{util::psbt::PartiallySignedTransaction, Txid};
|
||||
use anyhow::{Context, Result};
|
||||
use anyhow::{anyhow, bail, 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::{self, Client, ElectrumApi},
|
||||
miniscript::bitcoin::PrivateKey,
|
||||
FeeRate,
|
||||
};
|
||||
use reqwest::{Method, Url};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{path::Path, sync::Arc, time::Duration};
|
||||
use tokio::{sync::Mutex, time::interval};
|
||||
|
||||
const SLED_TREE_NAME: &str = "default_tree";
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
Io(reqwest::Error),
|
||||
Parse(std::num::ParseIntError),
|
||||
NotYetMined,
|
||||
JsonDeserialisation(reqwest::Error),
|
||||
}
|
||||
|
||||
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(
|
||||
electrum_rpc_url: Url,
|
||||
electrum_http_url: Url,
|
||||
network: bitcoin::Network,
|
||||
wallet_dir: &Path,
|
||||
private_key: PrivateKey,
|
||||
) -> Result<Self> {
|
||||
// Workaround for https://github.com/bitcoindevkit/rust-electrum-client/issues/47.
|
||||
let config = electrum_client::ConfigBuilder::default().retry(2).build();
|
||||
|
||||
let client = Client::from_config(electrum_rpc_url.as_str(), config)
|
||||
.map_err(|e| anyhow!("Failed to init electrum rpc client: {:?}", e))?;
|
||||
|
||||
let db = bdk::sled::open(wallet_dir)?.open_tree(SLED_TREE_NAME)?;
|
||||
|
||||
let bdk_wallet = bdk::Wallet::new(
|
||||
bdk::template::P2WPKH(private_key),
|
||||
None,
|
||||
network,
|
||||
db,
|
||||
ElectrumBlockchain::from(client),
|
||||
)?;
|
||||
|
||||
Ok(Self {
|
||||
inner: wallet,
|
||||
inner: Arc::new(Mutex::new(bdk_wallet)),
|
||||
network,
|
||||
http_url: electrum_http_url,
|
||||
rpc_url: electrum_rpc_url,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn balance(&self) -> Result<Amount> {
|
||||
let balance = self.inner.balance().await?;
|
||||
Ok(balance)
|
||||
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
|
||||
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))
|
||||
}
|
||||
|
||||
pub async fn sync_wallet(&self) -> Result<()> {
|
||||
tracing::debug!("syncing wallet");
|
||||
self.inner.lock().await.sync(noop_progress(), None)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,11 +118,17 @@ 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)?;
|
||||
|
||||
tracing::debug!("building tx lock");
|
||||
self.sync_wallet().await?;
|
||||
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)),
|
||||
)?;
|
||||
tracing::debug!("tx lock built");
|
||||
Ok(psbt)
|
||||
}
|
||||
}
|
||||
@ -78,22 +136,15 @@ impl BuildTxLockPsbt for Wallet {
|
||||
#[async_trait]
|
||||
impl SignTxLock for Wallet {
|
||||
async fn sign_tx_lock(&self, tx_lock: TxLock) -> Result<Transaction> {
|
||||
let txid = tx_lock.txid();
|
||||
tracing::debug!("signing tx lock: {}", txid);
|
||||
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)?;
|
||||
if !finalized {
|
||||
bail!("Could not finalize TxLock psbt")
|
||||
}
|
||||
let tx = signed_psbt.extract_tx();
|
||||
tracing::debug!("signed tx lock: {}", txid);
|
||||
Ok(tx)
|
||||
}
|
||||
}
|
||||
@ -101,70 +152,92 @@ 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)
|
||||
tracing::debug!("attempting to broadcast tx: {}", transaction.txid());
|
||||
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 {
|
||||
async fn watch_for_raw_transaction(&self, txid: Txid) -> Result<Transaction> {
|
||||
tracing::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)?;
|
||||
tracing::debug!("found tx: {}", txid);
|
||||
Ok(tx)
|
||||
})
|
||||
.await
|
||||
.expect("transient errors to be retried")
|
||||
.map_err(|err| anyhow!("transient errors to be retried: {:?}", err))
|
||||
}
|
||||
}
|
||||
|
||||
#[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))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl GetBlockHeight for Wallet {
|
||||
async fn get_block_height(&self) -> BlockHeight {
|
||||
async fn get_block_height(&self) -> Result<BlockHeight> {
|
||||
let url = blocks_tip_height_url(&self.http_url)?;
|
||||
let height = retry(ConstantBackoff::new(Duration::from_secs(1)), || async {
|
||||
Ok(self.inner.client.getblockcount().await?)
|
||||
let height = reqwest::Client::new()
|
||||
.request(Method::GET, url.clone())
|
||||
.send()
|
||||
.await
|
||||
.map_err(Error::Io)?
|
||||
.text()
|
||||
.await
|
||||
.map_err(Error::Io)?
|
||||
.parse::<u32>()
|
||||
.map_err(|err| backoff::Error::Permanent(Error::Parse(err)))?;
|
||||
Result::<_, backoff::Error<Error>>::Ok(height)
|
||||
})
|
||||
.await
|
||||
.expect("transient errors to be retried");
|
||||
.map_err(|err| anyhow!("transient errors to be retried: {:?}", err))?;
|
||||
|
||||
BlockHeight::new(height)
|
||||
Ok(BlockHeight::new(height))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl TransactionBlockHeight for Wallet {
|
||||
async fn transaction_block_height(&self, txid: Txid) -> BlockHeight {
|
||||
#[derive(Debug)]
|
||||
enum Error {
|
||||
Io,
|
||||
NotYetMined,
|
||||
async fn transaction_block_height(&self, txid: Txid) -> Result<BlockHeight> {
|
||||
let url = tx_status_url(txid, &self.http_url)?;
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
struct TransactionStatus {
|
||||
block_height: Option<u32>,
|
||||
confirmed: bool,
|
||||
}
|
||||
|
||||
let height = retry(ConstantBackoff::new(Duration::from_secs(1)), || async {
|
||||
let block_height = self
|
||||
.inner
|
||||
.transaction_block_height(txid)
|
||||
let resp = reqwest::Client::new()
|
||||
.request(Method::GET, url.clone())
|
||||
.send()
|
||||
.await
|
||||
.map_err(|_| backoff::Error::Transient(Error::Io))?;
|
||||
.map_err(|err| backoff::Error::Transient(Error::Io(err)))?;
|
||||
|
||||
let block_height = block_height.ok_or(backoff::Error::Transient(Error::NotYetMined))?;
|
||||
let tx_status: TransactionStatus = resp
|
||||
.json()
|
||||
.await
|
||||
.map_err(|err| backoff::Error::Permanent(Error::JsonDeserialisation(err)))?;
|
||||
|
||||
let block_height = tx_status
|
||||
.block_height
|
||||
.ok_or(backoff::Error::Transient(Error::NotYetMined))?;
|
||||
|
||||
Result::<_, backoff::Error<Error>>::Ok(block_height)
|
||||
})
|
||||
.await
|
||||
.expect("transient errors to be retried");
|
||||
.map_err(|err| anyhow!("transient errors to be retried: {:?}", err))?;
|
||||
|
||||
BlockHeight::new(height)
|
||||
Ok(BlockHeight::new(height))
|
||||
}
|
||||
}
|
||||
|
||||
@ -175,16 +248,19 @@ impl WaitForTransactionFinality for Wallet {
|
||||
txid: Txid,
|
||||
execution_params: ExecutionParams,
|
||||
) -> Result<()> {
|
||||
// TODO(Franck): This assumes that bitcoind runs with txindex=1
|
||||
|
||||
tracing::debug!("waiting for tx finality: {}", txid);
|
||||
// Divide by 4 to not check too often yet still be aware of the new block early
|
||||
// on.
|
||||
let mut interval = interval(execution_params.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 >= execution_params.bitcoin_finality_confirmations {
|
||||
let tx_block_height = self.transaction_block_height(txid).await?;
|
||||
tracing::debug!("tx_block_height: {:?}", tx_block_height);
|
||||
let block_height = self.get_block_height().await?;
|
||||
tracing::debug!("latest_block_height: {:?}", block_height);
|
||||
if let Some(confirmations) = block_height.checked_sub(tx_block_height) {
|
||||
tracing::debug!("confirmations: {:?}", confirmations);
|
||||
if u32::from(confirmations) >= execution_params.bitcoin_finality_confirmations {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -195,8 +271,48 @@ impl WaitForTransactionFinality for Wallet {
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl GetNetwork for Wallet {
|
||||
fn get_network(&self) -> bitcoin::Network {
|
||||
self.network
|
||||
async fn get_network(&self) -> bitcoin::Network {
|
||||
self.inner.lock().await.network()
|
||||
}
|
||||
}
|
||||
|
||||
fn tx_status_url(txid: Txid, base_url: &Url) -> Result<Url> {
|
||||
let url = base_url.join(&format!("tx/{}/status", txid))?;
|
||||
Ok(url)
|
||||
}
|
||||
|
||||
fn blocks_tip_height_url(base_url: &Url) -> Result<Url> {
|
||||
let url = base_url.join("blocks/tip/height")?;
|
||||
Ok(url)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
bitcoin::{
|
||||
wallet::{blocks_tip_height_url, tx_status_url},
|
||||
Txid,
|
||||
},
|
||||
cli::config::DEFAULT_ELECTRUM_HTTP_URL,
|
||||
};
|
||||
use reqwest::Url;
|
||||
|
||||
#[test]
|
||||
fn create_tx_status_url_from_default_base_url_success() {
|
||||
let txid: Txid = Txid::default();
|
||||
let base_url = Url::parse(DEFAULT_ELECTRUM_HTTP_URL).expect("Could not parse url");
|
||||
let url = tx_status_url(txid, &base_url).expect("Could not create url");
|
||||
let expected = format!("https://blockstream.info/testnet/api/tx/{}/status", txid);
|
||||
assert_eq!(url.as_str(), expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_block_tip_height_url_from_default_base_url_success() {
|
||||
let base_url = Url::parse(DEFAULT_ELECTRUM_HTTP_URL).expect("Could not parse url");
|
||||
let url = blocks_tip_height_url(&base_url).expect("Could not create url");
|
||||
let expected = "https://blockstream.info/testnet/api/blocks/tip/height";
|
||||
assert_eq!(url.as_str(), expected);
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,8 @@ use std::{
|
||||
use tracing::info;
|
||||
use url::Url;
|
||||
|
||||
const DEFAULT_BITCOIND_TESTNET_URL: &str = "http://127.0.0.1:18332";
|
||||
pub const DEFAULT_ELECTRUM_HTTP_URL: &str = "https://blockstream.info/testnet/api/";
|
||||
const DEFAULT_ELECTRUM_RPC_URL: &str = "ssl://electrum.blockstream.info:60002";
|
||||
const DEFAULT_MONERO_WALLET_RPC_TESTNET_URL: &str = "http://127.0.0.1:38083/json_rpc";
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)]
|
||||
@ -43,8 +44,8 @@ pub struct Data {
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Bitcoin {
|
||||
pub bitcoind_url: Url,
|
||||
pub wallet_name: String,
|
||||
pub electrum_http_url: Url,
|
||||
pub electrum_rpc_url: Url,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
@ -105,15 +106,17 @@ pub fn query_user_for_initial_testnet_config() -> Result<Config> {
|
||||
.interact_text()?;
|
||||
let data_dir = data_dir.as_str().parse()?;
|
||||
|
||||
let bitcoind_url = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Enter Bitcoind URL (including username and password if applicable) or hit return to use default")
|
||||
.default(DEFAULT_BITCOIND_TESTNET_URL.to_owned())
|
||||
let electrum_http_url: String = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Enter Electrum HTTP URL or hit return to use default")
|
||||
.default(DEFAULT_ELECTRUM_HTTP_URL.to_owned())
|
||||
.interact_text()?;
|
||||
let bitcoind_url = bitcoind_url.as_str().parse()?;
|
||||
let electrum_http_url = Url::parse(electrum_http_url.as_str())?;
|
||||
|
||||
let bitcoin_wallet_name = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Enter Bitcoind wallet name")
|
||||
let electrum_rpc_url: String = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Enter Electrum RPC URL or hit return to use default")
|
||||
.default(DEFAULT_ELECTRUM_RPC_URL.to_owned())
|
||||
.interact_text()?;
|
||||
let electrum_rpc_url = Url::parse(electrum_rpc_url.as_str())?;
|
||||
|
||||
let monero_wallet_rpc_url = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Enter Monero Wallet RPC URL or hit enter to use default")
|
||||
@ -125,8 +128,8 @@ pub fn query_user_for_initial_testnet_config() -> Result<Config> {
|
||||
Ok(Config {
|
||||
data: Data { dir: data_dir },
|
||||
bitcoin: Bitcoin {
|
||||
bitcoind_url,
|
||||
wallet_name: bitcoin_wallet_name,
|
||||
electrum_http_url,
|
||||
electrum_rpc_url,
|
||||
},
|
||||
monero: Monero {
|
||||
wallet_rpc_url: monero_wallet_rpc_url,
|
||||
@ -150,8 +153,8 @@ mod tests {
|
||||
dir: Default::default(),
|
||||
},
|
||||
bitcoin: Bitcoin {
|
||||
bitcoind_url: Url::from_str("http://127.0.0.1:18332").unwrap(),
|
||||
wallet_name: "alice".to_string(),
|
||||
electrum_http_url: Url::from_str(DEFAULT_ELECTRUM_HTTP_URL).unwrap(),
|
||||
electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL).unwrap(),
|
||||
},
|
||||
monero: Monero {
|
||||
wallet_rpc_url: Url::from_str("http://127.0.0.1:38083/json_rpc").unwrap(),
|
||||
|
@ -12,9 +12,10 @@ use std::{
|
||||
use tracing::info;
|
||||
use url::Url;
|
||||
|
||||
const DEFAULT_BITCOIND_TESTNET_URL: &str = "http://127.0.0.1:18332";
|
||||
const DEFAULT_MONERO_WALLET_RPC_TESTNET_URL: &str = "http://127.0.0.1:38083/json_rpc";
|
||||
const DEFAULT_LISTEN_ADDRESS: &str = "/ip4/0.0.0.0/tcp/9939";
|
||||
const DEFAULT_ELECTRUM_HTTP_URL: &str = "https://blockstream.info/testnet/api/";
|
||||
const DEFAULT_ELECTRUM_RPC_URL: &str = "ssl://electrum.blockstream.info:60002";
|
||||
const DEFAULT_MONERO_WALLET_RPC_TESTNET_URL: &str = "http://127.0.0.1:38083/json_rpc";
|
||||
|
||||
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize, PartialEq)]
|
||||
pub struct Config {
|
||||
@ -52,8 +53,8 @@ pub struct Network {
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Bitcoin {
|
||||
pub bitcoind_url: Url,
|
||||
pub wallet_name: String,
|
||||
pub electrum_http_url: Url,
|
||||
pub electrum_rpc_url: Url,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
|
||||
@ -120,15 +121,17 @@ pub fn query_user_for_initial_testnet_config() -> Result<Config> {
|
||||
.interact_text()?;
|
||||
let listen_address = listen_address.as_str().parse()?;
|
||||
|
||||
let bitcoind_url = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Enter Bitcoind URL (including username and password if applicable) or hit return to use default")
|
||||
.default(DEFAULT_BITCOIND_TESTNET_URL.to_owned())
|
||||
let electrum_http_url: String = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Enter Electrum HTTP URL or hit return to use default")
|
||||
.default(DEFAULT_ELECTRUM_HTTP_URL.to_owned())
|
||||
.interact_text()?;
|
||||
let bitcoind_url = bitcoind_url.as_str().parse()?;
|
||||
let electrum_http_url = Url::parse(electrum_http_url.as_str())?;
|
||||
|
||||
let bitcoin_wallet_name = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Enter Bitcoind wallet name")
|
||||
let electrum_rpc_url: String = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Enter Electrum RPC URL or hit return to use default")
|
||||
.default(DEFAULT_ELECTRUM_RPC_URL.to_owned())
|
||||
.interact_text()?;
|
||||
let electrum_rpc_url = Url::parse(electrum_rpc_url.as_str())?;
|
||||
|
||||
let monero_wallet_rpc_url = Input::with_theme(&ColorfulTheme::default())
|
||||
.with_prompt("Enter Monero Wallet RPC URL or hit enter to use default")
|
||||
@ -143,8 +146,8 @@ pub fn query_user_for_initial_testnet_config() -> Result<Config> {
|
||||
listen: listen_address,
|
||||
},
|
||||
bitcoin: Bitcoin {
|
||||
bitcoind_url,
|
||||
wallet_name: bitcoin_wallet_name,
|
||||
electrum_http_url,
|
||||
electrum_rpc_url,
|
||||
},
|
||||
monero: Monero {
|
||||
wallet_rpc_url: monero_wallet_rpc_url,
|
||||
@ -167,15 +170,16 @@ mod tests {
|
||||
data: Data {
|
||||
dir: Default::default(),
|
||||
},
|
||||
network: Network {
|
||||
listen: "/ip4/0.0.0.0/tcp/9939".parse().unwrap(),
|
||||
},
|
||||
bitcoin: Bitcoin {
|
||||
bitcoind_url: Url::from_str("http://127.0.0.1:18332").unwrap(),
|
||||
wallet_name: "alice".to_string(),
|
||||
electrum_http_url: Url::from_str(DEFAULT_ELECTRUM_HTTP_URL).unwrap(),
|
||||
electrum_rpc_url: Url::from_str(DEFAULT_ELECTRUM_RPC_URL).unwrap(),
|
||||
},
|
||||
network: Network {
|
||||
listen: DEFAULT_LISTEN_ADDRESS.parse().unwrap(),
|
||||
},
|
||||
|
||||
monero: Monero {
|
||||
wallet_rpc_url: Url::from_str("http://127.0.0.1:38083/json_rpc").unwrap(),
|
||||
wallet_rpc_url: Url::from_str(DEFAULT_MONERO_WALLET_RPC_TESTNET_URL).unwrap(),
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -42,7 +42,7 @@ where
|
||||
bitcoin_wallet.watch_for_raw_transaction(lock_bitcoin_txid),
|
||||
)
|
||||
.await
|
||||
.context("Failed to find lock Bitcoin tx")?;
|
||||
.context("Failed to find lock Bitcoin tx")??;
|
||||
|
||||
// // We saw the transaction in the mempool, waiting for it to be confirmed.
|
||||
bitcoin_wallet
|
||||
@ -158,8 +158,9 @@ where
|
||||
// First wait for cancel timelock to expire
|
||||
let tx_lock_height = bitcoin_wallet
|
||||
.transaction_block_height(tx_lock.txid())
|
||||
.await;
|
||||
poll_until_block_height_is_gte(bitcoin_wallet.as_ref(), tx_lock_height + cancel_timelock).await;
|
||||
.await?;
|
||||
poll_until_block_height_is_gte(bitcoin_wallet.as_ref(), tx_lock_height + cancel_timelock)
|
||||
.await?;
|
||||
|
||||
let tx_cancel = bitcoin::TxCancel::new(&tx_lock, cancel_timelock, a.public(), B);
|
||||
|
||||
@ -216,7 +217,7 @@ where
|
||||
|
||||
match select(punish_timelock_expired, seen_refund_tx).await {
|
||||
Either::Left(_) => Ok((tx_refund, None)),
|
||||
Either::Right((published_refund_tx, _)) => Ok((tx_refund, Some(published_refund_tx))),
|
||||
Either::Right((published_refund_tx, _)) => Ok((tx_refund, Some(published_refund_tx?))),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -292,7 +292,7 @@ async fn run_until_internal(
|
||||
AliceState::BtcCancelled { state3, tx_cancel } => {
|
||||
let tx_cancel_height = bitcoin_wallet
|
||||
.transaction_block_height(tx_cancel.txid())
|
||||
.await;
|
||||
.await?;
|
||||
|
||||
let (tx_refund, published_refund_tx) = wait_for_bitcoin_refund(
|
||||
&tx_cancel,
|
||||
@ -388,7 +388,7 @@ async fn run_until_internal(
|
||||
match select(refund_tx_seen, punish_tx_finalised).await {
|
||||
Either::Left((published_refund_tx, _)) => {
|
||||
let spend_key = extract_monero_private_key(
|
||||
published_refund_tx,
|
||||
published_refund_tx?,
|
||||
tx_refund,
|
||||
state3.s_a,
|
||||
state3.a.clone(),
|
||||
|
@ -480,7 +480,7 @@ impl State4 {
|
||||
|
||||
let tx_redeem_candidate = bitcoin_wallet
|
||||
.watch_for_raw_transaction(tx_redeem.txid())
|
||||
.await;
|
||||
.await?;
|
||||
|
||||
let tx_redeem_sig =
|
||||
tx_redeem.extract_signature_by_key(tx_redeem_candidate, self.b.public())?;
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::fs::ensure_directory_exists;
|
||||
use ::bitcoin::secp256k1::{self, constants::SECRET_KEY_SIZE, SecretKey};
|
||||
use anyhow::Result;
|
||||
use bdk::bitcoin::util::bip32::ExtendedPrivKey;
|
||||
use pem::{encode, Pem};
|
||||
use rand::prelude::*;
|
||||
use std::{
|
||||
@ -26,6 +28,11 @@ impl Seed {
|
||||
Ok(Seed(bytes))
|
||||
}
|
||||
|
||||
pub fn extended_private_key(&self, network: bitcoin::Network) -> Result<ExtendedPrivKey> {
|
||||
let private_key = ExtendedPrivKey::new_master(network, &self.bytes())?;
|
||||
Ok(private_key)
|
||||
}
|
||||
|
||||
pub fn bytes(&self) -> [u8; SEED_LENGTH] {
|
||||
self.0
|
||||
}
|
||||
|
139
swap/tests/testutils/bitcoind.rs
Normal file
139
swap/tests/testutils/bitcoind.rs
Normal file
@ -0,0 +1,139 @@
|
||||
use std::collections::HashMap;
|
||||
use testcontainers::{
|
||||
core::{Container, Docker, Port, WaitForMessage},
|
||||
Image,
|
||||
};
|
||||
|
||||
pub const RPC_USER: &str = "admin";
|
||||
pub const RPC_PASSWORD: &str = "123";
|
||||
pub const RPC_PORT: u16 = 18443;
|
||||
pub const PORT: u16 = 18886;
|
||||
pub const DATADIR: &str = "/home/bdk";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Bitcoind {
|
||||
tag: String,
|
||||
args: BitcoindArgs,
|
||||
entrypoint: Option<String>,
|
||||
volume: Option<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();
|
||||
match self.volume.clone() {
|
||||
None => {}
|
||||
Some(volume) => {
|
||||
volumes.insert(volume, DATADIR.to_string());
|
||||
}
|
||||
}
|
||||
volumes
|
||||
}
|
||||
|
||||
fn env_vars(&self) -> Self::EnvVars {
|
||||
HashMap::new()
|
||||
}
|
||||
|
||||
fn ports(&self) -> Option<Vec<Port>> {
|
||||
None
|
||||
}
|
||||
|
||||
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(),
|
||||
entrypoint: Some("/usr/bin/bitcoind".into()),
|
||||
volume: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Bitcoind {
|
||||
pub fn with_tag(self, tag_str: &str) -> Self {
|
||||
Bitcoind {
|
||||
tag: tag_str.to_string(),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_volume(mut self, volume: String) -> Self {
|
||||
self.volume = Some(volume);
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BitcoindArgs;
|
||||
|
||||
impl Default for BitcoindArgs {
|
||||
fn default() -> Self {
|
||||
BitcoindArgs
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoIterator for BitcoindArgs {
|
||||
type Item = String;
|
||||
type IntoIter = ::std::vec::IntoIter<String>;
|
||||
|
||||
// todo: these "defaults" are only suitable for our tests and need to be looked
|
||||
// at
|
||||
fn into_iter(self) -> <Self as IntoIterator>::IntoIter {
|
||||
let args = vec![
|
||||
"-server".to_string(),
|
||||
"-regtest".to_string(),
|
||||
"-listen=1".to_string(),
|
||||
"-prune=0".to_string(),
|
||||
"-rpcallowip=0.0.0.0/0".to_string(),
|
||||
"-rpcbind=0.0.0.0".to_string(),
|
||||
format!("-rpcuser={}", RPC_USER),
|
||||
format!("-rpcpassword={}", RPC_PASSWORD),
|
||||
"-printtoconsole".to_string(),
|
||||
"-rest".to_string(),
|
||||
"-fallbackfee=0.0002".to_string(),
|
||||
format!("-datadir={}", DATADIR),
|
||||
format!("-rpcport={}", RPC_PORT),
|
||||
format!("-port={}", PORT),
|
||||
"-rest".to_string(),
|
||||
];
|
||||
|
||||
args.into_iter()
|
||||
}
|
||||
}
|
156
swap/tests/testutils/electrs.rs
Normal file
156
swap/tests/testutils/electrs.rs
Normal file
@ -0,0 +1,156 @@
|
||||
use crate::testutils::bitcoind;
|
||||
use bitcoin::Network;
|
||||
use std::collections::HashMap;
|
||||
use testcontainers::{
|
||||
core::{Container, Docker, Port, WaitForMessage},
|
||||
Image,
|
||||
};
|
||||
|
||||
pub const HTTP_PORT: u16 = 60401;
|
||||
pub const RPC_PORT: u16 = 3002;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Electrs {
|
||||
tag: String,
|
||||
args: ElectrsArgs,
|
||||
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(), bitcoind::DATADIR.to_string());
|
||||
volumes
|
||||
}
|
||||
|
||||
fn env_vars(&self) -> Self::EnvVars {
|
||||
HashMap::new()
|
||||
}
|
||||
|
||||
fn ports(&self) -> Option<Vec<Port>> {
|
||||
None
|
||||
}
|
||||
|
||||
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(),
|
||||
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_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,
|
||||
}
|
||||
|
||||
impl Default for ElectrsArgs {
|
||||
fn default() -> Self {
|
||||
// todo: these "defaults" are only suitable for our tests and need to be looked
|
||||
// at
|
||||
ElectrsArgs {
|
||||
network: Network::Regtest,
|
||||
daemon_dir: bitcoind::DATADIR.to_string(),
|
||||
daemon_rpc_addr: format!("0.0.0.0:{}", bitcoind::RPC_PORT),
|
||||
cookie: format!("{}:{}", bitcoind::RPC_USER, bitcoind::RPC_PASSWORD),
|
||||
http_addr: format!("0.0.0.0:{}", HTTP_PORT),
|
||||
electrum_rpc_addr: format!("0.0.0.0:{}", RPC_PORT),
|
||||
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("-vvvvv".to_string());
|
||||
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()
|
||||
}
|
||||
}
|
@ -1,10 +1,18 @@
|
||||
mod bitcoind;
|
||||
mod electrs;
|
||||
|
||||
use crate::testutils;
|
||||
use bitcoin_harness::Bitcoind;
|
||||
use anyhow::{Context, Result};
|
||||
use bitcoin_harness::{BitcoindRpcApi, Client};
|
||||
use futures::{future::RemoteHandle, Future};
|
||||
use get_port::get_port;
|
||||
use libp2p::{core::Multiaddr, PeerId};
|
||||
use monero_harness::{image, Monero};
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
use std::{
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
};
|
||||
use swap::{
|
||||
bitcoin,
|
||||
bitcoin::{CancelTimelock, PunishTimelock},
|
||||
@ -21,19 +29,22 @@ use swap::{
|
||||
seed::Seed,
|
||||
};
|
||||
use tempfile::tempdir;
|
||||
use testcontainers::{clients::Cli, Container};
|
||||
use tokio::{sync::mpsc, task::JoinHandle};
|
||||
use testcontainers::{clients::Cli, Container, Docker, RunArgs};
|
||||
use tokio::{sync::mpsc, task::JoinHandle, time::sleep};
|
||||
use tracing_core::dispatcher::DefaultGuard;
|
||||
use tracing_log::LogTracer;
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
const TEST_WALLET_NAME: &str = "testwallet";
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct StartingBalances {
|
||||
pub xmr: monero::Amount,
|
||||
pub btc: bitcoin::Amount,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Clone)]
|
||||
struct BobParams {
|
||||
seed: Seed,
|
||||
db_path: PathBuf,
|
||||
@ -119,6 +130,11 @@ impl TestContext {
|
||||
|
||||
assert!(matches!(state, AliceState::BtcRedeemed));
|
||||
|
||||
self.alice_bitcoin_wallet
|
||||
.sync_wallet()
|
||||
.await
|
||||
.expect("Could not sync wallet");
|
||||
|
||||
let btc_balance_after_swap = self.alice_bitcoin_wallet.as_ref().balance().await.unwrap();
|
||||
assert_eq!(
|
||||
btc_balance_after_swap,
|
||||
@ -139,11 +155,12 @@ impl TestContext {
|
||||
let swap_handle = self.alice_swap_handle.recv().await.unwrap();
|
||||
let state = swap_handle.await.unwrap();
|
||||
|
||||
assert!(
|
||||
matches!(state, AliceState::XmrRefunded),
|
||||
"Alice state is not XmrRefunded: {}",
|
||||
state
|
||||
);
|
||||
assert!(matches!(state, AliceState::XmrRefunded));
|
||||
|
||||
self.alice_bitcoin_wallet
|
||||
.sync_wallet()
|
||||
.await
|
||||
.expect("Could not sync wallet");
|
||||
|
||||
let btc_balance_after_swap = self.alice_bitcoin_wallet.as_ref().balance().await.unwrap();
|
||||
assert_eq!(btc_balance_after_swap, self.alice_starting_balances.btc);
|
||||
@ -167,6 +184,11 @@ impl TestContext {
|
||||
pub async fn assert_alice_punished(&self, state: AliceState) {
|
||||
assert!(matches!(state, AliceState::BtcPunished));
|
||||
|
||||
self.alice_bitcoin_wallet
|
||||
.sync_wallet()
|
||||
.await
|
||||
.expect("Could not sync wallet");
|
||||
|
||||
let btc_balance_after_swap = self.alice_bitcoin_wallet.as_ref().balance().await.unwrap();
|
||||
assert_eq!(
|
||||
btc_balance_after_swap,
|
||||
@ -184,6 +206,11 @@ impl TestContext {
|
||||
}
|
||||
|
||||
pub async fn assert_bob_redeemed(&self, state: BobState) {
|
||||
self.bob_bitcoin_wallet
|
||||
.sync_wallet()
|
||||
.await
|
||||
.expect("Could not sync wallet");
|
||||
|
||||
let lock_tx_id = if let BobState::XmrRedeemed { tx_lock_id } = state {
|
||||
tx_lock_id
|
||||
} else {
|
||||
@ -217,6 +244,11 @@ impl TestContext {
|
||||
}
|
||||
|
||||
pub async fn assert_bob_refunded(&self, state: BobState) {
|
||||
self.bob_bitcoin_wallet
|
||||
.sync_wallet()
|
||||
.await
|
||||
.expect("Could not sync wallet");
|
||||
|
||||
let lock_tx_id = if let BobState::BtcRefunded(state4) = state {
|
||||
state4.tx_lock_id()
|
||||
} else {
|
||||
@ -249,6 +281,11 @@ impl TestContext {
|
||||
}
|
||||
|
||||
pub async fn assert_bob_punished(&self, state: BobState) {
|
||||
self.bob_bitcoin_wallet
|
||||
.sync_wallet()
|
||||
.await
|
||||
.expect("Could not sync wallet");
|
||||
|
||||
let lock_tx_id = if let BobState::BtcPunished { tx_lock_id } = state {
|
||||
tx_lock_id
|
||||
} else {
|
||||
@ -300,11 +337,27 @@ where
|
||||
.parse()
|
||||
.expect("failed to parse Alice's address");
|
||||
|
||||
let electrs_rpc_port = containers
|
||||
.electrs
|
||||
.get_host_port(testutils::electrs::RPC_PORT)
|
||||
.expect("Could not map electrs rpc port");
|
||||
let electrs_http_port = containers
|
||||
.electrs
|
||||
.get_host_port(testutils::electrs::HTTP_PORT)
|
||||
.expect("Could not map electrs http port");
|
||||
|
||||
let alice_seed = Seed::random().unwrap();
|
||||
let bob_seed = Seed::random().unwrap();
|
||||
|
||||
let (alice_bitcoin_wallet, alice_monero_wallet) = init_test_wallets(
|
||||
"alice",
|
||||
&containers.bitcoind,
|
||||
containers.bitcoind_url.clone(),
|
||||
&monero,
|
||||
alice_starting_balances.clone(),
|
||||
tempdir().unwrap().path(),
|
||||
electrs_rpc_port,
|
||||
electrs_http_port,
|
||||
alice_seed,
|
||||
)
|
||||
.await;
|
||||
|
||||
@ -320,9 +373,13 @@ where
|
||||
|
||||
let (bob_bitcoin_wallet, bob_monero_wallet) = init_test_wallets(
|
||||
"bob",
|
||||
&containers.bitcoind,
|
||||
containers.bitcoind_url,
|
||||
&monero,
|
||||
bob_starting_balances.clone(),
|
||||
tempdir().unwrap().path(),
|
||||
electrs_rpc_port,
|
||||
electrs_http_port,
|
||||
bob_seed,
|
||||
)
|
||||
.await;
|
||||
|
||||
@ -369,56 +426,224 @@ where
|
||||
testfn(test).await;
|
||||
}
|
||||
|
||||
fn random_prefix() -> String {
|
||||
use rand::{distributions::Alphanumeric, thread_rng, Rng};
|
||||
use std::iter;
|
||||
const LEN: usize = 8;
|
||||
let mut rng = thread_rng();
|
||||
let chars: String = iter::repeat(())
|
||||
.map(|()| rng.sample(Alphanumeric))
|
||||
.map(char::from)
|
||||
.take(LEN)
|
||||
.collect();
|
||||
chars
|
||||
}
|
||||
|
||||
async fn init_containers(cli: &Cli) -> (Monero, Containers<'_>) {
|
||||
let bitcoind = Bitcoind::new(&cli, "0.19.1").unwrap();
|
||||
let _ = bitcoind.init(5).await;
|
||||
let prefix = random_prefix();
|
||||
let bitcoind_name = format!("{}_{}", prefix, "bitcoind");
|
||||
let (bitcoind, bitcoind_url) =
|
||||
init_bitcoind_container(&cli, prefix.clone(), bitcoind_name.clone(), prefix.clone())
|
||||
.await
|
||||
.expect("could not init bitcoind");
|
||||
let electrs = init_electrs_container(&cli, prefix.clone(), bitcoind_name, prefix)
|
||||
.await
|
||||
.expect("could not init electrs");
|
||||
let (monero, monerods) = init_monero_container(&cli).await;
|
||||
(monero, Containers {
|
||||
bitcoind_url,
|
||||
bitcoind,
|
||||
monerods,
|
||||
electrs,
|
||||
})
|
||||
}
|
||||
|
||||
async fn init_bitcoind_container(
|
||||
cli: &Cli,
|
||||
volume: String,
|
||||
name: String,
|
||||
network: String,
|
||||
) -> Result<(Container<'_, Cli, bitcoind::Bitcoind>, Url)> {
|
||||
let image = bitcoind::Bitcoind::default()
|
||||
.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 a = docker
|
||||
.get_host_port(testutils::bitcoind::RPC_PORT)
|
||||
.context("Could not map bitcoind rpc port")?;
|
||||
|
||||
let bitcoind_url = {
|
||||
let input = format!(
|
||||
"http://{}:{}@localhost:{}",
|
||||
bitcoind::RPC_USER,
|
||||
bitcoind::RPC_PASSWORD,
|
||||
a
|
||||
);
|
||||
Url::parse(&input).unwrap()
|
||||
};
|
||||
|
||||
init_bitcoind(bitcoind_url.clone(), 5).await?;
|
||||
|
||||
Ok((docker, bitcoind_url.clone()))
|
||||
}
|
||||
|
||||
pub async fn init_electrs_container(
|
||||
cli: &Cli,
|
||||
volume: String,
|
||||
bitcoind_container_name: String,
|
||||
network: String,
|
||||
) -> Result<Container<'_, Cli, electrs::Electrs>> {
|
||||
let bitcoind_rpc_addr = format!(
|
||||
"{}:{}",
|
||||
bitcoind_container_name,
|
||||
testutils::bitcoind::RPC_PORT
|
||||
);
|
||||
let image = electrs::Electrs::default()
|
||||
.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);
|
||||
|
||||
Ok(docker)
|
||||
}
|
||||
|
||||
async fn mine(bitcoind_client: Client, reward_address: bitcoin::Address) -> Result<()> {
|
||||
loop {
|
||||
tokio::time::sleep(Duration::from_secs(1)).await;
|
||||
bitcoind_client
|
||||
.generatetoaddress(1, reward_address.clone(), None)
|
||||
.await?;
|
||||
}
|
||||
}
|
||||
|
||||
async fn init_bitcoind(node_url: Url, spendable_quantity: u32) -> Result<Client> {
|
||||
let bitcoind_client = Client::new(node_url.clone());
|
||||
|
||||
bitcoind_client
|
||||
.createwallet(TEST_WALLET_NAME, None, None, None, None)
|
||||
.await?;
|
||||
|
||||
let reward_address = bitcoind_client
|
||||
.with_wallet(TEST_WALLET_NAME)?
|
||||
.getnewaddress(None, None)
|
||||
.await?;
|
||||
|
||||
bitcoind_client
|
||||
.generatetoaddress(101 + spendable_quantity, reward_address.clone(), None)
|
||||
.await?;
|
||||
let _ = tokio::spawn(mine(bitcoind_client.clone(), reward_address));
|
||||
Ok(bitcoind_client)
|
||||
}
|
||||
|
||||
/// Send Bitcoin to the specified address, limited to the spendable bitcoin
|
||||
/// quantity.
|
||||
pub async fn mint(node_url: Url, address: bitcoin::Address, amount: bitcoin::Amount) -> Result<()> {
|
||||
let bitcoind_client = Client::new(node_url.clone());
|
||||
|
||||
bitcoind_client
|
||||
.send_to_address(TEST_WALLET_NAME, address.clone(), amount)
|
||||
.await?;
|
||||
|
||||
// Confirm the transaction
|
||||
let reward_address = bitcoind_client
|
||||
.with_wallet(TEST_WALLET_NAME)?
|
||||
.getnewaddress(None, None)
|
||||
.await?;
|
||||
bitcoind_client
|
||||
.generatetoaddress(1, reward_address, None)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn init_monero_container(
|
||||
cli: &Cli,
|
||||
) -> (
|
||||
Monero,
|
||||
Vec<Container<'_, Cli, monero_harness::image::Monero>>,
|
||||
) {
|
||||
let (monero, monerods) = Monero::new(&cli, None, vec!["alice".to_string(), "bob".to_string()])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
(monero, Containers { bitcoind, monerods })
|
||||
(monero, monerods)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
async fn init_test_wallets(
|
||||
name: &str,
|
||||
bitcoind: &Bitcoind<'_>,
|
||||
bitcoind_url: Url,
|
||||
monero: &Monero,
|
||||
starting_balances: StartingBalances,
|
||||
datadir: &Path,
|
||||
electrum_rpc_port: u16,
|
||||
electrum_http_port: u16,
|
||||
seed: Seed,
|
||||
) -> (Arc<bitcoin::Wallet>, Arc<monero::Wallet>) {
|
||||
monero
|
||||
.init(vec![(name, starting_balances.xmr.as_piconero())])
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let xmr_wallet = Arc::new(swap::monero::Wallet {
|
||||
let xmr_wallet = swap::monero::Wallet {
|
||||
inner: monero.wallet(name).unwrap().client(),
|
||||
network: monero::Network::default(),
|
||||
});
|
||||
};
|
||||
|
||||
let btc_wallet = Arc::new(
|
||||
swap::bitcoin::Wallet::new(name, bitcoind.node_url.clone(), bitcoin::Network::Regtest)
|
||||
.await
|
||||
.unwrap(),
|
||||
);
|
||||
let electrum_rpc_url = {
|
||||
let input = format!("tcp://@localhost:{}", electrum_rpc_port);
|
||||
Url::parse(&input).unwrap()
|
||||
};
|
||||
let electrum_http_url = {
|
||||
let input = format!("http://@localhost:{}", electrum_http_port);
|
||||
Url::parse(&input).unwrap()
|
||||
};
|
||||
|
||||
let btc_wallet = swap::bitcoin::Wallet::new(
|
||||
electrum_rpc_url,
|
||||
electrum_http_url,
|
||||
bitcoin::Network::Regtest,
|
||||
datadir,
|
||||
seed.extended_private_key(bitcoin::Network::Regtest)
|
||||
.expect("Could not create extended private key from seed")
|
||||
.private_key,
|
||||
)
|
||||
.await
|
||||
.expect("could not init btc wallet");
|
||||
|
||||
if starting_balances.btc != bitcoin::Amount::ZERO {
|
||||
bitcoind
|
||||
.mint(
|
||||
btc_wallet.inner.new_address().await.unwrap(),
|
||||
starting_balances.btc,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
mint(
|
||||
bitcoind_url,
|
||||
btc_wallet.new_address().await.unwrap(),
|
||||
starting_balances.btc,
|
||||
)
|
||||
.await
|
||||
.expect("could not mint btc starting balance");
|
||||
}
|
||||
|
||||
(btc_wallet, xmr_wallet)
|
||||
sleep(Duration::from_secs(5)).await;
|
||||
|
||||
btc_wallet
|
||||
.sync_wallet()
|
||||
.await
|
||||
.expect("Could not sync btc wallet");
|
||||
|
||||
(Arc::new(btc_wallet), Arc::new(xmr_wallet))
|
||||
}
|
||||
|
||||
// This is just to keep the containers alive
|
||||
#[allow(dead_code)]
|
||||
struct Containers<'a> {
|
||||
bitcoind: Bitcoind<'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.
|
||||
@ -427,7 +652,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.
|
||||
@ -438,16 +663,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()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user