Alice to validate Bob's PSBT for correctness

In order for the re-construction of TxLock to be meaningful, we limit
`Message2` to the PSBT instead of the full struct. This is a breaking
change in the network layer.

The PSBT is valid if:

- It has at most two outputs (we allow a change output)
- One of the outputs pays the agreed upon amount to a shared output script

Resolves #260.
pull/373/head
Thomas Eizinger 3 years ago
parent 8576894c10
commit 52b9a78de2
No known key found for this signature in database
GPG Key ID: 651AC83A6C6C8B96

@ -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

@ -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;

@ -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,7 +19,15 @@ 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())
@ -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()
}
}

@ -348,6 +348,39 @@ impl<B, D, C> Wallet<B, D, C> {
}
}
#[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

@ -47,7 +47,7 @@ pub struct Message1 {
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Message2 {
tx_lock: bitcoin::TxLock,
psbt: bitcoin::PartiallySignedTransaction,
}
#[derive(Clone, Debug, Serialize, Deserialize)]

@ -57,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(

@ -200,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,
@ -215,8 +218,8 @@ impl State1 {
refund_address: self.refund_address,
redeem_address: self.redeem_address,
punish_address: self.punish_address,
tx_lock: msg.tx_lock,
}
tx_lock,
})
}
}

@ -188,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(),
}
}

Loading…
Cancel
Save