Merge #819: Verify receive address

e9a217b3d87fc5ccd284df33fc58f877c7a5103e Add verify address modal to receive panel (edouardparis)
fa992aa5649ec406eb696437c86f9bc781d5a0b8 gui: move receive panel to its own file (edouardparis)

Pull request description:

ACKs for top commit:
  jp1ac4:
    utACK e9a217b3d8.

Tree-SHA512: 4b34260304f6417146c920861c63f4c4f5f9823a3f73538a398eba0144dee9c4630fe91007dfc34ae07e5957926cc30464abd8c55679b899dc9612e8aa4d98ce
This commit is contained in:
Antoine Poinsot 2023-11-20 17:42:03 +01:00
commit 1df4ab8f07
No known key found for this signature in database
GPG Key ID: E13FC145CD3F4304
9 changed files with 637 additions and 266 deletions

256
gui/Cargo.lock generated
View File

@ -62,30 +62,30 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aead"
version = "0.4.3"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
"crypto-common",
"generic-array",
]
[[package]]
name = "aes"
version = "0.7.5"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8"
checksum = "ac1f845298e95f983ff1944b728ae08b8cebab80d684f0a832ed0fc74dfa27e2"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"opaque-debug",
]
[[package]]
name = "aes-gcm"
version = "0.9.4"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
dependencies = [
"aead",
"aes",
@ -101,7 +101,7 @@ version = "0.7.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
dependencies = [
"getrandom 0.2.8",
"getrandom",
"once_cell",
"version_check",
]
@ -159,9 +159,9 @@ checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "arrayvec"
version = "0.7.2"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6"
checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711"
[[package]]
name = "ash"
@ -174,9 +174,9 @@ dependencies = [
[[package]]
name = "async-hwi"
version = "0.0.12"
version = "0.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bca4546c8774683d7c15e692be53751f59fcac3b83465332013b3ca673248e15"
checksum = "2a1d739fac959bf5e332425995a1892f99d94f39acd8acf36fe6c212f9583e0c"
dependencies = [
"async-trait",
"base64 0.13.1",
@ -297,21 +297,21 @@ checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
[[package]]
name = "bitbox-api"
version = "0.1.8"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f42283322b091f6650ed7cfa17febd100c245bf068fc1a28708e6b3763f2f43"
checksum = "bb3e44c693da4b4db46e2e3f2beb28479cb6a0bd4ebda12f1f22b39a48188f88"
dependencies = [
"async-trait",
"base32",
"bitcoin",
"byteorder",
"getrandom 0.2.8",
"getrandom",
"hex",
"hidapi",
"noise-protocol",
"noise-rust-crypto",
"num-bigint",
"prost",
"prost 0.12.2",
"prost-build",
"semver",
"serde",
@ -372,7 +372,7 @@ version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest 0.10.7",
"digest",
]
[[package]]
@ -485,21 +485,20 @@ dependencies = [
[[package]]
name = "chacha20"
version = "0.8.2"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c80e5460aa66fe3b91d40bcbdab953a597b60053e34d684ac6903f863b680a6"
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
dependencies = [
"cfg-if",
"cipher",
"cpufeatures",
"zeroize",
]
[[package]]
name = "chacha20poly1305"
version = "0.9.1"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a18446b09be63d457bbec447509e85f662f32952b035ce892290396bc0b0cff5"
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
dependencies = [
"aead",
"chacha20",
@ -531,11 +530,13 @@ dependencies = [
[[package]]
name = "cipher"
version = "0.3.0"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
"generic-array",
"crypto-common",
"inout",
"zeroize",
]
[[package]]
@ -657,7 +658,7 @@ version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d7d6ab3c3a2282db210df5f02c4dab6e0a7057af0fb7ebd4070f30fe05c0ddb"
dependencies = [
"getrandom 0.2.8",
"getrandom",
"once_cell",
"proc-macro-hack",
"tiny-keccak",
@ -833,9 +834,9 @@ dependencies = [
[[package]]
name = "ctr"
version = "0.8.0"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "049bb91fb4aaf0e3c7efa6cd5ef877dbbbd15b39dad06d9948de4ec8a75761ea"
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
dependencies = [
"cipher",
]
@ -848,17 +849,31 @@ checksum = "b365fabc795046672053e29c954733ec3b05e4be654ab130fe8f1f94d7051f35"
[[package]]
name = "curve25519-dalek"
version = "3.2.1"
version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90f9d052967f590a76e62eb387bd0bbb1b000182c3cefe5364db6b7211651bc0"
checksum = "e89b8c6a2e4b1f45971ad09761aafb85514a84744b67a95e32c3cc1352d1f65c"
dependencies = [
"byteorder",
"digest 0.9.0",
"rand_core 0.5.1",
"cfg-if",
"cpufeatures",
"curve25519-dalek-derive",
"fiat-crypto",
"platforms",
"rustc_version",
"subtle",
"zeroize",
]
[[package]]
name = "curve25519-dalek-derive"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.29",
]
[[package]]
name = "cxx"
version = "1.0.94"
@ -955,15 +970,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d7439c3735f405729d52c3fbbe4de140eaf938a1fe47d227c27f8254d4302a5"
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array",
]
[[package]]
name = "digest"
version = "0.10.7"
@ -1214,6 +1220,12 @@ dependencies = [
"log",
]
[[package]]
name = "fiat-crypto"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "27573eac26f4dd11e2b1916c3fe1baa56407c83c71a773a8ba17ec0bca03b6b7"
[[package]]
name = "filetime"
version = "0.2.22"
@ -1498,17 +1510,6 @@ dependencies = [
"winapi",
]
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.2.8"
@ -1524,9 +1525,9 @@ dependencies = [
[[package]]
name = "ghash"
version = "0.4.4"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1583cc1656d7839fd3732b80cf4f38850336cdb9b8ded1cd399ca62958de3c99"
checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40"
dependencies = [
"opaque-debug",
"polyval",
@ -2221,6 +2222,15 @@ dependencies = [
"hashbrown 0.14.0",
]
[[package]]
name = "inout"
version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"generic-array",
]
[[package]]
name = "instant"
version = "0.1.12"
@ -2354,7 +2364,7 @@ version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d676038719d1c892f91e6e85121550143c75880b42f7feff6d413a078cf91fb3"
dependencies = [
"arrayvec 0.7.2",
"arrayvec 0.7.4",
]
[[package]]
@ -2420,14 +2430,14 @@ dependencies = [
[[package]]
name = "liana"
version = "2.0.0"
source = "git+https://github.com/wizardsardine/liana?branch=master#2d303b139d4eeafc6b6505433e18b24cd561ab6b"
source = "git+https://github.com/wizardsardine/liana?branch=master#514535d8d6fec705c7271241f68276c42b918150"
dependencies = [
"backtrace",
"bdk_coin_select",
"bip39",
"dirs 5.0.0",
"fern",
"getrandom 0.2.8",
"getrandom",
"jsonrpc 0.16.0",
"log",
"miniscript",
@ -2599,7 +2609,7 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74df1ff0a0147282eb10699537a03baa7d31972b58984a1d44ce0624043fe8ad"
dependencies = [
"arrayvec 0.7.2",
"arrayvec 0.7.4",
"euclid",
"num-traits",
]
@ -2815,7 +2825,7 @@ version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"
dependencies = [
"getrandom 0.2.8",
"getrandom",
]
[[package]]
@ -2936,23 +2946,22 @@ checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
[[package]]
name = "noise-protocol"
version = "0.1.4"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb474d36dfe51bb4d7e733fee2b0dfd92ee1b95c716030a70e92737dea1a52b"
checksum = "2473d39689a839f5a363aaef7d99f76d5611bf352286682b25a6644fec18b1d3"
dependencies = [
"arrayvec 0.7.2",
"arrayvec 0.7.4",
]
[[package]]
name = "noise-rust-crypto"
version = "0.5.0"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82e7cfeb8e6a63b4a5ccef34ed7a22d084a129b1e53a000c080bbc54c0da6f8c"
checksum = "b4c6159f60beb3bbbcdc266bc789bfc6c37fdad7d7ca7152d3e049ef5af633f0"
dependencies = [
"aes-gcm",
"blake2",
"chacha20poly1305",
"getrandom 0.2.8",
"noise-protocol",
"sha2",
"x25519-dalek",
@ -3353,6 +3362,12 @@ version = "0.3.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
[[package]]
name = "platforms"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14e6ab3f592e6fb464fc9712d8d6e6912de6473954635fd76a589d832cffcbb0"
[[package]]
name = "png"
version = "0.17.7"
@ -3367,9 +3382,9 @@ dependencies = [
[[package]]
name = "poly1305"
version = "0.7.2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede"
checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
dependencies = [
"cpufeatures",
"opaque-debug",
@ -3378,9 +3393,9 @@ dependencies = [
[[package]]
name = "polyval"
version = "0.5.3"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8419d2b623c7c0896ff2d5d96e2cb4ede590fed28fcc34934f4c33c036e620a1"
checksum = "d52cff9d1d4dee5fe6d03729099f4a310a41179e0a10dbf542039873f2e826fb"
dependencies = [
"cfg-if",
"cpufeatures",
@ -3446,9 +3461,9 @@ checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
[[package]]
name = "proc-macro2"
version = "1.0.64"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78803b62cbf1f46fde80d7c0e803111524b9877184cfe7c3033659490ac7a7da"
checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da"
dependencies = [
"unicode-ident",
]
@ -3466,7 +3481,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd"
dependencies = [
"bytes",
"prost-derive",
"prost-derive 0.11.9",
]
[[package]]
name = "prost"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a5a410fc7882af66deb8d01d01737353cf3ad6204c408177ba494291a626312"
dependencies = [
"bytes",
"prost-derive 0.12.2",
]
[[package]]
@ -3483,7 +3508,7 @@ dependencies = [
"multimap",
"petgraph",
"prettyplease",
"prost",
"prost 0.11.9",
"prost-types",
"regex",
"syn 1.0.109",
@ -3504,13 +3529,26 @@ dependencies = [
"syn 1.0.109",
]
[[package]]
name = "prost-derive"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "065717a5dfaca4a83d2fe57db3487b311365200000551d7a364e715dbf4346bc"
dependencies = [
"anyhow",
"itertools",
"proc-macro2",
"quote",
"syn 2.0.29",
]
[[package]]
name = "prost-types"
version = "0.11.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "213622a1460818959ac1181aaeb2dc9c7f63df720db7d788b3e24eacd1983e13"
dependencies = [
"prost",
"prost 0.11.9",
]
[[package]]
@ -3533,9 +3571,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.30"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5907a1b7c277254a8b15170f6e7c97cfa60ee7872a3217663bb81151e48184bb"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2",
]
@ -3548,7 +3586,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core 0.6.4",
"rand_core",
]
[[package]]
@ -3558,16 +3596,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core 0.6.4",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
dependencies = [
"getrandom 0.1.16",
"rand_core",
]
[[package]]
@ -3576,7 +3605,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom 0.2.8",
"getrandom",
]
[[package]]
@ -3644,7 +3673,7 @@ version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e233b642160555c1aa1ff7a78443c6139342f411b6fa6602af2ebbfee9e166bb"
dependencies = [
"rand_core 0.6.4",
"rand_core",
]
[[package]]
@ -3671,7 +3700,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [
"getrandom 0.2.8",
"getrandom",
"redox_syscall 0.2.16",
"thiserror",
]
@ -3840,6 +3869,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "rustix"
version = "0.37.13"
@ -4065,13 +4103,13 @@ dependencies = [
[[package]]
name = "sha2"
version = "0.10.7"
version = "0.10.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "479fb9d862239e610720565ca91403019f2f00410f1864c5aa7479b950a76ed8"
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
dependencies = [
"cfg-if",
"cpufeatures",
"digest 0.10.7",
"digest",
]
[[package]]
@ -4446,7 +4484,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df8493a203431061e901613751931f047d1971337153f96d0e5e363d6dbf6a67"
dependencies = [
"arrayref",
"arrayvec 0.7.2",
"arrayvec 0.7.4",
"bytemuck",
"cfg-if",
"png",
@ -4745,11 +4783,11 @@ checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "universal-hash"
version = "0.4.1"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
dependencies = [
"generic-array",
"crypto-common",
"subtle",
]
@ -4836,12 +4874,6 @@ dependencies = [
"try-lock",
]
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.0+wasi-snapshot-preview1"
@ -5046,7 +5078,7 @@ version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d745a1b6d91d85c33defbb29f0eee0450e1d2614d987e14bf6baf26009d132d7"
dependencies = [
"arrayvec 0.7.2",
"arrayvec 0.7.4",
"cfg-if",
"js-sys",
"log",
@ -5070,7 +5102,7 @@ version = "0.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7131408d940e335792645a98f03639573b0480e9e2e7cddbbab74f7c6d9f3fff"
dependencies = [
"arrayvec 0.7.2",
"arrayvec 0.7.4",
"bit-vec",
"bitflags",
"codespan-reporting",
@ -5094,7 +5126,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdcf61a283adc744bb5453dd88ea91f3f86d5ca6b027661c6c73c7734ae0288b"
dependencies = [
"android_system_properties",
"arrayvec 0.7.2",
"arrayvec 0.7.4",
"ash",
"bit-set",
"bitflags",
@ -5502,12 +5534,12 @@ dependencies = [
[[package]]
name = "x25519-dalek"
version = "1.2.0"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2392b6b94a576b4e2bf3c5b2757d63f10ada8020a2e4d08ac849ebcf6ea8e077"
checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96"
dependencies = [
"curve25519-dalek",
"rand_core 0.5.1",
"rand_core",
"zeroize",
]
@ -5540,9 +5572,9 @@ checksum = "4d25c75bf9ea12c4040a97f829154768bbbce366287e2dc044af160cd79a13fd"
[[package]]
name = "zeroize"
version = "1.3.0"
version = "1.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
dependencies = [
"zeroize_derive",
]

View File

@ -14,7 +14,7 @@ name = "liana-gui"
path = "src/main.rs"
[dependencies]
async-hwi = "0.0.12"
async-hwi = "0.0.13"
liana = { git = "https://github.com/wizardsardine/liana", branch = "master", default-features = false, features = ["nonblocking_shutdown"] }
liana_ui = { path = "ui" }
backtrace = "0.3"

View File

@ -3,7 +3,11 @@ use std::sync::Arc;
use liana::{
config::Config as DaemonConfig,
miniscript::bitcoin::{bip32::Fingerprint, psbt::Psbt, Address},
miniscript::bitcoin::{
bip32::{ChildNumber, Fingerprint},
psbt::Psbt,
Address,
},
};
use crate::{
@ -21,7 +25,7 @@ pub enum Message {
LoadWallet,
WalletLoaded(Result<Arc<Wallet>, Error>),
Info(Result<GetInfoResult, Error>),
ReceiveAddress(Result<Address, Error>),
ReceiveAddress(Result<(Address, ChildNumber), Error>),
Coins(Result<Vec<Coin>, Error>),
Labels(Result<HashMap<String, String>, Error>),
SpendTxs(Result<Vec<SpendTx>, Error>),
@ -31,6 +35,7 @@ pub enum Message {
WalletRegistered(Result<Fingerprint, Error>),
Updated(Result<(), Error>),
Saved(Result<(), Error>),
Verified(Fingerprint, Result<(), Error>),
StartRescan(Result<(), Error>),
HardwareWallets(HardwareWalletMessage),
HistoryTransactions(Result<Vec<HistoryTransaction>, Error>),

View File

@ -90,7 +90,9 @@ impl App {
self.cache.blockheight,
)
.into(),
menu::Menu::Receive => ReceivePanel::default().into(),
menu::Menu::Receive => {
ReceivePanel::new(self.data_dir.clone(), self.wallet.clone()).into()
}
menu::Menu::Transactions => TransactionsPanel::new().into(),
menu::Menu::PSBTs => PsbtsPanel::new(self.wallet.clone(), &self.cache.spend_txs).into(),
menu::Menu::CreateSpendTx => CreateSpendPanel::new(

View File

@ -2,29 +2,30 @@ mod coins;
mod label;
mod psbt;
mod psbts;
mod receive;
mod recovery;
mod settings;
mod spend;
mod transactions;
use std::collections::HashMap;
use std::convert::TryInto;
use std::sync::Arc;
use std::time::{SystemTime, UNIX_EPOCH};
use iced::{widget::qr_code, Command, Subscription};
use liana::miniscript::bitcoin::{Address, Amount, OutPoint};
use iced::{Command, Subscription};
use liana::miniscript::bitcoin::{Amount, OutPoint};
use liana_ui::widget::*;
use super::{cache::Cache, error::Error, menu::Menu, message::Message, view, wallet::Wallet};
use crate::daemon::{
model::{remaining_sequence, Coin, HistoryTransaction, LabelItem, Labelled},
model::{remaining_sequence, Coin, HistoryTransaction, Labelled},
Daemon,
};
pub use coins::CoinsPanel;
use label::LabelsEdited;
pub use psbts::PsbtsPanel;
pub use receive::ReceivePanel;
pub use recovery::RecoveryPanel;
pub use settings::SettingsState;
pub use spend::CreateSpendPanel;
@ -48,6 +49,13 @@ pub trait State {
}
}
/// redirect to another state with a message menu
pub fn redirect(menu: Menu) -> Command<Message> {
Command::perform(async { menu }, |menu| {
Message::View(view::Message::Menu(menu))
})
}
pub struct Home {
wallet: Arc<Wallet>,
balance: Amount,
@ -292,142 +300,3 @@ impl From<Home> for Box<dyn State> {
Box::new(s)
}
}
#[derive(Debug, Default)]
pub struct Addresses {
list: Vec<Address>,
labels: HashMap<String, String>,
}
impl Labelled for Addresses {
fn labelled(&self) -> Vec<LabelItem> {
self.list
.iter()
.map(|a| LabelItem::Address(a.clone()))
.collect()
}
fn labels(&mut self) -> &mut HashMap<String, String> {
&mut self.labels
}
}
#[derive(Default)]
pub struct ReceivePanel {
addresses: Addresses,
labels_edited: LabelsEdited,
qr_code: Option<qr_code::State>,
warning: Option<Error>,
}
impl State for ReceivePanel {
fn view<'a>(&'a self, cache: &'a Cache) -> Element<'a, view::Message> {
view::dashboard(
&Menu::Receive,
cache,
self.warning.as_ref(),
view::receive::receive(
&self.addresses.list,
self.qr_code.as_ref(),
&self.addresses.labels,
self.labels_edited.cache(),
),
)
}
fn update(
&mut self,
daemon: Arc<dyn Daemon + Sync + Send>,
_cache: &Cache,
message: Message,
) -> Command<Message> {
match message {
Message::View(view::Message::Label(_, _)) | Message::LabelsUpdated(_) => {
match self.labels_edited.update(
daemon,
message,
std::iter::once(&mut self.addresses).map(|a| a as &mut dyn Labelled),
) {
Ok(cmd) => cmd,
Err(e) => {
self.warning = Some(e);
Command::none()
}
}
}
Message::ReceiveAddress(res) => {
match res {
Ok(address) => {
self.warning = None;
self.qr_code = Some(qr_code::State::new(address.to_qr_uri()).unwrap());
self.addresses.list.push(address);
}
Err(e) => self.warning = Some(e),
}
Command::none()
}
Message::View(view::Message::Next) => self.load(daemon),
_ => Command::none(),
}
}
fn load(&self, daemon: Arc<dyn Daemon + Sync + Send>) -> Command<Message> {
let daemon = daemon.clone();
Command::perform(
async move {
daemon
.get_new_address()
.map(|res| res.address().clone())
.map_err(|e| e.into())
},
Message::ReceiveAddress,
)
}
}
impl From<ReceivePanel> for Box<dyn State> {
fn from(s: ReceivePanel) -> Box<dyn State> {
Box::new(s)
}
}
/// redirect to another state with a message menu
pub fn redirect(menu: Menu) -> Command<Message> {
Command::perform(async { menu }, |menu| {
Message::View(view::Message::Menu(menu))
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
app::cache::Cache,
daemon::{
client::{Lianad, Request},
model::*,
},
utils::{mock::Daemon, sandbox::Sandbox},
};
use liana::miniscript::bitcoin::Address;
use serde_json::json;
use std::str::FromStr;
#[tokio::test]
async fn test_receive_panel() {
let addr =
Address::from_str("tb1qkldgvljmjpxrjq2ev5qxe8dvhn0dph9q85pwtfkjeanmwdue2akqj4twxj")
.unwrap()
.assume_checked();
let daemon = Daemon::new(vec![(
Some(json!({"method": "getnewaddress", "params": Option::<Request>::None})),
Ok(json!(GetAddressResult::new(addr.clone()))),
)]);
let sandbox: Sandbox<ReceivePanel> = Sandbox::new(ReceivePanel::default());
let client = Arc::new(Lianad::new(daemon.run()));
let sandbox = sandbox.load(client, &Cache::default()).await;
let panel = sandbox.state();
assert_eq!(panel.addresses.list, vec![addr]);
}
}

View File

@ -0,0 +1,321 @@
use std::collections::{HashMap, HashSet};
use std::path::PathBuf;
use std::sync::Arc;
use iced::{widget::qr_code, Command, Subscription};
use liana::miniscript::bitcoin::{
bip32::{ChildNumber, Fingerprint},
Address, Network,
};
use liana_ui::{component::modal, widget::*};
use crate::{
app::{
cache::Cache,
error::Error,
menu::Menu,
message::Message,
state::{label::LabelsEdited, State},
view,
wallet::Wallet,
},
hw::{HardwareWallet, HardwareWallets},
};
use crate::daemon::{
model::{LabelItem, Labelled},
Daemon,
};
#[derive(Debug, Default)]
pub struct Addresses {
list: Vec<Address>,
derivation_indexes: Vec<ChildNumber>,
labels: HashMap<String, String>,
}
impl Labelled for Addresses {
fn labelled(&self) -> Vec<LabelItem> {
self.list
.iter()
.map(|a| LabelItem::Address(a.clone()))
.collect()
}
fn labels(&mut self) -> &mut HashMap<String, String> {
&mut self.labels
}
}
pub struct ReceivePanel {
data_dir: PathBuf,
wallet: Arc<Wallet>,
addresses: Addresses,
labels_edited: LabelsEdited,
qr_code: Option<qr_code::State>,
modal: Option<VerifyAddressModal>,
warning: Option<Error>,
}
impl ReceivePanel {
pub fn new(data_dir: PathBuf, wallet: Arc<Wallet>) -> Self {
Self {
data_dir,
wallet,
addresses: Addresses::default(),
labels_edited: LabelsEdited::default(),
qr_code: None,
modal: None,
warning: None,
}
}
}
impl State for ReceivePanel {
fn view<'a>(&'a self, cache: &'a Cache) -> Element<'a, view::Message> {
let content = view::dashboard(
&Menu::Receive,
cache,
self.warning.as_ref(),
view::receive::receive(
&self.addresses.list,
self.qr_code.as_ref(),
&self.addresses.labels,
self.labels_edited.cache(),
),
);
if let Some(m) = &self.modal {
modal::Modal::new(content, m.view())
.on_blur(Some(view::Message::Close))
.into()
} else {
content
}
}
fn subscription(&self) -> Subscription<Message> {
if let Some(modal) = &self.modal {
modal.subscription()
} else {
Subscription::none()
}
}
fn update(
&mut self,
daemon: Arc<dyn Daemon + Sync + Send>,
cache: &Cache,
message: Message,
) -> Command<Message> {
match message {
Message::View(view::Message::Label(_, _)) | Message::LabelsUpdated(_) => {
match self.labels_edited.update(
daemon,
message,
std::iter::once(&mut self.addresses).map(|a| a as &mut dyn Labelled),
) {
Ok(cmd) => cmd,
Err(e) => {
self.warning = Some(e);
Command::none()
}
}
}
Message::ReceiveAddress(res) => {
match res {
Ok((address, derivation_index)) => {
self.warning = None;
self.qr_code = Some(qr_code::State::new(address.to_qr_uri()).unwrap());
self.addresses.list.push(address);
self.addresses.derivation_indexes.push(derivation_index);
}
Err(e) => self.warning = Some(e),
}
Command::none()
}
Message::View(view::Message::Close) => {
self.modal = None;
Command::none()
}
Message::View(view::Message::Select(i)) => {
self.modal = Some(VerifyAddressModal::new(
self.data_dir.clone(),
self.wallet.clone(),
cache.network,
self.addresses.list.get(i).expect("Must be present").clone(),
*self
.addresses
.derivation_indexes
.get(i)
.expect("Must be present"),
));
Command::none()
}
Message::View(view::Message::Next) => self.load(daemon),
_ => self
.modal
.as_mut()
.map(|m| m.update(daemon, cache, message))
.unwrap_or_else(Command::none),
}
}
fn load(&self, daemon: Arc<dyn Daemon + Sync + Send>) -> Command<Message> {
let daemon = daemon.clone();
Command::perform(
async move {
daemon
.get_new_address()
.map(|res| (res.address, res.derivation_index))
.map_err(|e| e.into())
},
Message::ReceiveAddress,
)
}
}
impl From<ReceivePanel> for Box<dyn State> {
fn from(s: ReceivePanel) -> Box<dyn State> {
Box::new(s)
}
}
pub struct VerifyAddressModal {
warning: Option<Error>,
chosen_hws: HashSet<Fingerprint>,
hws: HardwareWallets,
address: Address,
derivation_index: ChildNumber,
}
impl VerifyAddressModal {
pub fn new(
data_dir: PathBuf,
wallet: Arc<Wallet>,
network: Network,
address: Address,
derivation_index: ChildNumber,
) -> Self {
Self {
warning: None,
chosen_hws: HashSet::new(),
hws: HardwareWallets::new(data_dir, network).with_wallet(wallet),
address,
derivation_index,
}
}
}
impl VerifyAddressModal {
fn view(&self) -> Element<view::Message> {
view::receive::verify_address_modal(
self.warning.as_ref(),
&self.hws.list,
&self.chosen_hws,
&self.address,
&self.derivation_index,
)
}
fn subscription(&self) -> Subscription<Message> {
self.hws.refresh().map(Message::HardwareWallets)
}
fn update(
&mut self,
_daemon: Arc<dyn Daemon + Sync + Send>,
_cache: &Cache,
message: Message,
) -> Command<Message> {
match message {
Message::HardwareWallets(msg) => match self.hws.update(msg) {
Ok(cmd) => cmd.map(Message::HardwareWallets),
Err(e) => {
self.warning = Some(e.into());
Command::none()
}
},
Message::Verified(fg, res) => {
self.chosen_hws.remove(&fg);
if let Err(e) = res {
self.warning = Some(e);
}
Command::none()
}
Message::View(view::Message::SelectHardwareWallet(i)) => {
if let Some(HardwareWallet::Supported {
device,
fingerprint,
..
}) = self.hws.list.get(i)
{
self.warning = None;
self.chosen_hws.insert(*fingerprint);
let fg = *fingerprint;
Command::perform(
verify_address(device.clone(), self.derivation_index),
move |res| Message::Verified(fg, res),
)
} else {
Command::none()
}
}
_ => Command::none(),
}
}
}
async fn verify_address(
hw: std::sync::Arc<dyn async_hwi::HWI + Send + Sync>,
index: ChildNumber,
) -> Result<(), Error> {
hw.display_address(&async_hwi::AddressScript::Miniscript {
change: false,
index: index.into(),
})
.await?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
app::cache::Cache,
daemon::{
client::{Lianad, Request},
model::*,
},
utils::{mock::Daemon, sandbox::Sandbox},
};
use liana::{descriptors::LianaDescriptor, miniscript::bitcoin::Address};
use serde_json::json;
use std::str::FromStr;
const DESC: &str = "wsh(or_d(multi(2,[ffd63c8d/48'/1'/0'/2']tpubDExA3EC3iAsPxPhFn4j6gMiVup6V2eH3qKyk69RcTc9TTNRfFYVPad8bJD5FCHVQxyBT4izKsvr7Btd2R4xmQ1hZkvsqGBaeE82J71uTK4N/<0;1>/*,[de6eb005/48'/1'/0'/2']tpubDFGuYfS2JwiUSEXiQuNGdT3R7WTDhbaE6jbUhgYSSdhmfQcSx7ZntMPPv7nrkvAqjpj3jX9wbhSGMeKVao4qAzhbNyBi7iQmv5xxQk6H6jz/<0;1>/*),and_v(v:pkh([ffd63c8d/48'/1'/0'/2']tpubDExA3EC3iAsPxPhFn4j6gMiVup6V2eH3qKyk69RcTc9TTNRfFYVPad8bJD5FCHVQxyBT4izKsvr7Btd2R4xmQ1hZkvsqGBaeE82J71uTK4N/<2;3>/*),older(3))))#p9ax3xxp";
#[tokio::test]
async fn test_receive_panel() {
let addr =
Address::from_str("tb1qkldgvljmjpxrjq2ev5qxe8dvhn0dph9q85pwtfkjeanmwdue2akqj4twxj")
.unwrap()
.assume_checked();
let daemon = Daemon::new(vec![(
Some(json!({"method": "getnewaddress", "params": Option::<Request>::None})),
Ok(json!(GetAddressResult::new(
addr.clone(),
ChildNumber::from_normal_idx(0).unwrap()
))),
)]);
let sandbox: Sandbox<ReceivePanel> = Sandbox::new(ReceivePanel::new(
PathBuf::new(),
Arc::new(Wallet::new(LianaDescriptor::from_str(DESC).unwrap())),
));
let client = Arc::new(Lianad::new(daemon.run()));
let sandbox = sandbox.load(client, &Cache::default()).await;
let panel = sandbox.state();
assert_eq!(panel.addresses.list, vec![addr]);
}
}

View File

@ -107,3 +107,40 @@ pub fn hw_list_view_for_registration(
.style(theme::Container::Card(theme::Card::Simple))
.into()
}
pub fn hw_list_view_verify_address(
i: usize,
hw: &HardwareWallet,
chosen: bool,
) -> Element<Message> {
let mut bttn = Button::new(match hw {
HardwareWallet::Supported {
kind,
version,
fingerprint,
alias,
..
} => {
if chosen {
hw::processing_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
} else {
hw::supported_hardware_wallet(kind, version.as_ref(), fingerprint, alias.as_ref())
}
}
HardwareWallet::Unsupported { version, kind, .. } => {
hw::unsupported_hardware_wallet(&kind.to_string(), version.as_ref())
}
HardwareWallet::Locked {
kind, pairing_code, ..
} => hw::locked_hardware_wallet(kind, pairing_code.as_ref()),
})
.style(theme::Button::Border)
.width(Length::Fill);
if !chosen && hw.is_supported() {
bttn = bttn.on_press(Message::SelectHardwareWallet(i));
}
Container::new(bttn)
.width(Length::Fill)
.style(theme::Container::Card(theme::Card::Simple))
.into()
}

View File

@ -1,4 +1,4 @@
use std::collections::HashMap;
use std::collections::{HashMap, HashSet};
use iced::{
widget::{
@ -8,7 +8,11 @@ use iced::{
Alignment, Length,
};
use liana::miniscript::bitcoin;
use liana::miniscript::bitcoin::{
self,
bip32::{ChildNumber, Fingerprint},
Address,
};
use liana_ui::{
color,
@ -17,10 +21,17 @@ use liana_ui::{
text::{self, *},
},
icon, theme,
util::Collection,
widget::*,
};
use crate::app::view::label;
use crate::{
app::{
error::Error,
view::{hw, label, warning::warn},
},
hw::HardwareWallet,
};
use super::message::Message;
@ -44,9 +55,9 @@ pub fn receive<'a>(
.push(
Row::new()
.spacing(10)
.push(addresses.iter().rev().fold(
.push(addresses.iter().enumerate().rev().fold(
Column::new().spacing(10).width(Length::Fill),
|col, address| {
|col, (i, address)| {
let addr = address.to_string();
col.push(
card::simple(
@ -98,7 +109,12 @@ pub fn receive<'a>(
.style(theme::Button::TransparentBorder),
)
.align_items(Alignment::Center),
),
)
.push(
button::primary(None, "Verify on hardware device")
.on_press(Message::Select(i)),
)
.spacing(10),
)
.padding(20),
)
@ -115,3 +131,86 @@ pub fn receive<'a>(
.spacing(20)
.into()
}
pub fn verify_address_modal<'a>(
warning: Option<&Error>,
hws: &'a [HardwareWallet],
chosen_hws: &HashSet<Fingerprint>,
address: &Address,
derivation_index: &ChildNumber,
) -> Element<'a, Message> {
Column::new()
.push_maybe(warning.map(|w| warn(Some(w))))
.push(card::simple(
Column::new()
.push(
Column::new()
.push(
Column::new()
.push(
Row::new()
.width(Length::Fill)
.align_items(Alignment::Center)
.push(
Container::new(text("Address:").bold())
.width(Length::Fill),
)
.push(
Row::new()
.align_items(Alignment::Center)
.push(Container::new(
text(address.to_string()).small(),
))
.push(
Button::new(icon::clipboard_icon())
.on_press(Message::Clipboard(
address.to_string(),
))
.style(theme::Button::TransparentBorder),
)
.width(Length::Shrink),
),
)
.push(
Row::new()
.width(Length::Fill)
.align_items(Alignment::Center)
.push(
Container::new(text("Derivation index:").bold())
.width(Length::Fill),
)
.push(
Container::new(
text(derivation_index.to_string()).small(),
)
.width(Length::Shrink),
),
)
.spacing(5),
)
.push(text("Select device to verify address on:").width(Length::Fill))
.spacing(10)
.push(hws.iter().enumerate().fold(
Column::new().spacing(10),
|col, (i, hw)| {
col.push(hw::hw_list_view_verify_address(
i,
hw,
if let HardwareWallet::Supported { fingerprint, .. } = hw {
chosen_hws.contains(fingerprint)
} else {
false
},
))
},
))
.width(Length::Fill),
)
.spacing(20)
.width(Length::Fill)
.align_items(Alignment::Center),
))
.width(Length::Fill)
.max_width(750)
.into()
}

View File

@ -355,7 +355,13 @@ async fn refresh(mut state: State) -> (HardwareWalletMessage, State) {
still.push(id);
} else {
let device = specter::Specter::<specter::SerialTransport>::new(port.clone());
if device.is_connected().await.is_ok() {
if tokio::time::timeout(
std::time::Duration::from_millis(500),
device.fingerprint(),
)
.await
.is_ok()
{
match HardwareWallet::new(id, Arc::new(device), Some(&state.keys_aliases))
.await
{