mirror of
https://github.com/comit-network/xmr-btc-swap.git
synced 2024-12-29 01:16:24 -05:00
Merge #373
373: Alice verifies Bob's PSBT r=thomaseizinger a=thomaseizinger TODO: - [x] Update the changelog Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
This commit is contained in:
commit
7e5e00909a
@ -17,4 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
failing on non-english language systems preventing users from starting the swap-cli
|
||||
and asb.
|
||||
|
||||
### Security
|
||||
|
||||
- Fixed an issue where Alice would not verify if Bob's Bitcoin lock transaction is semantically correct, i.e. pays the agreed upon amount to an output owned by both of them.
|
||||
Fixing this required a **breaking change** on the network layer and hence old versions are not compatible with this version.
|
||||
|
||||
[Unreleased]: https://github.com/comit-network/xmr-btc-swap/compare/v0.3...HEAD
|
||||
|
355
Cargo.lock
generated
355
Cargo.lock
generated
@ -221,6 +221,16 @@ dependencies = [
|
||||
"keccak-hash 0.1.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"safemem",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.10.1"
|
||||
@ -251,9 +261,9 @@ dependencies = [
|
||||
"async-trait",
|
||||
"bdk-macros",
|
||||
"bitcoin",
|
||||
"electrum-client",
|
||||
"electrum-client 0.7.0",
|
||||
"js-sys",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"miniscript",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
@ -273,6 +283,22 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bdk-testutils"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6d9382c8dfda457f2be9b700ffd580f12babec5d34ee39343768f65724ddd64"
|
||||
dependencies = [
|
||||
"bitcoin",
|
||||
"bitcoincore-rpc",
|
||||
"electrum-client 0.6.0",
|
||||
"log 0.4.14",
|
||||
"miniscript",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"serial_test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bech32"
|
||||
version = "0.7.3"
|
||||
@ -326,7 +352,7 @@ dependencies = [
|
||||
"thiserror",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"url",
|
||||
"url 2.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -338,6 +364,19 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitcoincore-rpc"
|
||||
version = "0.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d708433972bf78bd5f909d1d288f9ac1cceeab1460edb954e962f83e1f440a3"
|
||||
dependencies = [
|
||||
"bitcoincore-rpc-json",
|
||||
"jsonrpc",
|
||||
"log 0.4.14",
|
||||
"serde",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitcoincore-rpc-json"
|
||||
version = "0.13.0"
|
||||
@ -886,6 +925,22 @@ version = "1.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "electrum-client"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21453800c95bb1aaa57490458c42d60c6277cb8a3e386030ec2381d5c2d4fa77"
|
||||
dependencies = [
|
||||
"bitcoin",
|
||||
"log 0.4.14",
|
||||
"rustls 0.16.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"socks",
|
||||
"webpki",
|
||||
"webpki-roots 0.19.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "electrum-client"
|
||||
version = "0.7.0"
|
||||
@ -893,7 +948,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4cab4d90cc575a7daab4cfed9e315912a88071bc47462e6be57516a2f01ccc89"
|
||||
dependencies = [
|
||||
"bitcoin",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"rustls 0.16.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -1050,7 +1105,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
"percent-encoding 2.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1194,7 +1249,7 @@ checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"typenum",
|
||||
"version_check",
|
||||
"version_check 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1395,6 +1450,25 @@ version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "494b4d60369511e7dea41cf646832512a94e542f68bb9c49e54518e0f468eb47"
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.10.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273"
|
||||
dependencies = [
|
||||
"base64 0.9.3",
|
||||
"httparse",
|
||||
"language-tags",
|
||||
"log 0.3.9",
|
||||
"mime 0.2.6",
|
||||
"num_cpus",
|
||||
"time 0.1.43",
|
||||
"traitobject",
|
||||
"typeable",
|
||||
"unicase",
|
||||
"url 1.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hyper"
|
||||
version = "0.14.5"
|
||||
@ -1426,14 +1500,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f9f7a97316d44c0af9b0301e65010573a853a9fc97046d7331d7f6bc0fd5a64"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"hyper",
|
||||
"log",
|
||||
"hyper 0.14.4",
|
||||
"log 0.4.14",
|
||||
"rustls 0.19.0",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"webpki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e"
|
||||
dependencies = [
|
||||
"matches",
|
||||
"unicode-bidi",
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "0.2.2"
|
||||
@ -1545,6 +1630,18 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "436f3455a8a4e9c7b14de9f1206198ee5d0bdc2db1b560339d2141093d7dd389"
|
||||
dependencies = [
|
||||
"hyper 0.10.16",
|
||||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jsonrpc_client"
|
||||
version = "0.5.1"
|
||||
@ -1556,7 +1653,7 @@ dependencies = [
|
||||
"reqwest",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"url",
|
||||
"url 2.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1589,6 +1686,12 @@ dependencies = [
|
||||
"tiny-keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "language-tags"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@ -1634,7 +1737,7 @@ dependencies = [
|
||||
"libp2p-tcp",
|
||||
"libp2p-yamux",
|
||||
"parity-multiaddr",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.1",
|
||||
"pin-project 1.0.5",
|
||||
"smallvec",
|
||||
"wasm-timer",
|
||||
@ -1646,7 +1749,7 @@ version = "0.1.0"
|
||||
source = "git+https://github.com/comit-network/rust-libp2p-async-await#7a9006ceddd132ef5d40a597936cf15381a5cfe1"
|
||||
dependencies = [
|
||||
"libp2p",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1664,11 +1767,11 @@ dependencies = [
|
||||
"futures-timer",
|
||||
"lazy_static",
|
||||
"libsecp256k1",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"multihash",
|
||||
"multistream-select",
|
||||
"parity-multiaddr",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.1",
|
||||
"pin-project 1.0.5",
|
||||
"prost",
|
||||
"prost-build",
|
||||
@ -1691,7 +1794,7 @@ checksum = "9712eb3e9f7dcc77cc5ca7d943b6a85ce4b1faaf91a67e003442412a26d6d6f8"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"libp2p-core",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"smallvec",
|
||||
"trust-dns-resolver",
|
||||
]
|
||||
@ -1706,9 +1809,9 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures",
|
||||
"libp2p-core",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"nohash-hasher",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.1",
|
||||
"rand 0.7.3",
|
||||
"smallvec",
|
||||
"unsigned-varint 0.7.0",
|
||||
@ -1725,7 +1828,7 @@ dependencies = [
|
||||
"futures",
|
||||
"lazy_static",
|
||||
"libp2p-core",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"prost",
|
||||
"prost-build",
|
||||
"rand 0.7.3",
|
||||
@ -1747,7 +1850,7 @@ dependencies = [
|
||||
"futures",
|
||||
"libp2p-core",
|
||||
"libp2p-swarm",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"lru",
|
||||
"minicbor",
|
||||
"rand 0.7.3",
|
||||
@ -1765,7 +1868,7 @@ dependencies = [
|
||||
"either",
|
||||
"futures",
|
||||
"libp2p-core",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"rand 0.7.3",
|
||||
"smallvec",
|
||||
"void",
|
||||
@ -1794,7 +1897,7 @@ dependencies = [
|
||||
"ipnet",
|
||||
"libc",
|
||||
"libp2p-core",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"socket2 0.4.0",
|
||||
"tokio",
|
||||
]
|
||||
@ -1807,7 +1910,7 @@ checksum = "96d6144cc94143fb0a8dd1e7c2fbcc32a2808168bcd1d69920635424d5993b7b"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"libp2p-core",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.1",
|
||||
"thiserror",
|
||||
"yamux",
|
||||
]
|
||||
@ -1834,6 +1937,15 @@ version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.2"
|
||||
@ -1843,6 +1955,15 @@ dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b"
|
||||
dependencies = [
|
||||
"log 0.4.14",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
@ -1906,6 +2027,15 @@ dependencies = [
|
||||
"autocfg 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0"
|
||||
dependencies = [
|
||||
"log 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mime"
|
||||
version = "0.3.16"
|
||||
@ -1958,7 +2088,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2182a122f3b7f3f5329cb1972cee089ba2459a0a80a56935e6e674f096f8d839"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"miow",
|
||||
"ntapi",
|
||||
"winapi 0.3.9",
|
||||
@ -2059,7 +2189,7 @@ checksum = "7d91ec0a2440aaff5f78ec35631a7027d50386c6163aa975f7caa0d5da4b6ff8"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"pin-project 1.0.5",
|
||||
"smallvec",
|
||||
"unsigned-varint 0.7.0",
|
||||
@ -2079,7 +2209,7 @@ checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
|
||||
dependencies = [
|
||||
"lexical-core",
|
||||
"memchr",
|
||||
"version_check",
|
||||
"version_check 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2208,11 +2338,11 @@ dependencies = [
|
||||
"byteorder",
|
||||
"data-encoding",
|
||||
"multihash",
|
||||
"percent-encoding",
|
||||
"percent-encoding 2.1.0",
|
||||
"serde",
|
||||
"static_assertions 1.1.0",
|
||||
"unsigned-varint 0.7.0",
|
||||
"url",
|
||||
"url 2.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2227,6 +2357,16 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e"
|
||||
dependencies = [
|
||||
"lock_api 0.3.4",
|
||||
"parking_lot_core 0.7.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot"
|
||||
version = "0.11.1"
|
||||
@ -2234,8 +2374,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
|
||||
dependencies = [
|
||||
"instant",
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
"lock_api 0.4.2",
|
||||
"parking_lot_core 0.8.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"cloudabi",
|
||||
"libc",
|
||||
"redox_syscall 0.1.57",
|
||||
"smallvec",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2263,6 +2417,12 @@ dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.1.0"
|
||||
@ -2414,7 +2574,7 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"version_check",
|
||||
"version_check 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2425,7 +2585,7 @@ checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"version_check",
|
||||
"version_check 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -2468,7 +2628,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"heck",
|
||||
"itertools",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"multimap",
|
||||
"petgraph",
|
||||
"prost",
|
||||
@ -2826,14 +2986,14 @@ dependencies = [
|
||||
"futures-util",
|
||||
"http",
|
||||
"http-body",
|
||||
"hyper",
|
||||
"hyper 0.14.4",
|
||||
"hyper-rustls",
|
||||
"ipnet",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"mime",
|
||||
"percent-encoding",
|
||||
"log 0.4.14",
|
||||
"mime 0.3.16",
|
||||
"percent-encoding 2.1.0",
|
||||
"pin-project-lite",
|
||||
"rustls 0.19.0",
|
||||
"serde",
|
||||
@ -2841,7 +3001,7 @@ dependencies = [
|
||||
"serde_urlencoded",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
"url",
|
||||
"url 2.2.1",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
"web-sys",
|
||||
@ -2925,7 +3085,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b25a18b1bf7387f0145e7f8324e700805aade3842dd3db2e74e4cdeb4677c09e"
|
||||
dependencies = [
|
||||
"base64 0.10.1",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
@ -2938,7 +3098,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "064fd21ff87c6e87ed4506e68beb42459caa4a0e2eb144932e6776768556980b"
|
||||
dependencies = [
|
||||
"base64 0.13.0",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"ring",
|
||||
"sct",
|
||||
"webpki",
|
||||
@ -2961,6 +3121,12 @@ version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
|
||||
|
||||
[[package]]
|
||||
name = "safemem"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
@ -3097,6 +3263,28 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fef5f7c7434b2f2c598adc6f9494648a1e41274a75c0ba4056f680ae0c117fd6"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"parking_lot 0.10.2",
|
||||
"serial_test_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test_derive"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d08338d8024b227c62bd68a12c7c9883f5c66780abaef15c550dc56f46ee6515"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha-1"
|
||||
version = "0.9.4"
|
||||
@ -3196,8 +3384,8 @@ dependencies = [
|
||||
"fs2",
|
||||
"fxhash",
|
||||
"libc",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"log 0.4.14",
|
||||
"parking_lot 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3278,7 +3466,7 @@ version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2beb4d1860a61f571530b3f855a1b538d0200f7871c63331ecd6f17b1f014f8"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
"version_check 0.9.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3426,6 +3614,7 @@ dependencies = [
|
||||
"backoff",
|
||||
"base64 0.13.0",
|
||||
"bdk",
|
||||
"bdk-testutils",
|
||||
"big-bytes",
|
||||
"bitcoin",
|
||||
"bitcoin-harness",
|
||||
@ -3437,7 +3626,7 @@ dependencies = [
|
||||
"ecdsa_fun",
|
||||
"futures",
|
||||
"get-port",
|
||||
"hyper",
|
||||
"hyper 0.14.4",
|
||||
"libp2p",
|
||||
"libp2p-async-await",
|
||||
"miniscript",
|
||||
@ -3472,7 +3661,7 @@ dependencies = [
|
||||
"tracing",
|
||||
"tracing-futures",
|
||||
"tracing-subscriber",
|
||||
"url",
|
||||
"url 2.2.1",
|
||||
"uuid",
|
||||
"void",
|
||||
"zip",
|
||||
@ -3545,7 +3734,7 @@ dependencies = [
|
||||
"derivative",
|
||||
"hex 0.4.3",
|
||||
"hmac 0.8.1",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"rand 0.7.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -3560,7 +3749,7 @@ checksum = "d5e3ed6e3598dbf32cba8cb356b881c085e0adea57597f387723430dd94b4084"
|
||||
dependencies = [
|
||||
"hex 0.4.3",
|
||||
"hmac 0.10.1",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"rand 0.8.3",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@ -3626,7 +3815,7 @@ dependencies = [
|
||||
"standback",
|
||||
"stdweb",
|
||||
"time-macros",
|
||||
"version_check",
|
||||
"version_check 0.9.3",
|
||||
"winapi 0.3.9",
|
||||
]
|
||||
|
||||
@ -3690,7 +3879,7 @@ dependencies = [
|
||||
"mio",
|
||||
"num_cpus",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.1",
|
||||
"pin-project-lite",
|
||||
"signal-hook-registry",
|
||||
"tokio-macros",
|
||||
@ -3751,7 +3940,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e96bb520beab540ab664bd5a9cfeaa1fcd846fa68c830b42e2c8963071251d2"
|
||||
dependencies = [
|
||||
"futures-util",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"pin-project 1.0.5",
|
||||
"rustls 0.19.0",
|
||||
"tokio",
|
||||
@ -3770,7 +3959,7 @@ dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"pin-project-lite",
|
||||
"tokio",
|
||||
]
|
||||
@ -3841,7 +4030,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6923477a48e41c1951f1999ef8bb5a3023eb723ceadafe78ffb65dc366761e3"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"tracing-core",
|
||||
]
|
||||
|
||||
@ -3863,6 +4052,12 @@ dependencies = [
|
||||
"tracing-log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "traitobject"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079"
|
||||
|
||||
[[package]]
|
||||
name = "trust-dns-proto"
|
||||
version = "0.20.1"
|
||||
@ -3876,16 +4071,16 @@ dependencies = [
|
||||
"futures-channel",
|
||||
"futures-io",
|
||||
"futures-util",
|
||||
"idna",
|
||||
"idna 0.2.2",
|
||||
"ipnet",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"rand 0.8.3",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"tinyvec",
|
||||
"tokio",
|
||||
"url",
|
||||
"url 2.2.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -3898,9 +4093,9 @@ dependencies = [
|
||||
"futures-util",
|
||||
"ipconfig",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"lru-cache",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.1",
|
||||
"resolv-conf",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
@ -3926,17 +4121,23 @@ dependencies = [
|
||||
"http",
|
||||
"httparse",
|
||||
"input_buffer",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"rand 0.8.3",
|
||||
"rustls 0.19.0",
|
||||
"sha-1",
|
||||
"thiserror",
|
||||
"url",
|
||||
"url 2.2.1",
|
||||
"utf-8",
|
||||
"webpki",
|
||||
"webpki-roots 0.21.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typeable"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.13.0"
|
||||
@ -3967,6 +4168,15 @@ dependencies = [
|
||||
"static_assertions 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33"
|
||||
dependencies = [
|
||||
"version_check 0.1.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.4"
|
||||
@ -4037,6 +4247,17 @@ version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "1.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a"
|
||||
dependencies = [
|
||||
"idna 0.1.5",
|
||||
"matches",
|
||||
"percent-encoding 1.0.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "url"
|
||||
version = "2.2.1"
|
||||
@ -4044,9 +4265,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ccd964113622c8e9322cfac19eb1004a07e636c545f325da085d5cdde6f1f8b"
|
||||
dependencies = [
|
||||
"form_urlencoded",
|
||||
"idna",
|
||||
"idna 0.2.2",
|
||||
"matches",
|
||||
"percent-encoding",
|
||||
"percent-encoding 2.1.0",
|
||||
"serde",
|
||||
]
|
||||
|
||||
@ -4072,6 +4293,12 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.3"
|
||||
@ -4090,7 +4317,7 @@ version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0"
|
||||
dependencies = [
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"try-lock",
|
||||
]
|
||||
|
||||
@ -4126,7 +4353,7 @@ checksum = "5b7d8b6942b8bb3a9b0e73fc79b98095a27de6fa247615e59d096754a3bc2aa8"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
@ -4182,7 +4409,7 @@ checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"js-sys",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.1",
|
||||
"pin-utils",
|
||||
"wasm-bindgen",
|
||||
"wasm-bindgen-futures",
|
||||
@ -4332,9 +4559,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1cc7bd8c983209ed5d527f44b01c41b7dc146fd960c61cf9e1d25399841dc271"
|
||||
dependencies = [
|
||||
"futures",
|
||||
"log",
|
||||
"log 0.4.14",
|
||||
"nohash-hasher",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.1",
|
||||
"rand 0.7.3",
|
||||
"static_assertions 1.1.0",
|
||||
]
|
||||
|
@ -64,6 +64,7 @@ tokio-tar = { path = "../tokio-tar" }
|
||||
zip = "0.5"
|
||||
|
||||
[dev-dependencies]
|
||||
bdk-testutils = { version = "0.3" }
|
||||
bitcoin-harness = { git = "https://github.com/coblox/bitcoin-harness-rs" }
|
||||
get-port = "3"
|
||||
hyper = "0.14"
|
||||
|
@ -14,6 +14,7 @@ pub use crate::bitcoin::redeem::TxRedeem;
|
||||
pub use crate::bitcoin::refund::TxRefund;
|
||||
pub use crate::bitcoin::timelocks::{BlockHeight, ExpiredTimelocks};
|
||||
pub use ::bitcoin::util::amount::Amount;
|
||||
pub use ::bitcoin::util::psbt::PartiallySignedTransaction;
|
||||
pub use ::bitcoin::{Address, Network, Transaction, Txid};
|
||||
pub use ecdsa_fun::adaptor::EncryptedSignature;
|
||||
pub use ecdsa_fun::fun::Scalar;
|
||||
@ -105,6 +106,13 @@ impl SecretKey {
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize, PartialEq)]
|
||||
pub struct PublicKey(Point);
|
||||
|
||||
impl PublicKey {
|
||||
#[cfg(test)]
|
||||
pub fn random() -> Self {
|
||||
Self(Point::random(&mut rand::thread_rng()))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PublicKey> for Point {
|
||||
fn from(from: PublicKey) -> Self {
|
||||
from.0
|
||||
|
@ -4,7 +4,8 @@ use crate::bitcoin::{
|
||||
};
|
||||
use ::bitcoin::util::psbt::PartiallySignedTransaction;
|
||||
use ::bitcoin::{OutPoint, TxIn, TxOut, Txid};
|
||||
use anyhow::Result;
|
||||
use anyhow::{bail, Result};
|
||||
use bdk::database::BatchDatabase;
|
||||
use bitcoin::Script;
|
||||
use ecdsa_fun::fun::Point;
|
||||
use miniscript::{Descriptor, DescriptorTrait};
|
||||
@ -18,10 +19,18 @@ pub struct TxLock {
|
||||
}
|
||||
|
||||
impl TxLock {
|
||||
pub async fn new(wallet: &Wallet, amount: Amount, A: PublicKey, B: PublicKey) -> Result<Self> {
|
||||
pub async fn new<B, D, C>(
|
||||
wallet: &Wallet<B, D, C>,
|
||||
amount: Amount,
|
||||
A: PublicKey,
|
||||
B: PublicKey,
|
||||
) -> Result<Self>
|
||||
where
|
||||
D: BatchDatabase,
|
||||
{
|
||||
let lock_output_descriptor = build_shared_output_descriptor(A.0, B.0);
|
||||
let address = lock_output_descriptor
|
||||
.address(wallet.get_network().await)
|
||||
.address(wallet.get_network())
|
||||
.expect("can derive address from descriptor");
|
||||
|
||||
let psbt = wallet.send_to_address(address, amount).await?;
|
||||
@ -32,6 +41,56 @@ impl TxLock {
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates an instance of `TxLock` from a PSBT, the public keys of the
|
||||
/// parties and the specified amount.
|
||||
///
|
||||
/// This function validates that the given PSBT does indeed pay that
|
||||
/// specified amount to a shared output.
|
||||
pub fn from_psbt(
|
||||
psbt: PartiallySignedTransaction,
|
||||
A: PublicKey,
|
||||
B: PublicKey,
|
||||
btc: Amount,
|
||||
) -> Result<Self> {
|
||||
let shared_output_candidate = match psbt.global.unsigned_tx.output.as_slice() {
|
||||
[shared_output_candidate, _] if shared_output_candidate.value == btc.as_sat() => {
|
||||
shared_output_candidate
|
||||
}
|
||||
[_, shared_output_candidate] if shared_output_candidate.value == btc.as_sat() => {
|
||||
shared_output_candidate
|
||||
}
|
||||
// A single output is possible if Bob funds without any change necessary
|
||||
[shared_output_candidate] if shared_output_candidate.value == btc.as_sat() => {
|
||||
shared_output_candidate
|
||||
}
|
||||
[_, _] => {
|
||||
bail!("Neither of the two provided outputs pays the right amount!");
|
||||
}
|
||||
[_] => {
|
||||
bail!("The provided output does not pay the right amount!");
|
||||
}
|
||||
other => {
|
||||
let num_outputs = other.len();
|
||||
bail!(
|
||||
"PSBT has {} outputs, expected one or two. Something is fishy!",
|
||||
num_outputs
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let descriptor = build_shared_output_descriptor(A.0, B.0);
|
||||
let legit_shared_output_script = descriptor.script_pubkey();
|
||||
|
||||
if shared_output_candidate.script_pubkey != legit_shared_output_script {
|
||||
bail!("Output script is not a shared output")
|
||||
}
|
||||
|
||||
Ok(TxLock {
|
||||
inner: psbt,
|
||||
output_descriptor: descriptor,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lock_amount(&self) -> Amount {
|
||||
Amount::from_sat(self.inner.clone().extract_tx().output[self.lock_output_vout()].value)
|
||||
}
|
||||
@ -116,3 +175,84 @@ impl Watchable for TxLock {
|
||||
self.output_descriptor.script_pubkey()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[tokio::test]
|
||||
async fn given_bob_sends_good_psbt_when_reconstructing_then_succeeeds() {
|
||||
let (A, B) = alice_and_bob();
|
||||
let wallet = Wallet::new_funded(50000);
|
||||
let agreed_amount = Amount::from_sat(10000);
|
||||
|
||||
let psbt = bob_make_psbt(A, B, &wallet, agreed_amount).await;
|
||||
let result = TxLock::from_psbt(psbt, A, B, agreed_amount);
|
||||
|
||||
result.expect("PSBT to be valid");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn bob_can_fund_without_a_change_output() {
|
||||
let (A, B) = alice_and_bob();
|
||||
let fees = 610;
|
||||
let agreed_amount = Amount::from_sat(10000);
|
||||
let wallet = Wallet::new_funded(agreed_amount.as_sat() + fees);
|
||||
|
||||
let psbt = bob_make_psbt(A, B, &wallet, agreed_amount).await;
|
||||
assert_eq!(
|
||||
psbt.global.unsigned_tx.output.len(),
|
||||
1,
|
||||
"psbt should only have a single output"
|
||||
);
|
||||
let result = TxLock::from_psbt(psbt, A, B, agreed_amount);
|
||||
|
||||
result.expect("PSBT to be valid");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn given_bob_is_sending_less_than_agreed_when_reconstructing_txlock_then_fails() {
|
||||
let (A, B) = alice_and_bob();
|
||||
let wallet = Wallet::new_funded(50000);
|
||||
let agreed_amount = Amount::from_sat(10000);
|
||||
|
||||
let bad_amount = Amount::from_sat(5000);
|
||||
let psbt = bob_make_psbt(A, B, &wallet, bad_amount).await;
|
||||
let result = TxLock::from_psbt(psbt, A, B, agreed_amount);
|
||||
|
||||
result.expect_err("PSBT to be invalid");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn given_bob_is_sending_to_a_bad_output_reconstructing_txlock_then_fails() {
|
||||
let (A, B) = alice_and_bob();
|
||||
let wallet = Wallet::new_funded(50000);
|
||||
let agreed_amount = Amount::from_sat(10000);
|
||||
|
||||
let E = eve();
|
||||
let psbt = bob_make_psbt(E, B, &wallet, agreed_amount).await;
|
||||
let result = TxLock::from_psbt(psbt, A, B, agreed_amount);
|
||||
|
||||
result.expect_err("PSBT to be invalid");
|
||||
}
|
||||
|
||||
/// Helper function that represents Bob's action of constructing the PSBT.
|
||||
///
|
||||
/// Extracting this allows us to keep the tests concise.
|
||||
async fn bob_make_psbt(
|
||||
A: PublicKey,
|
||||
B: PublicKey,
|
||||
wallet: &Wallet<(), bdk::database::MemoryDatabase, ()>,
|
||||
amount: Amount,
|
||||
) -> PartiallySignedTransaction {
|
||||
TxLock::new(&wallet, amount, A, B).await.unwrap().into()
|
||||
}
|
||||
|
||||
fn alice_and_bob() -> (PublicKey, PublicKey) {
|
||||
(PublicKey::random(), PublicKey::random())
|
||||
}
|
||||
|
||||
fn eve() -> PublicKey {
|
||||
PublicKey::random()
|
||||
}
|
||||
}
|
||||
|
@ -5,11 +5,12 @@ use ::bitcoin::util::psbt::PartiallySignedTransaction;
|
||||
use ::bitcoin::Txid;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use bdk::blockchain::{noop_progress, Blockchain, ElectrumBlockchain};
|
||||
use bdk::database::BatchDatabase;
|
||||
use bdk::descriptor::Segwitv0;
|
||||
use bdk::electrum_client::{ElectrumApi, GetHistoryRes};
|
||||
use bdk::keys::DerivableKey;
|
||||
use bdk::{FeeRate, KeychainKind};
|
||||
use bitcoin::Script;
|
||||
use bitcoin::{Network, Script};
|
||||
use reqwest::Url;
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::convert::TryFrom;
|
||||
@ -21,10 +22,11 @@ use tokio::sync::{watch, Mutex};
|
||||
|
||||
const SLED_TREE_NAME: &str = "default_tree";
|
||||
|
||||
pub struct Wallet {
|
||||
client: Arc<Mutex<Client>>,
|
||||
wallet: Arc<Mutex<bdk::Wallet<ElectrumBlockchain, bdk::sled::Tree>>>,
|
||||
pub struct Wallet<B = ElectrumBlockchain, D = bdk::sled::Tree, C = Client> {
|
||||
client: Arc<Mutex<C>>,
|
||||
wallet: Arc<Mutex<bdk::Wallet<B, D>>>,
|
||||
finality_confirmations: u32,
|
||||
network: Network,
|
||||
}
|
||||
|
||||
impl Wallet {
|
||||
@ -39,7 +41,7 @@ impl Wallet {
|
||||
|
||||
let db = bdk::sled::open(wallet_dir)?.open_tree(SLED_TREE_NAME)?;
|
||||
|
||||
let bdk_wallet = bdk::Wallet::new(
|
||||
let wallet = bdk::Wallet::new(
|
||||
bdk::template::BIP84(key.clone(), KeychainKind::External),
|
||||
Some(bdk::template::BIP84(key, KeychainKind::Internal)),
|
||||
env_config.bitcoin_network,
|
||||
@ -50,108 +52,19 @@ impl Wallet {
|
||||
let electrum = bdk::electrum_client::Client::new(electrum_rpc_url.as_str())
|
||||
.context("Failed to initialize Electrum RPC client")?;
|
||||
|
||||
let network = wallet.network();
|
||||
|
||||
Ok(Self {
|
||||
wallet: Arc::new(Mutex::new(bdk_wallet)),
|
||||
client: Arc::new(Mutex::new(Client::new(
|
||||
electrum,
|
||||
env_config.bitcoin_sync_interval(),
|
||||
)?)),
|
||||
wallet: Arc::new(Mutex::new(wallet)),
|
||||
finality_confirmations: env_config.bitcoin_finality_confirmations,
|
||||
network,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn balance(&self) -> Result<Amount> {
|
||||
let balance = self
|
||||
.wallet
|
||||
.lock()
|
||||
.await
|
||||
.get_balance()
|
||||
.context("Failed to calculate Bitcoin balance")?;
|
||||
|
||||
Ok(Amount::from_sat(balance))
|
||||
}
|
||||
|
||||
pub async fn new_address(&self) -> Result<Address> {
|
||||
let address = self
|
||||
.wallet
|
||||
.lock()
|
||||
.await
|
||||
.get_new_address()
|
||||
.context("Failed to get new Bitcoin address")?;
|
||||
|
||||
Ok(address)
|
||||
}
|
||||
|
||||
pub async fn get_tx(&self, txid: Txid) -> Result<Option<Transaction>> {
|
||||
let tx = self.wallet.lock().await.client().get_tx(&txid)?;
|
||||
|
||||
Ok(tx)
|
||||
}
|
||||
|
||||
pub async fn transaction_fee(&self, txid: Txid) -> Result<Amount> {
|
||||
let fees = self
|
||||
.wallet
|
||||
.lock()
|
||||
.await
|
||||
.list_transactions(true)?
|
||||
.iter()
|
||||
.find(|tx| tx.txid == txid)
|
||||
.context("Could not find tx in bdk wallet when trying to determine fees")?
|
||||
.fees;
|
||||
|
||||
Ok(Amount::from_sat(fees))
|
||||
}
|
||||
|
||||
pub async fn sync(&self) -> Result<()> {
|
||||
self.wallet
|
||||
.lock()
|
||||
.await
|
||||
.sync(noop_progress(), None)
|
||||
.context("Failed to sync balance of Bitcoin wallet")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_to_address(
|
||||
&self,
|
||||
address: Address,
|
||||
amount: Amount,
|
||||
) -> Result<PartiallySignedTransaction> {
|
||||
let wallet = self.wallet.lock().await;
|
||||
|
||||
let mut tx_builder = wallet.build_tx();
|
||||
tx_builder.add_recipient(address.script_pubkey(), amount.as_sat());
|
||||
tx_builder.fee_rate(self.select_feerate());
|
||||
let (psbt, _details) = tx_builder.finish()?;
|
||||
|
||||
Ok(psbt)
|
||||
}
|
||||
|
||||
/// Calculates the maximum "giveable" amount of this wallet.
|
||||
///
|
||||
/// We define this as the maximum amount we can pay to a single output,
|
||||
/// already accounting for the fees we need to spend to get the
|
||||
/// transaction confirmed.
|
||||
pub async fn max_giveable(&self, locking_script_size: usize) -> Result<Amount> {
|
||||
let wallet = self.wallet.lock().await;
|
||||
|
||||
let mut tx_builder = wallet.build_tx();
|
||||
|
||||
let dummy_script = Script::from(vec![0u8; locking_script_size]);
|
||||
tx_builder.set_single_recipient(dummy_script);
|
||||
tx_builder.drain_wallet();
|
||||
tx_builder.fee_rate(self.select_feerate());
|
||||
let (_, details) = tx_builder.finish().context("Failed to build transaction")?;
|
||||
|
||||
let max_giveable = details.sent - details.fees;
|
||||
|
||||
Ok(Amount::from_sat(max_giveable))
|
||||
}
|
||||
|
||||
pub async fn get_network(&self) -> bitcoin::Network {
|
||||
self.wallet.lock().await.network()
|
||||
}
|
||||
|
||||
/// Broadcast the given transaction to the network and emit a log statement
|
||||
/// if done so successfully.
|
||||
///
|
||||
@ -260,13 +173,6 @@ impl Wallet {
|
||||
|
||||
sub
|
||||
}
|
||||
|
||||
/// Selects an appropriate [`FeeRate`] to be used for getting transactions
|
||||
/// confirmed within a reasonable amount of time.
|
||||
fn select_feerate(&self) -> FeeRate {
|
||||
// TODO: This should obviously not be a const :)
|
||||
FeeRate::from_sat_per_vb(5.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a subscription to the status of a given transaction.
|
||||
@ -329,6 +235,152 @@ impl Subscription {
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, D, C> Wallet<B, D, C>
|
||||
where
|
||||
D: BatchDatabase,
|
||||
{
|
||||
pub async fn balance(&self) -> Result<Amount> {
|
||||
let balance = self
|
||||
.wallet
|
||||
.lock()
|
||||
.await
|
||||
.get_balance()
|
||||
.context("Failed to calculate Bitcoin balance")?;
|
||||
|
||||
Ok(Amount::from_sat(balance))
|
||||
}
|
||||
|
||||
pub async fn new_address(&self) -> Result<Address> {
|
||||
let address = self
|
||||
.wallet
|
||||
.lock()
|
||||
.await
|
||||
.get_new_address()
|
||||
.context("Failed to get new Bitcoin address")?;
|
||||
|
||||
Ok(address)
|
||||
}
|
||||
|
||||
pub async fn transaction_fee(&self, txid: Txid) -> Result<Amount> {
|
||||
let fees = self
|
||||
.wallet
|
||||
.lock()
|
||||
.await
|
||||
.list_transactions(true)?
|
||||
.iter()
|
||||
.find(|tx| tx.txid == txid)
|
||||
.context("Could not find tx in bdk wallet when trying to determine fees")?
|
||||
.fees;
|
||||
|
||||
Ok(Amount::from_sat(fees))
|
||||
}
|
||||
|
||||
pub async fn send_to_address(
|
||||
&self,
|
||||
address: Address,
|
||||
amount: Amount,
|
||||
) -> Result<PartiallySignedTransaction> {
|
||||
let wallet = self.wallet.lock().await;
|
||||
|
||||
let mut tx_builder = wallet.build_tx();
|
||||
tx_builder.add_recipient(address.script_pubkey(), amount.as_sat());
|
||||
tx_builder.fee_rate(self.select_feerate());
|
||||
let (psbt, _details) = tx_builder.finish()?;
|
||||
|
||||
Ok(psbt)
|
||||
}
|
||||
|
||||
/// Calculates the maximum "giveable" amount of this wallet.
|
||||
///
|
||||
/// We define this as the maximum amount we can pay to a single output,
|
||||
/// already accounting for the fees we need to spend to get the
|
||||
/// transaction confirmed.
|
||||
pub async fn max_giveable(&self, locking_script_size: usize) -> Result<Amount> {
|
||||
let wallet = self.wallet.lock().await;
|
||||
|
||||
let mut tx_builder = wallet.build_tx();
|
||||
|
||||
let dummy_script = Script::from(vec![0u8; locking_script_size]);
|
||||
tx_builder.set_single_recipient(dummy_script);
|
||||
tx_builder.drain_wallet();
|
||||
tx_builder.fee_rate(self.select_feerate());
|
||||
let (_, details) = tx_builder.finish().context("Failed to build transaction")?;
|
||||
|
||||
let max_giveable = details.sent - details.fees;
|
||||
|
||||
Ok(Amount::from_sat(max_giveable))
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, D, C> Wallet<B, D, C>
|
||||
where
|
||||
B: Blockchain,
|
||||
D: BatchDatabase,
|
||||
{
|
||||
pub async fn get_tx(&self, txid: Txid) -> Result<Option<Transaction>> {
|
||||
let tx = self.wallet.lock().await.client().get_tx(&txid)?;
|
||||
|
||||
Ok(tx)
|
||||
}
|
||||
|
||||
pub async fn sync(&self) -> Result<()> {
|
||||
self.wallet
|
||||
.lock()
|
||||
.await
|
||||
.sync(noop_progress(), None)
|
||||
.context("Failed to sync balance of Bitcoin wallet")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<B, D, C> Wallet<B, D, C> {
|
||||
// TODO: Get rid of this by changing bounds on bdk::Wallet
|
||||
pub fn get_network(&self) -> bitcoin::Network {
|
||||
self.network
|
||||
}
|
||||
|
||||
/// Selects an appropriate [`FeeRate`] to be used for getting transactions
|
||||
/// confirmed within a reasonable amount of time.
|
||||
fn select_feerate(&self) -> FeeRate {
|
||||
// TODO: This should obviously not be a const :)
|
||||
FeeRate::from_sat_per_vb(5.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl Wallet<(), bdk::database::MemoryDatabase, ()> {
|
||||
/// Creates a new, funded wallet to be used within tests.
|
||||
pub fn new_funded(amount: u64) -> Self {
|
||||
use bdk::database::MemoryDatabase;
|
||||
use bdk::{LocalUtxo, TransactionDetails};
|
||||
use bitcoin::OutPoint;
|
||||
use std::str::FromStr;
|
||||
use testutils::testutils;
|
||||
|
||||
let descriptors = testutils!(@descriptors ("wpkh(tpubEBr4i6yk5nf5DAaJpsi9N2pPYBeJ7fZ5Z9rmN4977iYLCGco1VyjB9tvvuvYtfZzjD5A8igzgw3HeWeeKFmanHYqksqZXYXGsw5zjnj7KM9/*)"));
|
||||
|
||||
let mut database = MemoryDatabase::new();
|
||||
bdk::populate_test_db!(
|
||||
&mut database,
|
||||
testutils! {
|
||||
@tx ( (@external descriptors, 0) => amount ) (@confirmations 1)
|
||||
},
|
||||
Some(100)
|
||||
);
|
||||
|
||||
let wallet =
|
||||
bdk::Wallet::new_offline(&descriptors.0, None, Network::Regtest, database).unwrap();
|
||||
|
||||
Self {
|
||||
client: Arc::new(Mutex::new(())),
|
||||
wallet: Arc::new(Mutex::new(wallet)),
|
||||
finality_confirmations: 1,
|
||||
network: Network::Regtest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Defines a watchable transaction.
|
||||
///
|
||||
/// For a transaction to be watchable, we need to know two things: Its
|
||||
@ -350,7 +402,7 @@ impl Watchable for (Txid, Script) {
|
||||
}
|
||||
}
|
||||
|
||||
struct Client {
|
||||
pub struct Client {
|
||||
electrum: bdk::electrum_client::Client,
|
||||
latest_block: BlockHeight,
|
||||
last_ping: Instant,
|
||||
|
@ -1,7 +1,9 @@
|
||||
use crate::{bitcoin, monero};
|
||||
use conquer_once::Lazy;
|
||||
use ecdsa_fun::fun::marker::Mark;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::Sha256;
|
||||
use sigma_fun::ext::dl_secp256k1_ed25519_eq::CrossCurveDLEQ;
|
||||
use sigma_fun::ext::dl_secp256k1_ed25519_eq::{CrossCurveDLEQ, CrossCurveDLEQProof};
|
||||
use sigma_fun::HashTranscript;
|
||||
|
||||
pub mod alice;
|
||||
@ -18,6 +20,44 @@ pub static CROSS_CURVE_PROOF_SYSTEM: Lazy<
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct StartingBalances {
|
||||
pub xmr: crate::monero::Amount,
|
||||
pub xmr: monero::Amount,
|
||||
pub btc: bitcoin::Amount,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Message0 {
|
||||
B: bitcoin::PublicKey,
|
||||
S_b_monero: monero::PublicKey,
|
||||
S_b_bitcoin: bitcoin::PublicKey,
|
||||
dleq_proof_s_b: CrossCurveDLEQProof,
|
||||
v_b: monero::PrivateViewKey,
|
||||
refund_address: bitcoin::Address,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Message1 {
|
||||
A: bitcoin::PublicKey,
|
||||
S_a_monero: monero::PublicKey,
|
||||
S_a_bitcoin: bitcoin::PublicKey,
|
||||
dleq_proof_s_a: CrossCurveDLEQProof,
|
||||
v_a: monero::PrivateViewKey,
|
||||
redeem_address: bitcoin::Address,
|
||||
punish_address: bitcoin::Address,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Message2 {
|
||||
psbt: bitcoin::PartiallySignedTransaction,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Message3 {
|
||||
tx_cancel_sig: bitcoin::Signature,
|
||||
tx_refund_encsig: bitcoin::EncryptedSignature,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Message4 {
|
||||
tx_punish_sig: bitcoin::Signature,
|
||||
tx_cancel_sig: bitcoin::Signature,
|
||||
}
|
||||
|
@ -8,10 +8,8 @@ use uuid::Uuid;
|
||||
|
||||
pub use self::behaviour::{Behaviour, OutEvent};
|
||||
pub use self::event_loop::{EventLoop, EventLoopHandle};
|
||||
pub use self::execution_setup::Message1;
|
||||
pub use self::state::*;
|
||||
pub use self::swap::{run, run_until};
|
||||
pub use execution_setup::Message3;
|
||||
|
||||
mod behaviour;
|
||||
pub mod event_loop;
|
||||
|
@ -1,30 +1,9 @@
|
||||
use crate::bitcoin::{EncryptedSignature, Signature};
|
||||
use crate::network::cbor_request_response::BUF_SIZE;
|
||||
use crate::protocol::alice::{State0, State3};
|
||||
use crate::protocol::bob::{Message0, Message2, Message4};
|
||||
use crate::{bitcoin, monero};
|
||||
use crate::protocol::{Message0, Message2, Message4};
|
||||
use anyhow::{Context, Error};
|
||||
use libp2p::PeerId;
|
||||
use libp2p_async_await::BehaviourOutEvent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sigma_fun::ext::dl_secp256k1_ed25519_eq::CrossCurveDLEQProof;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Message1 {
|
||||
pub(crate) A: bitcoin::PublicKey,
|
||||
pub(crate) S_a_monero: monero::PublicKey,
|
||||
pub(crate) S_a_bitcoin: bitcoin::PublicKey,
|
||||
pub(crate) dleq_proof_s_a: CrossCurveDLEQProof,
|
||||
pub(crate) v_a: monero::PrivateViewKey,
|
||||
pub(crate) redeem_address: bitcoin::Address,
|
||||
pub(crate) punish_address: bitcoin::Address,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Message3 {
|
||||
pub(crate) tx_cancel_sig: Signature,
|
||||
pub(crate) tx_refund_encsig: EncryptedSignature,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OutEvent {
|
||||
@ -78,7 +57,9 @@ impl Behaviour {
|
||||
let message2 =
|
||||
serde_cbor::from_slice::<Message2>(&substream.read_message(BUF_SIZE).await?)
|
||||
.context("Failed to deserialize message2")?;
|
||||
let state2 = state1.receive(message2);
|
||||
let state2 = state1
|
||||
.receive(message2)
|
||||
.context("Failed to receive Message2")?;
|
||||
|
||||
substream
|
||||
.write_message(
|
||||
|
@ -4,9 +4,8 @@ use crate::bitcoin::{
|
||||
use crate::env::Config;
|
||||
use crate::monero::wallet::{TransferRequest, WatchRequest};
|
||||
use crate::monero::TransferProof;
|
||||
use crate::protocol::alice::{Message1, Message3};
|
||||
use crate::protocol::bob::{Message0, Message2, Message4};
|
||||
use crate::protocol::CROSS_CURVE_PROOF_SYSTEM;
|
||||
use crate::monero_ext::ScalarExt;
|
||||
use crate::protocol::{Message0, Message1, Message2, Message3, Message4, CROSS_CURVE_PROOF_SYSTEM};
|
||||
use crate::{bitcoin, monero};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use monero_rpc::wallet::BlockHeight;
|
||||
@ -74,21 +73,20 @@ impl fmt::Display for AliceState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct State0 {
|
||||
pub a: bitcoin::SecretKey,
|
||||
pub s_a: monero::Scalar,
|
||||
pub v_a: monero::PrivateViewKey,
|
||||
pub(crate) S_a_monero: monero::PublicKey,
|
||||
pub(crate) S_a_bitcoin: bitcoin::PublicKey,
|
||||
pub dleq_proof_s_a: CrossCurveDLEQProof,
|
||||
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||||
pub btc: bitcoin::Amount,
|
||||
pub xmr: monero::Amount,
|
||||
pub cancel_timelock: CancelTimelock,
|
||||
pub punish_timelock: PunishTimelock,
|
||||
pub redeem_address: bitcoin::Address,
|
||||
pub punish_address: bitcoin::Address,
|
||||
a: bitcoin::SecretKey,
|
||||
s_a: monero::Scalar,
|
||||
v_a: monero::PrivateViewKey,
|
||||
S_a_monero: monero::PublicKey,
|
||||
S_a_bitcoin: bitcoin::PublicKey,
|
||||
dleq_proof_s_a: CrossCurveDLEQProof,
|
||||
btc: bitcoin::Amount,
|
||||
xmr: monero::Amount,
|
||||
cancel_timelock: CancelTimelock,
|
||||
punish_timelock: PunishTimelock,
|
||||
redeem_address: bitcoin::Address,
|
||||
punish_address: bitcoin::Address,
|
||||
}
|
||||
|
||||
impl State0 {
|
||||
@ -168,7 +166,7 @@ impl State0 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct State1 {
|
||||
a: bitcoin::SecretKey,
|
||||
B: bitcoin::PublicKey,
|
||||
@ -180,7 +178,6 @@ pub struct State1 {
|
||||
v: monero::PrivateViewKey,
|
||||
v_a: monero::PrivateViewKey,
|
||||
dleq_proof_s_a: CrossCurveDLEQProof,
|
||||
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||||
btc: bitcoin::Amount,
|
||||
xmr: monero::Amount,
|
||||
cancel_timelock: CancelTimelock,
|
||||
@ -203,8 +200,11 @@ impl State1 {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn receive(self, msg: Message2) -> State2 {
|
||||
State2 {
|
||||
pub fn receive(self, msg: Message2) -> Result<State2> {
|
||||
let tx_lock = bitcoin::TxLock::from_psbt(msg.psbt, self.a.public(), self.B, self.btc)
|
||||
.context("Failed to re-construct TxLock from received PSBT")?;
|
||||
|
||||
Ok(State2 {
|
||||
a: self.a,
|
||||
B: self.B,
|
||||
s_a: self.s_a,
|
||||
@ -218,12 +218,12 @@ impl State1 {
|
||||
refund_address: self.refund_address,
|
||||
redeem_address: self.redeem_address,
|
||||
punish_address: self.punish_address,
|
||||
tx_lock: msg.tx_lock,
|
||||
}
|
||||
tx_lock,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct State2 {
|
||||
a: bitcoin::SecretKey,
|
||||
B: bitcoin::PublicKey,
|
||||
@ -231,7 +231,6 @@ pub struct State2 {
|
||||
S_b_monero: monero::PublicKey,
|
||||
S_b_bitcoin: bitcoin::PublicKey,
|
||||
v: monero::PrivateViewKey,
|
||||
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||||
btc: bitcoin::Amount,
|
||||
xmr: monero::Amount,
|
||||
cancel_timelock: CancelTimelock,
|
||||
@ -295,23 +294,23 @@ impl State2 {
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
|
||||
pub struct State3 {
|
||||
pub a: bitcoin::SecretKey,
|
||||
pub B: bitcoin::PublicKey,
|
||||
pub s_a: monero::Scalar,
|
||||
pub S_b_monero: monero::PublicKey,
|
||||
pub S_b_bitcoin: bitcoin::PublicKey,
|
||||
a: bitcoin::SecretKey,
|
||||
B: bitcoin::PublicKey,
|
||||
s_a: monero::Scalar,
|
||||
S_b_monero: monero::PublicKey,
|
||||
S_b_bitcoin: bitcoin::PublicKey,
|
||||
pub v: monero::PrivateViewKey,
|
||||
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||||
pub btc: bitcoin::Amount,
|
||||
pub xmr: monero::Amount,
|
||||
btc: bitcoin::Amount,
|
||||
xmr: monero::Amount,
|
||||
pub cancel_timelock: CancelTimelock,
|
||||
pub punish_timelock: PunishTimelock,
|
||||
pub refund_address: bitcoin::Address,
|
||||
pub redeem_address: bitcoin::Address,
|
||||
pub punish_address: bitcoin::Address,
|
||||
refund_address: bitcoin::Address,
|
||||
redeem_address: bitcoin::Address,
|
||||
punish_address: bitcoin::Address,
|
||||
pub tx_lock: bitcoin::TxLock,
|
||||
pub tx_punish_sig_bob: bitcoin::Signature,
|
||||
pub tx_cancel_sig_bob: bitcoin::Signature,
|
||||
tx_punish_sig_bob: bitcoin::Signature,
|
||||
tx_cancel_sig_bob: bitcoin::Signature,
|
||||
}
|
||||
|
||||
impl State3 {
|
||||
@ -367,15 +366,48 @@ impl State3 {
|
||||
TxCancel::new(&self.tx_lock, self.cancel_timelock, self.a.public(), self.B)
|
||||
}
|
||||
|
||||
pub fn tx_punish(&self) -> TxPunish {
|
||||
pub fn tx_refund(&self) -> TxRefund {
|
||||
bitcoin::TxRefund::new(&self.tx_cancel(), &self.refund_address)
|
||||
}
|
||||
|
||||
pub fn extract_monero_private_key(
|
||||
&self,
|
||||
published_refund_tx: bitcoin::Transaction,
|
||||
) -> Result<monero::PrivateKey> {
|
||||
self.tx_refund().extract_monero_private_key(
|
||||
published_refund_tx,
|
||||
self.s_a,
|
||||
self.a.clone(),
|
||||
self.S_b_bitcoin,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn signed_redeem_transaction(
|
||||
&self,
|
||||
sig: bitcoin::EncryptedSignature,
|
||||
) -> Result<bitcoin::Transaction> {
|
||||
bitcoin::TxRedeem::new(&self.tx_lock, &self.redeem_address)
|
||||
.complete(sig, self.a.clone(), self.s_a.to_secpfun_scalar(), self.B)
|
||||
.context("Failed to complete Bitcoin redeem transaction")
|
||||
}
|
||||
|
||||
pub fn signed_cancel_transaction(&self) -> Result<bitcoin::Transaction> {
|
||||
self.tx_cancel()
|
||||
.complete_as_alice(self.a.clone(), self.B, self.tx_cancel_sig_bob.clone())
|
||||
.context("Failed to complete Bitcoin cancel transaction")
|
||||
}
|
||||
|
||||
pub fn signed_punish_transaction(&self) -> Result<bitcoin::Transaction> {
|
||||
self.tx_punish()
|
||||
.complete(self.tx_punish_sig_bob.clone(), self.a.clone(), self.B)
|
||||
.context("Failed to complete Bitcoin punish transaction")
|
||||
}
|
||||
|
||||
fn tx_punish(&self) -> TxPunish {
|
||||
bitcoin::TxPunish::new(
|
||||
&self.tx_cancel(),
|
||||
&self.punish_address,
|
||||
self.punish_timelock,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn tx_refund(&self) -> TxRefund {
|
||||
bitcoin::TxRefund::new(&self.tx_cancel(), &self.refund_address)
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,7 @@
|
||||
//! Run an XMR/BTC swap in the role of Alice.
|
||||
//! Alice holds XMR and wishes receive BTC.
|
||||
use crate::bitcoin::{ExpiredTimelocks, TxRedeem};
|
||||
use crate::bitcoin::ExpiredTimelocks;
|
||||
use crate::env::Config;
|
||||
use crate::monero_ext::ScalarExt;
|
||||
use crate::protocol::alice;
|
||||
use crate::protocol::alice::event_loop::EventLoopHandle;
|
||||
use crate::protocol::alice::AliceState;
|
||||
@ -158,12 +157,7 @@ async fn next_state(
|
||||
} => match state3.expired_timelocks(bitcoin_wallet).await? {
|
||||
ExpiredTimelocks::None => {
|
||||
let tx_lock_status = bitcoin_wallet.subscribe_to(state3.tx_lock.clone()).await;
|
||||
match TxRedeem::new(&state3.tx_lock, &state3.redeem_address).complete(
|
||||
*encrypted_signature,
|
||||
state3.a.clone(),
|
||||
state3.s_a.to_secpfun_scalar(),
|
||||
state3.B,
|
||||
) {
|
||||
match state3.signed_redeem_transaction(*encrypted_signature) {
|
||||
Ok(tx) => match bitcoin_wallet.broadcast(tx, "redeem").await {
|
||||
Ok((_, subscription)) => match subscription.wait_until_final().await {
|
||||
Ok(_) => AliceState::BtcRedeemed,
|
||||
@ -205,18 +199,14 @@ async fn next_state(
|
||||
state3,
|
||||
monero_wallet_restore_blockheight,
|
||||
} => {
|
||||
let tx_cancel = state3.tx_cancel();
|
||||
let transaction = state3.signed_cancel_transaction()?;
|
||||
|
||||
// If Bob hasn't yet broadcasted the tx cancel, we do it
|
||||
if bitcoin_wallet
|
||||
.get_raw_transaction(tx_cancel.txid())
|
||||
.get_raw_transaction(transaction.txid())
|
||||
.await
|
||||
.is_err()
|
||||
{
|
||||
let transaction = tx_cancel
|
||||
.complete_as_alice(state3.a.clone(), state3.B, state3.tx_cancel_sig_bob.clone())
|
||||
.context("Failed to complete Bitcoin cancel transaction")?;
|
||||
|
||||
if let Err(e) = bitcoin_wallet.broadcast(transaction, "cancel").await {
|
||||
tracing::debug!(
|
||||
"Assuming transaction is already broadcasted because: {:#}",
|
||||
@ -243,14 +233,9 @@ async fn next_state(
|
||||
select! {
|
||||
seen_refund = tx_refund_status.wait_until_seen() => {
|
||||
seen_refund.context("Failed to monitor refund transaction")?;
|
||||
let published_refund_tx = bitcoin_wallet.get_raw_transaction(state3.tx_refund().txid()).await?;
|
||||
|
||||
let spend_key = state3.tx_refund().extract_monero_private_key(
|
||||
published_refund_tx,
|
||||
state3.s_a,
|
||||
state3.a.clone(),
|
||||
state3.S_b_bitcoin,
|
||||
)?;
|
||||
let published_refund_tx = bitcoin_wallet.get_raw_transaction(state3.tx_refund().txid()).await?;
|
||||
let spend_key = state3.extract_monero_private_key(published_refund_tx)?;
|
||||
|
||||
AliceState::BtcRefunded {
|
||||
spend_key,
|
||||
@ -283,11 +268,7 @@ async fn next_state(
|
||||
state3,
|
||||
monero_wallet_restore_blockheight,
|
||||
} => {
|
||||
let signed_tx_punish = state3.tx_punish().complete(
|
||||
state3.tx_punish_sig_bob.clone(),
|
||||
state3.a.clone(),
|
||||
state3.B,
|
||||
)?;
|
||||
let signed_tx_punish = state3.signed_punish_transaction()?;
|
||||
|
||||
let punish = async {
|
||||
let (txid, subscription) =
|
||||
@ -313,16 +294,11 @@ async fn next_state(
|
||||
// because a punish tx failure is not recoverable (besides re-trying) if the
|
||||
// refund tx was not included.
|
||||
|
||||
let tx_refund = state3.tx_refund();
|
||||
let published_refund_tx =
|
||||
bitcoin_wallet.get_raw_transaction(tx_refund.txid()).await?;
|
||||
let published_refund_tx = bitcoin_wallet
|
||||
.get_raw_transaction(state3.tx_refund().txid())
|
||||
.await?;
|
||||
|
||||
let spend_key = tx_refund.extract_monero_private_key(
|
||||
published_refund_tx,
|
||||
state3.s_a,
|
||||
state3.a.clone(),
|
||||
state3.S_b_bitcoin,
|
||||
)?;
|
||||
let spend_key = state3.extract_monero_private_key(published_refund_tx)?;
|
||||
|
||||
AliceState::BtcRefunded {
|
||||
spend_key,
|
||||
|
@ -4,7 +4,6 @@ use crate::network::{encrypted_signature, spot_price};
|
||||
use crate::protocol::bob;
|
||||
use crate::{bitcoin, monero};
|
||||
use anyhow::{anyhow, Error, Result};
|
||||
pub use execution_setup::{Message0, Message2, Message4};
|
||||
use libp2p::core::Multiaddr;
|
||||
use libp2p::request_response::{RequestResponseEvent, RequestResponseMessage, ResponseChannel};
|
||||
use libp2p::{NetworkBehaviour, PeerId};
|
||||
|
@ -1,35 +1,11 @@
|
||||
use crate::bitcoin::Signature;
|
||||
use crate::network::cbor_request_response::BUF_SIZE;
|
||||
use crate::protocol::alice::{Message1, Message3};
|
||||
use crate::protocol::bob::{State0, State2};
|
||||
use crate::protocol::{Message1, Message3};
|
||||
use anyhow::{Context, Error, Result};
|
||||
use libp2p::PeerId;
|
||||
use libp2p_async_await::BehaviourOutEvent;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sigma_fun::ext::dl_secp256k1_ed25519_eq::CrossCurveDLEQProof;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Message0 {
|
||||
pub(crate) B: crate::bitcoin::PublicKey,
|
||||
pub(crate) S_b_monero: monero::PublicKey,
|
||||
pub(crate) S_b_bitcoin: crate::bitcoin::PublicKey,
|
||||
pub(crate) dleq_proof_s_b: CrossCurveDLEQProof,
|
||||
pub(crate) v_b: crate::monero::PrivateViewKey,
|
||||
pub(crate) refund_address: bitcoin::Address,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Message2 {
|
||||
pub(crate) tx_lock: crate::bitcoin::TxLock,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||
pub struct Message4 {
|
||||
pub(crate) tx_punish_sig: Signature,
|
||||
pub(crate) tx_cancel_sig: Signature,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum OutEvent {
|
||||
Done(Result<State2>),
|
||||
|
@ -6,9 +6,7 @@ use crate::monero;
|
||||
use crate::monero::wallet::WatchRequest;
|
||||
use crate::monero::{monero_private_key, TransferProof};
|
||||
use crate::monero_ext::ScalarExt;
|
||||
use crate::protocol::alice::{Message1, Message3};
|
||||
use crate::protocol::bob::{Message0, Message2, Message4};
|
||||
use crate::protocol::CROSS_CURVE_PROOF_SYSTEM;
|
||||
use crate::protocol::{Message0, Message1, Message2, Message3, Message4, CROSS_CURVE_PROOF_SYSTEM};
|
||||
use anyhow::{anyhow, bail, Context, Result};
|
||||
use ecdsa_fun::adaptor::{Adaptor, HashTranscript};
|
||||
use ecdsa_fun::nonce::Deterministic;
|
||||
@ -69,7 +67,7 @@ impl fmt::Display for BobState {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize, PartialEq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct State0 {
|
||||
b: bitcoin::SecretKey,
|
||||
s_b: monero::Scalar,
|
||||
@ -77,7 +75,6 @@ pub struct State0 {
|
||||
S_b_bitcoin: bitcoin::PublicKey,
|
||||
v_b: monero::PrivateViewKey,
|
||||
dleq_proof_s_b: CrossCurveDLEQProof,
|
||||
#[serde(with = "::bitcoin::util::amount::serde::as_sat")]
|
||||
btc: bitcoin::Amount,
|
||||
xmr: monero::Amount,
|
||||
cancel_timelock: CancelTimelock,
|
||||
@ -170,7 +167,7 @@ impl State0 {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[derive(Debug)]
|
||||
pub struct State1 {
|
||||
A: bitcoin::PublicKey,
|
||||
b: bitcoin::SecretKey,
|
||||
@ -191,7 +188,7 @@ pub struct State1 {
|
||||
impl State1 {
|
||||
pub fn next_message(&self) -> Message2 {
|
||||
Message2 {
|
||||
tx_lock: self.tx_lock.clone(),
|
||||
psbt: self.tx_lock.clone().into(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
pub mod testutils;
|
||||
pub mod harness;
|
||||
|
||||
use harness::bob_run_until::is_btc_locked;
|
||||
use harness::FastCancelConfig;
|
||||
use swap::protocol::bob::BobState;
|
||||
use swap::protocol::{alice, bob};
|
||||
use testutils::bob_run_until::is_btc_locked;
|
||||
use testutils::FastCancelConfig;
|
||||
|
||||
#[tokio::test]
|
||||
async fn given_bob_manually_refunds_after_btc_locked_bob_refunds() {
|
||||
testutils::setup_test(FastCancelConfig, |mut ctx| async move {
|
||||
harness::setup_test(FastCancelConfig, |mut ctx| async move {
|
||||
let (bob_swap, bob_join_handle) = ctx.bob_swap().await;
|
||||
let bob_swap = tokio::spawn(bob::run_until(bob_swap, is_btc_locked));
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
pub mod testutils;
|
||||
pub mod harness;
|
||||
|
||||
use bob::cancel::Error;
|
||||
use harness::bob_run_until::is_btc_locked;
|
||||
use harness::SlowCancelConfig;
|
||||
use swap::protocol::bob::BobState;
|
||||
use swap::protocol::{alice, bob};
|
||||
use testutils::bob_run_until::is_btc_locked;
|
||||
use testutils::SlowCancelConfig;
|
||||
|
||||
#[tokio::test]
|
||||
async fn given_bob_manually_cancels_when_timelock_not_expired_errors() {
|
||||
testutils::setup_test(SlowCancelConfig, |mut ctx| async move {
|
||||
harness::setup_test(SlowCancelConfig, |mut ctx| async move {
|
||||
let (bob_swap, bob_join_handle) = ctx.bob_swap().await;
|
||||
let bob_swap = tokio::spawn(bob::run_until(bob_swap, is_btc_locked));
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
pub mod testutils;
|
||||
pub mod harness;
|
||||
|
||||
use harness::bob_run_until::is_btc_locked;
|
||||
use harness::SlowCancelConfig;
|
||||
use swap::protocol::bob::BobState;
|
||||
use swap::protocol::{alice, bob};
|
||||
use testutils::bob_run_until::is_btc_locked;
|
||||
use testutils::SlowCancelConfig;
|
||||
|
||||
#[tokio::test]
|
||||
async fn given_bob_manually_forces_cancel_when_timelock_not_expired_errors() {
|
||||
testutils::setup_test(SlowCancelConfig, |mut ctx| async move {
|
||||
harness::setup_test(SlowCancelConfig, |mut ctx| async move {
|
||||
let (bob_swap, bob_join_handle) = ctx.bob_swap().await;
|
||||
let bob_swap = tokio::spawn(bob::run_until(bob_swap, is_btc_locked));
|
||||
|
||||
|
@ -1,14 +1,14 @@
|
||||
pub mod testutils;
|
||||
pub mod harness;
|
||||
|
||||
use harness::SlowCancelConfig;
|
||||
use swap::protocol::{alice, bob};
|
||||
use testutils::SlowCancelConfig;
|
||||
use tokio::join;
|
||||
|
||||
/// Run the following tests with RUST_MIN_STACK=10000000
|
||||
|
||||
#[tokio::test]
|
||||
async fn happy_path() {
|
||||
testutils::setup_test(SlowCancelConfig, |mut ctx| async move {
|
||||
harness::setup_test(SlowCancelConfig, |mut ctx| async move {
|
||||
let (bob_swap, _) = ctx.bob_swap().await;
|
||||
let bob_swap = tokio::spawn(bob::run(bob_swap));
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
pub mod testutils;
|
||||
pub mod harness;
|
||||
|
||||
use harness::bob_run_until::is_xmr_locked;
|
||||
use harness::SlowCancelConfig;
|
||||
use swap::protocol::bob::BobState;
|
||||
use swap::protocol::{alice, bob};
|
||||
use testutils::bob_run_until::is_xmr_locked;
|
||||
use testutils::SlowCancelConfig;
|
||||
|
||||
#[tokio::test]
|
||||
async fn given_bob_restarts_after_xmr_is_locked_resume_swap() {
|
||||
testutils::setup_test(SlowCancelConfig, |mut ctx| async move {
|
||||
harness::setup_test(SlowCancelConfig, |mut ctx| async move {
|
||||
let (bob_swap, bob_join_handle) = ctx.bob_swap().await;
|
||||
let bob_swap = tokio::spawn(bob::run_until(bob_swap, is_xmr_locked));
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
pub mod testutils;
|
||||
pub mod harness;
|
||||
|
||||
use harness::bob_run_until::is_xmr_locked;
|
||||
use harness::SlowCancelConfig;
|
||||
use swap::protocol::bob::BobState;
|
||||
use swap::protocol::{alice, bob};
|
||||
use testutils::bob_run_until::is_xmr_locked;
|
||||
use testutils::SlowCancelConfig;
|
||||
|
||||
#[tokio::test]
|
||||
async fn given_bob_restarts_after_xmr_is_locked_resume_swap() {
|
||||
testutils::setup_test(SlowCancelConfig, |mut ctx| async move {
|
||||
harness::setup_test(SlowCancelConfig, |mut ctx| async move {
|
||||
let (bob_swap, bob_join_handle) = ctx.bob_swap().await;
|
||||
let bob_swap = tokio::spawn(bob::run_until(bob_swap, is_xmr_locked));
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::testutils::bitcoind;
|
||||
use crate::harness::bitcoind;
|
||||
use bitcoin::Network;
|
||||
use std::collections::HashMap;
|
||||
use testcontainers::core::{Container, Docker, WaitForMessage};
|
@ -1,7 +1,7 @@
|
||||
mod bitcoind;
|
||||
mod electrs;
|
||||
|
||||
use crate::testutils;
|
||||
use crate::harness;
|
||||
use anyhow::{bail, Context, Result};
|
||||
use async_trait::async_trait;
|
||||
use bitcoin_harness::{BitcoindRpcApi, Client};
|
||||
@ -452,7 +452,7 @@ where
|
||||
|
||||
let env_config = C::get_config();
|
||||
|
||||
let (monero, containers) = testutils::init_containers(&cli).await;
|
||||
let (monero, containers) = harness::init_containers(&cli).await;
|
||||
|
||||
let btc_amount = bitcoin::Amount::from_sat(1_000_000);
|
||||
let xmr_amount = monero::Amount::from_monero(btc_amount.as_btc() / FixedRate::RATE).unwrap();
|
||||
@ -470,7 +470,7 @@ where
|
||||
|
||||
let electrs_rpc_port = containers
|
||||
.electrs
|
||||
.get_host_port(testutils::electrs::RPC_PORT)
|
||||
.get_host_port(harness::electrs::RPC_PORT)
|
||||
.expect("Could not map electrs rpc port");
|
||||
|
||||
let alice_seed = Seed::random().unwrap();
|
||||
@ -600,7 +600,7 @@ async fn init_bitcoind_container(
|
||||
|
||||
let docker = cli.run_with_args(image, run_args);
|
||||
let a = docker
|
||||
.get_host_port(testutils::bitcoind::RPC_PORT)
|
||||
.get_host_port(harness::bitcoind::RPC_PORT)
|
||||
.context("Could not map bitcoind rpc port")?;
|
||||
|
||||
let bitcoind_url = {
|
||||
@ -627,7 +627,7 @@ pub async fn init_electrs_container(
|
||||
let bitcoind_rpc_addr = format!(
|
||||
"{}:{}",
|
||||
bitcoind_container_name,
|
||||
testutils::bitcoind::RPC_PORT
|
||||
harness::bitcoind::RPC_PORT
|
||||
);
|
||||
let image = electrs::Electrs::default()
|
||||
.with_volume(volume)
|
@ -1,15 +1,15 @@
|
||||
pub mod testutils;
|
||||
pub mod harness;
|
||||
|
||||
use harness::bob_run_until::is_btc_locked;
|
||||
use harness::FastPunishConfig;
|
||||
use swap::protocol::bob::BobState;
|
||||
use swap::protocol::{alice, bob};
|
||||
use testutils::bob_run_until::is_btc_locked;
|
||||
use testutils::FastPunishConfig;
|
||||
|
||||
/// Bob locks Btc and Alice locks Xmr. Bob does not act; he fails to send Alice
|
||||
/// the encsig and fail to refund or redeem. Alice punishes.
|
||||
#[tokio::test]
|
||||
async fn alice_punishes_if_bob_never_acts_after_fund() {
|
||||
testutils::setup_test(FastPunishConfig, |mut ctx| async move {
|
||||
harness::setup_test(FastPunishConfig, |mut ctx| async move {
|
||||
let (bob_swap, bob_join_handle) = ctx.bob_swap().await;
|
||||
let bob_swap = tokio::spawn(bob::run_until(bob_swap, is_btc_locked));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user