mirror of
https://github.com/mimblewimble/grin-wallet.git
synced 2025-01-20 19:11:09 +03:00
Slatepack Workflow (#423)
* debugging tor sends * use fixed yeast version of hyper-socks2 * send command working according to slatepack * changes to handling proof address * modifications of recieve command to work with slatepack and to attempt to return to sender via TOR * finalize command tested * attempting to add invoice processing * fixes to tests, propagation of test mode where needed * modify return values of api functions that can send as sync * cleanup, testing and post_tx function * revert changes to API, many test fixes * deprecate http on the command-line warning
This commit is contained in:
parent
4fe6bf1c23
commit
60ab3728ab
28 changed files with 1425 additions and 738 deletions
175
Cargo.lock
generated
175
Cargo.lock
generated
|
@ -94,7 +94,7 @@ dependencies = [
|
|||
"rand 0.7.3",
|
||||
"scrypt",
|
||||
"secrecy",
|
||||
"sha2 0.8.1",
|
||||
"sha2 0.8.2",
|
||||
"subtle 2.2.2",
|
||||
"x25519-dalek",
|
||||
"zeroize 1.1.0",
|
||||
|
@ -195,9 +195,9 @@ version = "0.1.31"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26c4f3195085c36ea8d24d32b2f828d23296a9370a28aa39d111f6f16bef9f3b"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
"quote 1.0.6",
|
||||
"syn 1.0.22",
|
||||
"syn 1.0.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -271,7 +271,7 @@ dependencies = [
|
|||
"byteorder",
|
||||
"crypto-mac 0.7.0",
|
||||
"pbkdf2 0.3.0",
|
||||
"sha2 0.8.1",
|
||||
"sha2 0.8.2",
|
||||
"zeroize 1.1.0",
|
||||
]
|
||||
|
||||
|
@ -297,7 +297,7 @@ dependencies = [
|
|||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
"quote 1.0.6",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
|
@ -482,9 +482,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.53"
|
||||
version = "1.0.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "404b1fe4f65288577753b17e3b36a04596ee784493ec249bf81c7f2d2acd751c"
|
||||
checksum = "7bbb73db36c1246e9034e307d0fba23f9a2e251faa47ade70c1bd252220c8311"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
|
@ -676,9 +676,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.2.1"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
|
||||
checksum = "ab6bffe714b6bb07e42f201352c34f51fefd355ace793f9e638ebd52d23f98d2"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
|
@ -760,9 +760,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "data-encoding"
|
||||
version = "2.2.0"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "11c0346158a19b3627234e15596f5e465c360fcdb97d817bcb255e0510f5a788"
|
||||
checksum = "72aa14c04dfae8dd7d8a2b1cb7ca2152618cd01336dbfe704b8dcbf8d41dbd69"
|
||||
|
||||
[[package]]
|
||||
name = "difference"
|
||||
|
@ -892,7 +892,7 @@ dependencies = [
|
|||
"clear_on_drop",
|
||||
"curve25519-dalek",
|
||||
"rand 0.7.3",
|
||||
"sha2 0.8.1",
|
||||
"sha2 0.8.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -945,9 +945,9 @@ version = "0.1.8"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
"quote 1.0.6",
|
||||
"syn 1.0.22",
|
||||
"syn 1.0.27",
|
||||
"synstructure 0.12.3",
|
||||
]
|
||||
|
||||
|
@ -1073,9 +1073,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "d0b5a30a4328ab5473878237c447333c093297bded83a4983d10f4deea240d39"
|
||||
dependencies = [
|
||||
"proc-macro-hack",
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
"quote 1.0.6",
|
||||
"syn 1.0.22",
|
||||
"syn 1.0.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1176,7 +1176,7 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
|||
[[package]]
|
||||
name = "grin_api"
|
||||
version = "4.0.0-alpha.1"
|
||||
source = "git+https://github.com/mimblewimble/grin#5b825fbf0ddb7ccc6c2fcd49c1ec585fc0583331"
|
||||
source = "git+https://github.com/mimblewimble/grin#e7d2c71ca6c90695ecc5f1e9dc1cd3eab6050718"
|
||||
dependencies = [
|
||||
"bytes 0.5.4",
|
||||
"easy-jsonrpc-mw",
|
||||
|
@ -1209,7 +1209,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "grin_chain"
|
||||
version = "4.0.0-alpha.1"
|
||||
source = "git+https://github.com/mimblewimble/grin#5b825fbf0ddb7ccc6c2fcd49c1ec585fc0583331"
|
||||
source = "git+https://github.com/mimblewimble/grin#e7d2c71ca6c90695ecc5f1e9dc1cd3eab6050718"
|
||||
dependencies = [
|
||||
"bit-vec",
|
||||
"bitflags 1.2.1",
|
||||
|
@ -1232,7 +1232,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "grin_core"
|
||||
version = "4.0.0-alpha.1"
|
||||
source = "git+https://github.com/mimblewimble/grin#5b825fbf0ddb7ccc6c2fcd49c1ec585fc0583331"
|
||||
source = "git+https://github.com/mimblewimble/grin#e7d2c71ca6c90695ecc5f1e9dc1cd3eab6050718"
|
||||
dependencies = [
|
||||
"blake2-rfc",
|
||||
"byteorder",
|
||||
|
@ -1258,7 +1258,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "grin_keychain"
|
||||
version = "4.0.0-alpha.1"
|
||||
source = "git+https://github.com/mimblewimble/grin#5b825fbf0ddb7ccc6c2fcd49c1ec585fc0583331"
|
||||
source = "git+https://github.com/mimblewimble/grin#e7d2c71ca6c90695ecc5f1e9dc1cd3eab6050718"
|
||||
dependencies = [
|
||||
"blake2-rfc",
|
||||
"byteorder",
|
||||
|
@ -1280,7 +1280,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "grin_p2p"
|
||||
version = "4.0.0-alpha.1"
|
||||
source = "git+https://github.com/mimblewimble/grin#5b825fbf0ddb7ccc6c2fcd49c1ec585fc0583331"
|
||||
source = "git+https://github.com/mimblewimble/grin#e7d2c71ca6c90695ecc5f1e9dc1cd3eab6050718"
|
||||
dependencies = [
|
||||
"bitflags 1.2.1",
|
||||
"chrono",
|
||||
|
@ -1301,7 +1301,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "grin_pool"
|
||||
version = "4.0.0-alpha.1"
|
||||
source = "git+https://github.com/mimblewimble/grin#5b825fbf0ddb7ccc6c2fcd49c1ec585fc0583331"
|
||||
source = "git+https://github.com/mimblewimble/grin#e7d2c71ca6c90695ecc5f1e9dc1cd3eab6050718"
|
||||
dependencies = [
|
||||
"blake2-rfc",
|
||||
"chrono",
|
||||
|
@ -1335,7 +1335,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "grin_store"
|
||||
version = "4.0.0-alpha.1"
|
||||
source = "git+https://github.com/mimblewimble/grin#5b825fbf0ddb7ccc6c2fcd49c1ec585fc0583331"
|
||||
source = "git+https://github.com/mimblewimble/grin#e7d2c71ca6c90695ecc5f1e9dc1cd3eab6050718"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"croaring-mw",
|
||||
|
@ -1355,7 +1355,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "grin_util"
|
||||
version = "4.0.0-alpha.1"
|
||||
source = "git+https://github.com/mimblewimble/grin#5b825fbf0ddb7ccc6c2fcd49c1ec585fc0583331"
|
||||
source = "git+https://github.com/mimblewimble/grin#e7d2c71ca6c90695ecc5f1e9dc1cd3eab6050718"
|
||||
dependencies = [
|
||||
"backtrace",
|
||||
"base64 0.9.3",
|
||||
|
@ -1528,7 +1528,7 @@ dependencies = [
|
|||
"serde",
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha2 0.8.1",
|
||||
"sha2 0.8.2",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"uuid",
|
||||
|
@ -1730,8 +1730,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "hyper-socks2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b04745c5e7e8f4f93209f411644659055ce99f1973bd615015e3a7321ec2771"
|
||||
source = "git+https://github.com/yeastplume/hyper-socks2#08c3e5f6e8fb642110abe61e8a381195ac60cb6c"
|
||||
dependencies = [
|
||||
"async-socks5",
|
||||
"futures 0.3.5",
|
||||
|
@ -1824,9 +1823,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.39"
|
||||
version = "0.3.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa5a448de267e7358beaf4a5d849518fe9a0c13fce7afd44b06e68550e5562a7"
|
||||
checksum = "ce10c23ad2ea25ceca0093bd3192229da4c5b3c0f2de499c1ecac0d98d452177"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
@ -1887,9 +1886,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.70"
|
||||
version = "0.2.71"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3baa92041a6fec78c687fa0cc2b3fae8884f743d672cf551bed1d6dac6988d0f"
|
||||
checksum = "9457b06509d27052635f90d6466700c65095fdf75409b3fbdd903e988b886f49"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
|
@ -2106,7 +2105,7 @@ checksum = "f5e374eff525ce1c5b7687c4cef63943e7686524a387933ad27ca7ec43779cb3"
|
|||
dependencies = [
|
||||
"log",
|
||||
"mio",
|
||||
"miow 0.3.3",
|
||||
"miow 0.3.4",
|
||||
"winapi 0.3.8",
|
||||
]
|
||||
|
||||
|
@ -2135,9 +2134,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "miow"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "396aa0f2003d7df8395cb93e09871561ccc3e785f0acb369170e8cc74ddf9226"
|
||||
checksum = "22dfdd1d51b2639a5abd17ed07005c3af05fb7a2a3b1a1d0d7af1000a520c1c7"
|
||||
dependencies = [
|
||||
"socket2",
|
||||
"winapi 0.3.8",
|
||||
|
@ -2239,7 +2238,7 @@ checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6"
|
|||
dependencies = [
|
||||
"lexical-core",
|
||||
"memchr 2.3.3",
|
||||
"version_check 0.9.1",
|
||||
"version_check 0.9.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2432,9 +2431,9 @@ checksum = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de"
|
|||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.56"
|
||||
version = "0.9.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f02309a7f127000ed50594f0b50ecc69e7c654e16d41b4e8156d1b3df8e0b52e"
|
||||
checksum = "7410fef80af8ac071d4f63755c0ab89ac3df0fd1ea91f1d1f37cf5cec4395990"
|
||||
dependencies = [
|
||||
"autocfg 1.0.0",
|
||||
"cc",
|
||||
|
@ -2581,9 +2580,9 @@ version = "0.4.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e58db2081ba5b4c93bd6be09c40fd36cb9193a8336c384f3b40012e531aa7e40"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
"quote 1.0.6",
|
||||
"syn 1.0.22",
|
||||
"syn 1.0.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2606,9 +2605,9 @@ checksum = "05da548ad6865900e60eaba7f589cc0783590a92e940c26953ff81ddbab2d677"
|
|||
|
||||
[[package]]
|
||||
name = "podio"
|
||||
version = "0.1.6"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "780fb4b6698bbf9cf2444ea5d22411cef2953f0824b98f33cf454ec5615645bd"
|
||||
checksum = "b18befed8bc2b61abc79a457295e7e838417326da1586050b919414073977f19"
|
||||
|
||||
[[package]]
|
||||
name = "poly1305"
|
||||
|
@ -2651,9 +2650,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.15"
|
||||
version = "0.5.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0d659fe7c6d27f25e9d80a1a094c223f5246f6a6596453e09d7229bf42750b63"
|
||||
checksum = "7e0456befd48169b9f13ef0f0ad46d492cf9d2dbb918bcf38e01eed4ce3ec5e4"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-nested"
|
||||
|
@ -2672,9 +2671,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.13"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53f5ffe53a6b28e37c9c1ce74893477864d64f74778a93a4beb43c8fa167f639"
|
||||
checksum = "1502d12e458c49a4c9cbff560d0fe0060c252bc29799ed94ca2ed4bb665a0101"
|
||||
dependencies = [
|
||||
"unicode-xid 0.2.0",
|
||||
]
|
||||
|
@ -2700,7 +2699,7 @@ version = "1.0.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54a21852a652ad6f610c9510194f398ff6f8692e334fd1145fed931f7fbe44ea"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2938,9 +2937,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex"
|
||||
version = "1.3.7"
|
||||
version = "1.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6020f034922e3194c711b82a627453881bc4682166cabb07134a10c26ba7692"
|
||||
checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6"
|
||||
dependencies = [
|
||||
"aho-corasick",
|
||||
"memchr 2.3.3",
|
||||
|
@ -2950,9 +2949,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "regex-syntax"
|
||||
version = "0.6.17"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fe5bd57d1d7414c6b5ed48563a2c855d995ff777729dcd91c369ec7fea395ae"
|
||||
checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8"
|
||||
|
||||
[[package]]
|
||||
name = "remove_dir_all"
|
||||
|
@ -3170,7 +3169,7 @@ dependencies = [
|
|||
"byteorder",
|
||||
"hmac 0.7.1",
|
||||
"pbkdf2 0.3.0",
|
||||
"sha2 0.8.1",
|
||||
"sha2 0.8.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3282,9 +3281,9 @@ version = "1.0.110"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "818fbf6bfa9a42d3bfcaca148547aa00c7b915bec71d1757aa2d44ca68771984"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
"quote 1.0.6",
|
||||
"syn 1.0.22",
|
||||
"syn 1.0.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3324,9 +3323,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.8.1"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0"
|
||||
checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69"
|
||||
dependencies = [
|
||||
"block-buffer 0.7.3",
|
||||
"digest 0.8.1",
|
||||
|
@ -3493,11 +3492,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.22"
|
||||
version = "1.0.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1425de3c33b0941002740a420b1a906a350b88d08b82b2c8a01035a3f9447bac"
|
||||
checksum = "ef781e621ee763a2a40721a8861ec519cb76966aee03bb5d00adb6a31dc1c1de"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
"quote 1.0.6",
|
||||
"unicode-xid 0.2.0",
|
||||
]
|
||||
|
@ -3520,9 +3519,9 @@ version = "0.12.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
"quote 1.0.6",
|
||||
"syn 1.0.22",
|
||||
"syn 1.0.27",
|
||||
"unicode-xid 0.2.0",
|
||||
]
|
||||
|
||||
|
@ -3596,22 +3595,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.18"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5976891d6950b4f68477850b5b9e5aa64d955961466f9e174363f573e54e8ca7"
|
||||
checksum = "b13f926965ad00595dd129fa12823b04bbf866e9085ab0a5f2b05b850fbfc344"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.18"
|
||||
version = "1.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab81dbd1cd69cd2ce22ecfbdd3bdb73334ba25350649408cc6c085f46d89573d"
|
||||
checksum = "893582086c2f98cde18f906265a65b5030a074b1046c674ae898be6519a7f479"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
"quote 1.0.6",
|
||||
"syn 1.0.22",
|
||||
"syn 1.0.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3693,9 +3692,9 @@ version = "0.2.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0c3acc6aa564495a0f2e1d59fab677cd7f81a19994cfc7f3ad0e64301560389"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
"quote 1.0.6",
|
||||
"syn 1.0.22",
|
||||
"syn 1.0.27",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -3922,9 +3921,9 @@ checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
|||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.1"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce"
|
||||
checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
|
||||
|
||||
[[package]]
|
||||
name = "void"
|
||||
|
@ -3961,9 +3960,9 @@ checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.62"
|
||||
version = "0.2.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3c7d40d09cdbf0f4895ae58cf57d92e1e57a9dd8ed2e8390514b54a47cc5551"
|
||||
checksum = "4c2dc4aa152834bc334f506c1a06b866416a8b6697d5c9f75b9a689c8486def0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
|
@ -3971,24 +3970,24 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.62"
|
||||
version = "0.2.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3972e137ebf830900db522d6c8fd74d1900dcfc733462e9a12e942b00b4ac94"
|
||||
checksum = "ded84f06e0ed21499f6184df0e0cb3494727b0c5da89534e0fcc55c51d812101"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
"quote 1.0.6",
|
||||
"syn 1.0.22",
|
||||
"syn 1.0.27",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.62"
|
||||
version = "0.2.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2cd85aa2c579e8892442954685f0d801f9129de24fa2136b2c6a539c76b65776"
|
||||
checksum = "838e423688dac18d73e31edce74ddfac468e37b1506ad163ffaf0a46f703ffe3"
|
||||
dependencies = [
|
||||
"quote 1.0.6",
|
||||
"wasm-bindgen-macro-support",
|
||||
|
@ -3996,28 +3995,28 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.62"
|
||||
version = "0.2.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8eb197bd3a47553334907ffd2f16507b4f4f01bbec3ac921a7719e0decdfe72a"
|
||||
checksum = "3156052d8ec77142051a533cdd686cba889537b213f948cd1d20869926e68e92"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
"quote 1.0.6",
|
||||
"syn 1.0.22",
|
||||
"syn 1.0.27",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.62"
|
||||
version = "0.2.63"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a91c2916119c17a8e316507afaaa2dd94b47646048014bbdf6bef098c1bb58ad"
|
||||
checksum = "c9ba19973a58daf4db6f352eda73dc0e289493cd29fb2632eb172085b6521acd"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.39"
|
||||
version = "0.3.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8bc359e5dd3b46cb9687a051d50a2fdd228e4ba7cf6fcf861a5365c3d671a642"
|
||||
checksum = "7b72fe77fd39e4bd3eaa4412fd299a0be6b3dfe9d2597e2f1c20beb968f41d17"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
|
@ -4157,9 +4156,9 @@ version = "1.0.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de251eec69fc7c1bc3923403d18ececb929380e016afe103da75f396704f8ca2"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.13",
|
||||
"proc-macro2 1.0.17",
|
||||
"quote 1.0.6",
|
||||
"syn 1.0.22",
|
||||
"syn 1.0.27",
|
||||
"synstructure 0.12.3",
|
||||
]
|
||||
|
||||
|
|
|
@ -14,12 +14,14 @@
|
|||
|
||||
//! Foreign API External Definition
|
||||
|
||||
use crate::config::TorConfig;
|
||||
use crate::keychain::Keychain;
|
||||
use crate::libwallet::api_impl::foreign;
|
||||
use crate::libwallet::{
|
||||
BlockFees, CbData, Error, NodeClient, NodeVersionInfo, Slate, VersionInfo, WalletInst,
|
||||
WalletLCProvider,
|
||||
};
|
||||
use crate::try_slatepack_sync_workflow;
|
||||
use crate::util::secp::key::SecretKey;
|
||||
use crate::util::Mutex;
|
||||
use std::sync::Arc;
|
||||
|
@ -38,8 +40,10 @@ pub enum ForeignCheckMiddlewareFn {
|
|||
VerifySlateMessages,
|
||||
/// receive_tx
|
||||
ReceiveTx,
|
||||
/// finalize_invoice_tx
|
||||
/// finalize_invoice_tx (delete HF3)
|
||||
FinalizeInvoiceTx,
|
||||
/// finalize_tx
|
||||
FinalizeTx,
|
||||
}
|
||||
|
||||
/// Main interface into all wallet API functions.
|
||||
|
@ -70,6 +74,9 @@ where
|
|||
middleware: Option<ForeignCheckMiddleware>,
|
||||
/// Stored keychain mask (in case the stored wallet seed is tokenized)
|
||||
keychain_mask: Option<SecretKey>,
|
||||
/// Optional TOR configuration, holding address of sender and
|
||||
/// data directory
|
||||
tor_config: Mutex<Option<TorConfig>>,
|
||||
}
|
||||
|
||||
impl<'a, L, C, K> Foreign<'a, L, C, K>
|
||||
|
@ -158,7 +165,7 @@ where
|
|||
/// // All wallet functions operate on an Arc::Mutex to allow multithreading where needed
|
||||
/// let mut wallet = Arc::new(Mutex::new(wallet));
|
||||
///
|
||||
/// let api_foreign = Foreign::new(wallet.clone(), None, None);
|
||||
/// let api_foreign = Foreign::new(wallet.clone(), None, None, false);
|
||||
/// // .. perform wallet operations
|
||||
///
|
||||
/// ```
|
||||
|
@ -167,15 +174,30 @@ where
|
|||
wallet_inst: Arc<Mutex<Box<dyn WalletInst<'a, L, C, K>>>>,
|
||||
keychain_mask: Option<SecretKey>,
|
||||
middleware: Option<ForeignCheckMiddleware>,
|
||||
doctest_mode: bool,
|
||||
) -> Self {
|
||||
Foreign {
|
||||
wallet_inst,
|
||||
doctest_mode: false,
|
||||
doctest_mode,
|
||||
middleware,
|
||||
keychain_mask,
|
||||
tor_config: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the TOR configuration for this instance of the ForeignAPI, used during
|
||||
/// `recieve_tx` when a return address is specified
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `tor_config` - The optional [TorConfig](#) to use
|
||||
/// # Returns
|
||||
/// * Nothing
|
||||
|
||||
pub fn set_tor_config(&self, tor_config: Option<TorConfig>) {
|
||||
let mut lock = self.tor_config.lock();
|
||||
*lock = tor_config;
|
||||
}
|
||||
|
||||
/// Return the version capabilities of the running ForeignApi Node
|
||||
/// # Arguments
|
||||
/// None
|
||||
|
@ -186,7 +208,7 @@ where
|
|||
/// ```
|
||||
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
|
||||
///
|
||||
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None);
|
||||
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None, false);
|
||||
///
|
||||
/// let version_info = api_foreign.check_version();
|
||||
/// // check and proceed accordingly
|
||||
|
@ -238,7 +260,7 @@ where
|
|||
/// ```
|
||||
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
|
||||
///
|
||||
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None);
|
||||
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None, false);
|
||||
///
|
||||
/// let block_fees = BlockFees {
|
||||
/// fees: 800000,
|
||||
|
@ -294,6 +316,9 @@ where
|
|||
/// excess value).
|
||||
/// * `dest_acct_name` - The name of the account into which the slate should be received. If
|
||||
/// `None`, the default account is used.
|
||||
/// * `r_addr` - If included, attempt to send the slate back to the sender using the slatepack sync
|
||||
/// send (TOR). If providing this argument, check the `state` field of the slate to see if the
|
||||
/// sync_send was successful (it should be S3 if the synced send sent successfully).
|
||||
///
|
||||
/// # Returns
|
||||
/// * a result containing:
|
||||
|
@ -310,12 +335,12 @@ where
|
|||
/// ```
|
||||
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
|
||||
///
|
||||
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None);
|
||||
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None, false);
|
||||
/// # let slate = Slate::blank(2, false);
|
||||
///
|
||||
/// // . . .
|
||||
/// // Obtain a sent slate somehow
|
||||
/// let result = api_foreign.receive_tx(&slate, None);
|
||||
/// let result = api_foreign.receive_tx(&slate, None, None);
|
||||
///
|
||||
/// if let Ok(slate) = result {
|
||||
/// // Send back to recipient somehow
|
||||
|
@ -323,7 +348,12 @@ where
|
|||
/// }
|
||||
/// ```
|
||||
|
||||
pub fn receive_tx(&self, slate: &Slate, dest_acct_name: Option<&str>) -> Result<Slate, Error> {
|
||||
pub fn receive_tx(
|
||||
&self,
|
||||
slate: &Slate,
|
||||
dest_acct_name: Option<&str>,
|
||||
r_addr: Option<String>,
|
||||
) -> Result<Slate, Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
if let Some(m) = self.middleware.as_ref() {
|
||||
|
@ -333,23 +363,41 @@ where
|
|||
Some(slate),
|
||||
)?;
|
||||
}
|
||||
foreign::receive_tx(
|
||||
let ret_slate = foreign::receive_tx(
|
||||
&mut **w,
|
||||
(&self.keychain_mask).as_ref(),
|
||||
slate,
|
||||
dest_acct_name,
|
||||
self.doctest_mode,
|
||||
)
|
||||
)?;
|
||||
match r_addr {
|
||||
Some(a) => {
|
||||
let tor_config_lock = self.tor_config.lock();
|
||||
let res = try_slatepack_sync_workflow(
|
||||
&ret_slate,
|
||||
&a,
|
||||
tor_config_lock.clone(),
|
||||
None,
|
||||
true,
|
||||
self.doctest_mode,
|
||||
);
|
||||
match res {
|
||||
Ok(s) => return Ok(s.unwrap()),
|
||||
Err(_) => return Ok(ret_slate),
|
||||
}
|
||||
}
|
||||
None => Ok(ret_slate),
|
||||
}
|
||||
}
|
||||
|
||||
/// Finalizes an invoice transaction initiated by this wallet's Owner api.
|
||||
/// Finalizes a (standard or invoice) transaction initiated by this wallet's Owner api.
|
||||
/// This step assumes the paying party has completed round 1 and 2 of slate
|
||||
/// creation, and added their partial signatures. The invoicer will verify
|
||||
/// creation, and added their partial signatures. This wallet will verify
|
||||
/// and add their partial sig, then create the finalized transaction,
|
||||
/// ready to post to a node.
|
||||
///
|
||||
/// Note that this function DOES NOT POST the transaction to a node
|
||||
/// for validation. This is done in separately via the
|
||||
/// This function posts to the node if the `post_automatically`
|
||||
/// argument is sent to true. Posting can be done in separately via the
|
||||
/// [`post_tx`](struct.Owner.html#method.post_tx) function.
|
||||
///
|
||||
/// This function also stores the final transaction in the user's wallet files for retrieval
|
||||
|
@ -357,7 +405,8 @@ where
|
|||
///
|
||||
/// # Arguments
|
||||
/// * `slate` - The transaction [`Slate`](../grin_wallet_libwallet/slate/struct.Slate.html). The
|
||||
/// payer should have filled in round 1 and 2.
|
||||
/// * `post_automatically` - If true, post the finalized transaction to the configured listening
|
||||
/// node
|
||||
///
|
||||
/// # Returns
|
||||
/// * Ok([`slate`](../grin_wallet_libwallet/slate/struct.Slate.html)) if successful,
|
||||
|
@ -370,7 +419,7 @@ where
|
|||
/// # grin_wallet_api::doctest_helper_setup_doc_env_foreign!(wallet, wallet_config);
|
||||
///
|
||||
/// let mut api_owner = Owner::new(wallet.clone(), None);
|
||||
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None);
|
||||
/// let mut api_foreign = Foreign::new(wallet.clone(), None, None, false);
|
||||
///
|
||||
/// // . . .
|
||||
/// // Issue the invoice tx via the owner API
|
||||
|
@ -385,11 +434,11 @@ where
|
|||
/// // ...
|
||||
/// # let slate = Slate::blank(2, true);
|
||||
///
|
||||
/// let slate = api_foreign.finalize_invoice_tx(&slate);
|
||||
/// let slate = api_foreign.finalize_tx(&slate, true);
|
||||
/// // if okay, then post via the owner API
|
||||
/// ```
|
||||
|
||||
pub fn finalize_invoice_tx(&self, slate: &Slate) -> Result<Slate, Error> {
|
||||
pub fn finalize_tx(&self, slate: &Slate, post_automatically: bool) -> Result<Slate, Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
if let Some(m) = self.middleware.as_ref() {
|
||||
|
@ -399,7 +448,16 @@ where
|
|||
Some(slate),
|
||||
)?;
|
||||
}
|
||||
foreign::finalize_invoice_tx(&mut **w, (&self.keychain_mask).as_ref(), slate)
|
||||
let post_automatically = match self.doctest_mode {
|
||||
true => false,
|
||||
false => post_automatically,
|
||||
};
|
||||
foreign::finalize_tx(
|
||||
&mut **w,
|
||||
(&self.keychain_mask).as_ref(),
|
||||
slate,
|
||||
post_automatically,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -183,13 +183,12 @@ pub trait ForeignRpc {
|
|||
&self,
|
||||
slate: VersionedSlate,
|
||||
dest_acct_name: Option<String>,
|
||||
//TODO: Remove post-HF3
|
||||
message: Option<String>,
|
||||
dest: Option<String>,
|
||||
) -> Result<VersionedSlate, ErrorKind>;
|
||||
|
||||
/**
|
||||
|
||||
Networked version of [Foreign::finalize_invoice_tx](struct.Foreign.html#method.finalize_invoice_tx).
|
||||
Networked version of [Foreign::finalize_tx](struct.Foreign.html#method.finalize_tx).
|
||||
|
||||
# Json rpc example
|
||||
|
||||
|
@ -198,7 +197,7 @@ pub trait ForeignRpc {
|
|||
# r#"
|
||||
{
|
||||
"jsonrpc": "2.0",
|
||||
"method": "finalize_invoice_tx",
|
||||
"method": "finalize_tx",
|
||||
"id": 1,
|
||||
"params": [{
|
||||
"ver": "4:2",
|
||||
|
@ -279,6 +278,9 @@ pub trait ForeignRpc {
|
|||
# ,false, 5, false, true);
|
||||
```
|
||||
*/
|
||||
fn finalize_tx(&self, slate: VersionedSlate) -> Result<VersionedSlate, ErrorKind>;
|
||||
|
||||
/// For backwards-compatibility. Remove HF3
|
||||
fn finalize_invoice_tx(&self, slate: VersionedSlate) -> Result<VersionedSlate, ErrorKind>;
|
||||
}
|
||||
|
||||
|
@ -301,8 +303,7 @@ where
|
|||
&self,
|
||||
in_slate: VersionedSlate,
|
||||
dest_acct_name: Option<String>,
|
||||
//TODO: Remove post HF3
|
||||
_message: Option<String>,
|
||||
dest: Option<String>,
|
||||
) -> Result<VersionedSlate, ErrorKind> {
|
||||
let version = in_slate.version();
|
||||
let slate_from = Slate::from(in_slate);
|
||||
|
@ -310,15 +311,24 @@ where
|
|||
self,
|
||||
&slate_from,
|
||||
dest_acct_name.as_ref().map(String::as_str),
|
||||
dest,
|
||||
)
|
||||
.map_err(|e| e.kind())?;
|
||||
Ok(VersionedSlate::into_version(out_slate, version).map_err(|e| e.kind())?)
|
||||
}
|
||||
|
||||
fn finalize_tx(&self, in_slate: VersionedSlate) -> Result<VersionedSlate, ErrorKind> {
|
||||
let version = in_slate.version();
|
||||
let out_slate =
|
||||
Foreign::finalize_tx(self, &Slate::from(in_slate), true).map_err(|e| e.kind())?;
|
||||
Ok(VersionedSlate::into_version(out_slate, version).map_err(|e| e.kind())?)
|
||||
}
|
||||
|
||||
//TODO: Delete HF3
|
||||
fn finalize_invoice_tx(&self, in_slate: VersionedSlate) -> Result<VersionedSlate, ErrorKind> {
|
||||
let version = in_slate.version();
|
||||
let out_slate =
|
||||
Foreign::finalize_invoice_tx(self, &Slate::from(in_slate)).map_err(|e| e.kind())?;
|
||||
Foreign::finalize_tx(self, &Slate::from(in_slate), false).map_err(|e| e.kind())?;
|
||||
Ok(VersionedSlate::into_version(out_slate, version).map_err(|e| e.kind())?)
|
||||
}
|
||||
}
|
||||
|
@ -513,8 +523,8 @@ pub fn run_doctest_foreign(
|
|||
}
|
||||
|
||||
let mut api_foreign = match init_invoice_tx {
|
||||
false => Foreign::new(wallet1, mask1, Some(test_check_middleware)),
|
||||
true => Foreign::new(wallet2, mask2, Some(test_check_middleware)),
|
||||
false => Foreign::new(wallet1, mask1, Some(test_check_middleware), true),
|
||||
true => Foreign::new(wallet2, mask2, Some(test_check_middleware), true),
|
||||
};
|
||||
api_foreign.doctest_mode = true;
|
||||
let foreign_api = &api_foreign as &dyn ForeignRpc;
|
||||
|
|
|
@ -47,7 +47,7 @@ mod types;
|
|||
|
||||
pub use crate::foreign::{Foreign, ForeignCheckMiddleware, ForeignCheckMiddlewareFn};
|
||||
pub use crate::foreign_rpc::ForeignRpc;
|
||||
pub use crate::owner::Owner;
|
||||
pub use crate::owner::{try_slatepack_sync_workflow, Owner};
|
||||
pub use crate::owner_rpc::OwnerRpc;
|
||||
|
||||
pub use crate::foreign_rpc::foreign_rpc as foreign_rpc_client;
|
||||
|
|
201
api/src/owner.rs
201
api/src/owner.rs
|
@ -21,18 +21,21 @@ use uuid::Uuid;
|
|||
use crate::config::{TorConfig, WalletConfig};
|
||||
use crate::core::core::Transaction;
|
||||
use crate::core::global;
|
||||
use crate::impls::create_sender;
|
||||
use crate::impls::HttpSlateSender;
|
||||
use crate::impls::SlateSender as _;
|
||||
use crate::keychain::{Identifier, Keychain};
|
||||
use crate::libwallet::api_impl::owner_updater::{start_updater_log_thread, StatusMessage};
|
||||
use crate::libwallet::api_impl::{owner, owner_updater};
|
||||
use crate::libwallet::{
|
||||
AcctPathMapping, Error, ErrorKind, InitTxArgs, IssueInvoiceTxArgs, NodeClient,
|
||||
NodeHeightResult, OutputCommitMapping, PaymentProof, Slate, Slatepack, SlatepackAddress,
|
||||
TxLogEntry, WalletInfo, WalletInst, WalletLCProvider,
|
||||
AcctPathMapping, Error, InitTxArgs, IssueInvoiceTxArgs, NodeClient, NodeHeightResult,
|
||||
OutputCommitMapping, PaymentProof, Slate, Slatepack, SlatepackAddress, TxLogEntry, WalletInfo,
|
||||
WalletInst, WalletLCProvider,
|
||||
};
|
||||
use crate::util::logger::LoggingConfig;
|
||||
use crate::util::secp::key::SecretKey;
|
||||
use crate::util::{from_hex, static_secp_instance, Mutex, ZeroingString};
|
||||
use grin_wallet_util::OnionV3Address;
|
||||
use std::convert::TryFrom;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::mpsc::{channel, Sender};
|
||||
use std::sync::Arc;
|
||||
|
@ -63,6 +66,8 @@ where
|
|||
pub wallet_inst: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K>>>>,
|
||||
/// Flag to normalize some output during testing. Can mostly be ignored.
|
||||
pub doctest_mode: bool,
|
||||
/// retail TLD during doctest
|
||||
pub doctest_retain_tld: bool,
|
||||
/// Share ECDH key
|
||||
pub shared_key: Arc<Mutex<Option<SecretKey>>>,
|
||||
/// Update thread
|
||||
|
@ -189,6 +194,7 @@ where
|
|||
Owner {
|
||||
wallet_inst,
|
||||
doctest_mode: false,
|
||||
doctest_retain_tld: false,
|
||||
shared_key: Arc::new(Mutex::new(None)),
|
||||
updater,
|
||||
updater_running,
|
||||
|
@ -586,9 +592,10 @@ where
|
|||
///
|
||||
/// If the `send_args` [`InitTxSendArgs`](../grin_wallet_libwallet/types/struct.InitTxSendArgs.html),
|
||||
/// of the [`args`](../grin_wallet_libwallet/types/struct.InitTxArgs.html), field is Some, this
|
||||
/// function will attempt to perform a synchronous send to the recipient specified in the `dest`
|
||||
/// field according to the `method` field, and will also finalize and post the transaction if
|
||||
/// the `finalize` field is set.
|
||||
/// function will attempt to send the slate back to the sender using the slatepack sync
|
||||
/// send (TOR). If providing this argument, check the `state` field of the slate to see if the
|
||||
/// sync_send was successful (it should be S2 if the sync sent successfully). It will also post
|
||||
/// the transction if the `post_tx` field is set.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `keychain_mask` - Wallet secret mask to XOR against the stored wallet seed before using, if
|
||||
|
@ -648,39 +655,47 @@ where
|
|||
args: InitTxArgs,
|
||||
) -> Result<Slate, Error> {
|
||||
let send_args = args.send_args.clone();
|
||||
let mut slate = {
|
||||
let slate = {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
owner::init_send_tx(&mut **w, keychain_mask, args, self.doctest_mode)?
|
||||
};
|
||||
// Helper functionality. If send arguments exist, attempt to send
|
||||
// Helper functionality. If send arguments exist, attempt to send sync and
|
||||
// finalize
|
||||
match send_args {
|
||||
Some(sa) => {
|
||||
//TODO: in case of keybase, the response might take 60s and leave the service hanging
|
||||
match sa.method.as_ref() {
|
||||
"http" | "keybase" => {}
|
||||
_ => {
|
||||
error!("unsupported payment method: {}", sa.method);
|
||||
return Err(ErrorKind::ClientCallback(
|
||||
"unsupported payment method".to_owned(),
|
||||
)
|
||||
.into());
|
||||
}
|
||||
};
|
||||
let tor_config_lock = self.tor_config.lock();
|
||||
let comm_adapter = create_sender(&sa.method, &sa.dest, tor_config_lock.clone())
|
||||
.map_err(|e| ErrorKind::GenericError(format!("{}", e)))?;
|
||||
slate = comm_adapter.send_tx(&slate)?;
|
||||
self.tx_lock_outputs(keychain_mask, &slate)?;
|
||||
let slate = match sa.finalize {
|
||||
true => self.finalize_tx(keychain_mask, &slate)?,
|
||||
false => slate,
|
||||
};
|
||||
|
||||
let res = try_slatepack_sync_workflow(
|
||||
&slate,
|
||||
&sa.dest,
|
||||
tor_config_lock.clone(),
|
||||
None,
|
||||
false,
|
||||
self.doctest_mode,
|
||||
);
|
||||
match res {
|
||||
Ok(Some(s)) => {
|
||||
if sa.post_tx {
|
||||
self.post_tx(keychain_mask, &slate, sa.fluff)?;
|
||||
self.tx_lock_outputs(keychain_mask, &s)?;
|
||||
let ret_slate = self.finalize_tx(keychain_mask, &s)?;
|
||||
let result = self.post_tx(keychain_mask, &ret_slate, sa.fluff);
|
||||
match result {
|
||||
Ok(_) => {
|
||||
info!("Tx sent ok",);
|
||||
return Ok(ret_slate);
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Tx sent fail: {}", e);
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Ok(slate);
|
||||
}
|
||||
}
|
||||
Ok(None) => Ok(slate),
|
||||
Err(_) => Ok(slate),
|
||||
}
|
||||
Ok(slate)
|
||||
}
|
||||
None => Ok(slate),
|
||||
}
|
||||
|
@ -742,6 +757,12 @@ where
|
|||
/// it is up to the caller to present the request for payment to the user
|
||||
/// and verify that payment should go ahead.
|
||||
///
|
||||
/// If the `send_args` [`InitTxSendArgs`](../grin_wallet_libwallet/types/struct.InitTxSendArgs.html),
|
||||
/// of the [`args`](../grin_wallet_libwallet/types/struct.InitTxArgs.html), field is Some, this
|
||||
/// function will attempt to send the slate back to the initiator using the slatepack sync
|
||||
/// send (TOR). If providing this argument, check the `state` field of the slate to see if the
|
||||
/// sync_send was successful (it should be I3 if the sync sent successfully).
|
||||
///
|
||||
/// This function also stores the final transaction in the user's wallet files for retrieval
|
||||
/// via the [`get_stored_tx`](struct.Owner.html#method.get_stored_tx) function.
|
||||
///
|
||||
|
@ -794,7 +815,28 @@ where
|
|||
) -> Result<Slate, Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
let w = w_lock.lc_provider()?.wallet_inst()?;
|
||||
owner::process_invoice_tx(&mut **w, keychain_mask, slate, args, self.doctest_mode)
|
||||
let send_args = args.send_args.clone();
|
||||
let slate =
|
||||
owner::process_invoice_tx(&mut **w, keychain_mask, slate, args, self.doctest_mode)?;
|
||||
// Helper functionality. If send arguments exist, attempt to send
|
||||
match send_args {
|
||||
Some(sa) => {
|
||||
let tor_config_lock = self.tor_config.lock();
|
||||
let res = try_slatepack_sync_workflow(
|
||||
&slate,
|
||||
&sa.dest,
|
||||
tor_config_lock.clone(),
|
||||
None,
|
||||
true,
|
||||
self.doctest_mode,
|
||||
);
|
||||
match res {
|
||||
Ok(s) => Ok(s.unwrap()),
|
||||
Err(_) => Ok(slate),
|
||||
}
|
||||
}
|
||||
None => Ok(slate),
|
||||
}
|
||||
}
|
||||
|
||||
/// Locks the outputs associated with the inputs to the transaction in the given
|
||||
|
@ -1277,7 +1319,7 @@ where
|
|||
pub fn get_top_level_directory(&self) -> Result<String, Error> {
|
||||
let mut w_lock = self.wallet_inst.lock();
|
||||
let lc = w_lock.lc_provider()?;
|
||||
if self.doctest_mode {
|
||||
if self.doctest_mode && !self.doctest_retain_tld {
|
||||
Ok("/doctest/dir".to_owned())
|
||||
} else {
|
||||
lc.get_top_level_directory()
|
||||
|
@ -2249,6 +2291,99 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// attempt to send slate synchronously, starting with TOR and downgrading to HTTP
|
||||
pub fn try_slatepack_sync_workflow(
|
||||
slate: &Slate,
|
||||
dest: &str,
|
||||
tor_config: Option<TorConfig>,
|
||||
tor_sender: Option<HttpSlateSender>,
|
||||
send_to_finalize: bool,
|
||||
test_mode: bool,
|
||||
) -> Result<Option<Slate>, libwallet::Error> {
|
||||
let mut ret_slate = Slate::blank(2, false);
|
||||
let mut send_sync = |mut sender: HttpSlateSender, method_str: &str| match sender
|
||||
.send_tx(&slate, send_to_finalize)
|
||||
{
|
||||
Ok(s) => {
|
||||
ret_slate = s;
|
||||
return Ok(());
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(
|
||||
"Send ({}): Could not send Slate via {}: {}",
|
||||
method_str, method_str, e
|
||||
);
|
||||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
// First, try TOR
|
||||
match SlatepackAddress::try_from(dest) {
|
||||
Ok(address) => {
|
||||
let tor_addr = OnionV3Address::try_from(&address).unwrap();
|
||||
// Try sending to the destination via TOR
|
||||
let sender = match tor_sender {
|
||||
None => {
|
||||
if test_mode {
|
||||
None
|
||||
} else {
|
||||
match HttpSlateSender::with_socks_proxy(
|
||||
&tor_addr.to_http_str(),
|
||||
&tor_config.as_ref().unwrap().socks_proxy_addr,
|
||||
&tor_config.as_ref().unwrap().send_config_dir,
|
||||
) {
|
||||
Ok(s) => Some(s),
|
||||
Err(e) => {
|
||||
debug!("Send (TOR): Cannot create TOR Slate sender {:?}", e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(s) => {
|
||||
if test_mode {
|
||||
None
|
||||
} else {
|
||||
Some(s)
|
||||
}
|
||||
}
|
||||
};
|
||||
if let Some(s) = sender {
|
||||
warn!("Attempting to send transaction via TOR");
|
||||
match send_sync(s, "TOR") {
|
||||
Ok(_) => return Ok(Some(ret_slate)),
|
||||
Err(e) => {
|
||||
debug!("Unable to send via TOR: {}", e);
|
||||
warn!("Unable to send transaction via TOR. Attempting alternate methods.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Send (TOR): Destination is not SlatepackAddress {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Try Fallback to HTTP for deprecation period
|
||||
match HttpSlateSender::new(&dest) {
|
||||
Ok(sender) => {
|
||||
println!("Attempting to send transaction via HTTP (deprecated)");
|
||||
match send_sync(sender, "HTTP") {
|
||||
Ok(_) => return Ok(Some(ret_slate)),
|
||||
Err(e) => {
|
||||
debug!("Unable to send via HTTP: {}", e);
|
||||
warn!("Unable to send transaction via HTTP. Will output Slatepack.");
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Send (HTTP): Cannot create HTTP Slate sender {:?}", e);
|
||||
return Ok(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[macro_export]
|
||||
macro_rules! doctest_helper_setup_doc_env {
|
||||
|
|
|
@ -15,21 +15,23 @@
|
|||
//! Grin wallet command-line function implementations
|
||||
|
||||
use crate::api::TLSConfig;
|
||||
use crate::apiwallet::Owner;
|
||||
use crate::apiwallet::{try_slatepack_sync_workflow, Owner};
|
||||
use crate::config::{TorConfig, WalletConfig, WALLET_CONFIG_FILE_NAME};
|
||||
use crate::core::{core, global};
|
||||
use crate::error::{Error, ErrorKind};
|
||||
use crate::impls::{create_sender, KeybaseAllChannels, SlateGetter as _, SlateReceiver as _};
|
||||
use crate::impls::{HttpSlateSender, PathToSlate, SlatePutter};
|
||||
use crate::impls::SlateGetter as _;
|
||||
use crate::impls::{HttpSlateSender, PathToSlate, PathToSlatepack, SlatePutter};
|
||||
use crate::keychain;
|
||||
use crate::libwallet::{
|
||||
self, InitTxArgs, IssueInvoiceTxArgs, NodeClient, PaymentProof, Slate, SlateVersion,
|
||||
SlatepackAddress, WalletLCProvider,
|
||||
SlatepackAddress, Slatepacker, SlatepackerArgs, WalletLCProvider,
|
||||
};
|
||||
use crate::util::secp::key::SecretKey;
|
||||
use crate::util::{Mutex, ZeroingString};
|
||||
use crate::{controller, display};
|
||||
use grin_wallet_util::OnionV3Address;
|
||||
use serde_json as json;
|
||||
use std::convert::TryFrom;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
@ -71,6 +73,7 @@ pub fn init<L, C, K>(
|
|||
owner_api: &mut Owner<L, C, K>,
|
||||
_g_args: &GlobalArgs,
|
||||
args: InitArgs,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
|
@ -88,7 +91,7 @@ where
|
|||
args.recovery_phrase,
|
||||
args.list_length,
|
||||
args.password.clone(),
|
||||
false,
|
||||
test_mode,
|
||||
)?;
|
||||
|
||||
let m = p.get_mnemonic(None, args.password)?;
|
||||
|
@ -115,26 +118,23 @@ where
|
|||
}
|
||||
|
||||
/// Arguments for listen command
|
||||
pub struct ListenArgs {
|
||||
pub method: String,
|
||||
}
|
||||
pub struct ListenArgs {}
|
||||
|
||||
pub fn listen<L, C, K>(
|
||||
owner_api: &mut Owner<L, C, K>,
|
||||
keychain_mask: Arc<Mutex<Option<SecretKey>>>,
|
||||
config: &WalletConfig,
|
||||
tor_config: &TorConfig,
|
||||
args: &ListenArgs,
|
||||
_args: &ListenArgs,
|
||||
g_args: &GlobalArgs,
|
||||
cli_mode: bool,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let res = match args.method.as_str() {
|
||||
"http" => {
|
||||
let wallet_inst = owner_api.wallet_inst.clone();
|
||||
let config = config.clone();
|
||||
let tor_config = tor_config.clone();
|
||||
|
@ -148,6 +148,8 @@ where
|
|||
&config.api_listen_addr(),
|
||||
g_args.tls_conf.clone(),
|
||||
tor_config.use_tor_listener,
|
||||
test_mode,
|
||||
Some(tor_config.clone()),
|
||||
);
|
||||
if let Err(e) = res {
|
||||
error!("Error starting listener: {}", e);
|
||||
|
@ -164,26 +166,6 @@ where
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
"keybase" => KeybaseAllChannels::new()?.listen(
|
||||
config.clone(),
|
||||
g_args.password.clone().unwrap(),
|
||||
&g_args.account,
|
||||
g_args.node_api_secret.clone(),
|
||||
),
|
||||
method => {
|
||||
return Err(ErrorKind::ArgumentError(format!(
|
||||
"No listener for method \"{}\".",
|
||||
method
|
||||
))
|
||||
.into());
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(e) = res {
|
||||
return Err(ErrorKind::LibWallet(e.kind(), e.cause_string()).into());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn owner_api<L, C, K>(
|
||||
owner_api: &mut Owner<L, C, K>,
|
||||
|
@ -191,6 +173,7 @@ pub fn owner_api<L, C, K>(
|
|||
config: &WalletConfig,
|
||||
tor_config: &TorConfig,
|
||||
g_args: &GlobalArgs,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + Send + Sync + 'static,
|
||||
|
@ -208,6 +191,7 @@ where
|
|||
g_args.tls_conf.clone(),
|
||||
config.owner_api_include_foreign.clone(),
|
||||
Some(tor_config.clone()),
|
||||
test_mode,
|
||||
);
|
||||
if let Err(e) = res {
|
||||
return Err(ErrorKind::LibWallet(e.kind(), e.cause_string()).into());
|
||||
|
@ -260,12 +244,12 @@ where
|
|||
}
|
||||
|
||||
/// Arguments for the send command
|
||||
#[derive(Clone)]
|
||||
pub struct SendArgs {
|
||||
pub amount: u64,
|
||||
pub minimum_confirmations: u64,
|
||||
pub selection_strategy: String,
|
||||
pub estimate_selection_strategies: bool,
|
||||
pub method: String,
|
||||
pub dest: String,
|
||||
pub change_outputs: usize,
|
||||
pub fluff: bool,
|
||||
|
@ -283,6 +267,7 @@ pub fn send<L, C, K>(
|
|||
tor_config: Option<TorConfig>,
|
||||
args: SendArgs,
|
||||
dark_scheme: bool,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
|
@ -295,55 +280,121 @@ where
|
|||
// TODO: This block is temporary, for the period between the release of v4.0.0 and HF3,
|
||||
// after which this should be removable
|
||||
let mut args = args;
|
||||
|
||||
//TODO: Remove block post HF3
|
||||
// All this block does is determine whether the slate should be
|
||||
// output as a V3 Slate for the receiver
|
||||
let mut tor_sender = None;
|
||||
let is_pre_fork;
|
||||
{
|
||||
let invalid = || {
|
||||
ErrorKind::GenericError(format!(
|
||||
"Invalid wallet comm type and destination. method: {}, dest: {}",
|
||||
args.method, args.dest
|
||||
))
|
||||
};
|
||||
let trailing = match args.dest.ends_with('/') {
|
||||
true => "",
|
||||
false => "/",
|
||||
};
|
||||
let url_str = format!("{}{}v2/foreign", args.dest, trailing);
|
||||
match args.method.as_ref() {
|
||||
"http" => {
|
||||
let v_sender = HttpSlateSender::new(&args.dest).map_err(|_| invalid())?;
|
||||
let other_version = v_sender.check_other_version(&url_str)?;
|
||||
if other_version == SlateVersion::V3 {
|
||||
args.target_slate_version = Some(3);
|
||||
}
|
||||
}
|
||||
"tor" => {
|
||||
let v_sender = HttpSlateSender::with_socks_proxy(
|
||||
&args.dest,
|
||||
&tor_config.as_ref().unwrap().socks_proxy_addr,
|
||||
&tor_config.as_ref().unwrap().send_config_dir,
|
||||
)
|
||||
.map_err(|_| invalid())?;
|
||||
let other_version = v_sender.check_other_version(&url_str)?;
|
||||
if other_version == SlateVersion::V3 {
|
||||
args.target_slate_version = Some(3);
|
||||
}
|
||||
}
|
||||
"file" => {
|
||||
// For files spit out a V3 Slate if we're before HF3,
|
||||
// Or V4 slate otherwise
|
||||
is_pre_fork = {
|
||||
let cur_height = {
|
||||
libwallet::wallet_lock!(wallet_inst, w);
|
||||
w.w2n_client().get_chain_tip()?.0
|
||||
};
|
||||
// TODO: Floonet HF4
|
||||
match global::get_chain_type() {
|
||||
global::ChainTypes::Mainnet => {
|
||||
if cur_height < 786240 && !args.output_v4_slate {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
global::ChainTypes::Floonet => {
|
||||
if cur_height < 552960 && !args.output_v4_slate {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
|
||||
if is_pre_fork {
|
||||
let trailing = match args.dest.ends_with('/') {
|
||||
true => "",
|
||||
false => "/",
|
||||
};
|
||||
|
||||
let mut address_found = false;
|
||||
// For sync methods, derive intended endpoint from dest
|
||||
match SlatepackAddress::try_from(args.dest.as_str()) {
|
||||
Ok(address) => {
|
||||
let tor_addr = OnionV3Address::try_from(&address).unwrap();
|
||||
// Try pinging the destination via TOR
|
||||
debug!("Version ping: TOR address is: {}", tor_addr);
|
||||
match HttpSlateSender::with_socks_proxy(
|
||||
&tor_addr.to_http_str(),
|
||||
&tor_config.as_ref().unwrap().socks_proxy_addr,
|
||||
&tor_config.as_ref().unwrap().send_config_dir,
|
||||
) {
|
||||
Ok(mut sender) => {
|
||||
let url_str =
|
||||
format!("{}{}v2/foreign", tor_addr.to_http_str(), trailing);
|
||||
if let Ok(v) = sender.check_other_version(&url_str) {
|
||||
if v == SlateVersion::V3 {
|
||||
args.target_slate_version = Some(3);
|
||||
}
|
||||
address_found = true;
|
||||
}
|
||||
tor_sender = Some(sender);
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(
|
||||
"Version ping: Couldn't create slate sender for TOR: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
debug!("Version ping: Address is not SlatepackAddress: {:?}", e);
|
||||
}
|
||||
}
|
||||
|
||||
// now try http
|
||||
if !address_found {
|
||||
// Try pinging the destination via TOR
|
||||
match HttpSlateSender::new(&args.dest) {
|
||||
Ok(mut sender) => {
|
||||
let url_str = format!("{}{}v2/foreign", args.dest, trailing);
|
||||
match sender.check_other_version(&url_str) {
|
||||
Ok(v) => {
|
||||
if v == SlateVersion::V3 {
|
||||
args.target_slate_version = Some(3);
|
||||
}
|
||||
address_found = true;
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(
|
||||
"Version ping: Couldn't get other version for HTTP: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
debug!(
|
||||
"Version ping: Couldn't create slate sender for HTTP: {:?}",
|
||||
e
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !address_found {
|
||||
// otherwise, determine slate format based on block height
|
||||
// For files spit out a V3 Slate if we're before HF3,
|
||||
// Or V4 slate otherwise
|
||||
if is_pre_fork {
|
||||
args.target_slate_version = Some(3);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// end block to delete post HF3
|
||||
} // end pre HF3 Block
|
||||
|
||||
let mut slate = Slate::blank(2, false);
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
if args.estimate_selection_strategies {
|
||||
let strategies = vec!["smallest", "all"]
|
||||
|
@ -364,6 +415,7 @@ where
|
|||
})
|
||||
.collect();
|
||||
display::estimate(args.amount, strategies, dark_scheme);
|
||||
return Ok(());
|
||||
} else {
|
||||
let init_args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
|
@ -379,7 +431,7 @@ where
|
|||
..Default::default()
|
||||
};
|
||||
let result = api.init_send_tx(m, init_args);
|
||||
let mut slate = match result {
|
||||
slate = match result {
|
||||
Ok(s) => {
|
||||
info!(
|
||||
"Tx created: {} grin to {} (strategy '{}')",
|
||||
|
@ -394,57 +446,208 @@ where
|
|||
return Err(e);
|
||||
}
|
||||
};
|
||||
|
||||
match args.method.as_str() {
|
||||
"file" => {
|
||||
PathToSlate((&args.dest).into()).put_tx(&slate, false)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
return Ok(());
|
||||
}
|
||||
"binfile" => {
|
||||
PathToSlate((&args.dest).into()).put_tx(&slate, true)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
return Ok(());
|
||||
}
|
||||
"self" => {
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
let km = match keychain_mask.as_ref() {
|
||||
None => None,
|
||||
Some(&m) => Some(m.to_owned()),
|
||||
};
|
||||
controller::foreign_single_use(wallet_inst, km, |api| {
|
||||
slate = api.receive_tx(&slate, Some(&args.dest))?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
method => {
|
||||
let sender = create_sender(method, &args.dest, tor_config)?;
|
||||
slate = sender.send_tx(&slate)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
}
|
||||
}
|
||||
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
let result = api.post_tx(m, &slate, args.fluff);
|
||||
let res =
|
||||
try_slatepack_sync_workflow(&slate, &args.dest, tor_config, tor_sender, false, test_mode);
|
||||
|
||||
match res {
|
||||
Ok(Some(s)) => {
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
api.tx_lock_outputs(m, &s)?;
|
||||
let ret_slate = api.finalize_tx(m, &s)?;
|
||||
let result = api.post_tx(m, &ret_slate, args.fluff);
|
||||
match result {
|
||||
Ok(_) => {
|
||||
info!("Tx sent ok",);
|
||||
return Ok(());
|
||||
println!("Tx sent successfully",);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Tx sent fail: {}", e);
|
||||
return Err(e);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
Ok(None) => {
|
||||
output_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
&slate,
|
||||
args.dest.as_str(),
|
||||
true,
|
||||
false,
|
||||
is_pre_fork,
|
||||
)?;
|
||||
}
|
||||
Err(e) => return Err(e.into()),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn output_slatepack<L, C, K>(
|
||||
owner_api: &mut Owner<L, C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
dest: &str,
|
||||
lock: bool,
|
||||
file_only: bool,
|
||||
is_pre_fork: bool,
|
||||
) -> Result<(), libwallet::Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
// Output the slatepack file to stdout and to a file
|
||||
let mut message = String::from("");
|
||||
let mut address = None;
|
||||
let mut tld = String::from("");
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
address = match SlatepackAddress::try_from(dest) {
|
||||
Ok(a) => Some(a),
|
||||
Err(_) => None,
|
||||
};
|
||||
// encrypt for recipient by default
|
||||
let recipients = match address.clone() {
|
||||
Some(a) => vec![a],
|
||||
None => vec![],
|
||||
};
|
||||
message = api.create_slatepack_message(m, &slate, Some(0), recipients)?;
|
||||
tld = api.get_top_level_directory()?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
// create a directory to which files will be output
|
||||
let slate_dir = format!("{}/{}", tld, "slatepack");
|
||||
let _ = std::fs::create_dir_all(slate_dir.clone());
|
||||
let out_file_name = format!("{}/{}.{}.slatepack", slate_dir, slate.id, slate.state);
|
||||
|
||||
if lock {
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
|
||||
// TODO: Remove HF3
|
||||
if is_pre_fork || file_only {
|
||||
PathToSlate((&out_file_name).into()).put_tx(&slate, false)?;
|
||||
println!();
|
||||
println!("Transaction file was output to:");
|
||||
println!();
|
||||
println!("{}", out_file_name);
|
||||
println!();
|
||||
if !file_only {
|
||||
println!("Please send this file to the other party manually");
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("{}", out_file_name);
|
||||
let mut output = File::create(out_file_name.clone())?;
|
||||
output.write_all(&message.as_bytes())?;
|
||||
output.sync_all()?;
|
||||
|
||||
println!();
|
||||
println!("Slatepack data follows. Please provide this output to the other party");
|
||||
println!();
|
||||
println!("--- CUT BELOW THIS LINE ---");
|
||||
println!();
|
||||
println!("{}", message);
|
||||
println!("--- CUT ABOVE THIS LINE ---");
|
||||
println!();
|
||||
println!("Slatepack data was also output to");
|
||||
println!();
|
||||
println!("{}", out_file_name);
|
||||
println!();
|
||||
if address.is_some() {
|
||||
println!("The slatepack data is encrypted for the recipient only");
|
||||
} else {
|
||||
println!("The slatepack data is NOT encrypted");
|
||||
}
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Parse a slate and slatepack from a message
|
||||
pub fn parse_slatepack<L, C, K>(
|
||||
owner_api: &mut Owner<L, C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
filename: Option<String>,
|
||||
message: Option<String>,
|
||||
) -> Result<(Slate, Option<SlatepackAddress>), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K>,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let mut ret_address = None;
|
||||
let slate = match filename {
|
||||
Some(f) => {
|
||||
// first try regular slate - remove HF3
|
||||
let mut sl = match PathToSlate((&f).into()).get_tx() {
|
||||
Ok(s) => Some(s.0), //pre HF3, regular slate
|
||||
Err(_) => None,
|
||||
};
|
||||
// otherwise, get slate from slatepack
|
||||
if sl.is_none() {
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
let dec_key = api.get_slatepack_secret_key(m, 0)?;
|
||||
let packer = Slatepacker::new(SlatepackerArgs {
|
||||
sender: None,
|
||||
recipients: vec![],
|
||||
dec_key: Some(&dec_key),
|
||||
});
|
||||
let pts = PathToSlatepack::new(f.into(), &packer, true);
|
||||
sl = Some(pts.get_tx()?.0);
|
||||
ret_address = pts.get_slatepack()?.sender;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
sl
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let slate = match slate {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
// try and parse directly from input_slatepack_message
|
||||
let mut slate = Slate::blank(2, false);
|
||||
match message {
|
||||
Some(message) => {
|
||||
controller::owner_single_use(
|
||||
None,
|
||||
keychain_mask,
|
||||
Some(owner_api),
|
||||
|api, m| {
|
||||
slate =
|
||||
api.slate_from_slatepack_message(m, message.clone(), vec![0])?;
|
||||
let slatepack = api.decode_slatepack_message(message)?;
|
||||
ret_address = slatepack.sender;
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
}
|
||||
None => {
|
||||
let msg = "No slate provided via file or direct input";
|
||||
return Err(ErrorKind::GenericError(msg.into()).into());
|
||||
}
|
||||
}
|
||||
slate
|
||||
}
|
||||
};
|
||||
Ok((slate, ret_address))
|
||||
}
|
||||
|
||||
/// Receive command argument
|
||||
#[derive(Clone)]
|
||||
pub struct ReceiveArgs {
|
||||
pub input: String,
|
||||
pub input_file: Option<String>,
|
||||
pub input_slatepack_message: Option<String>,
|
||||
}
|
||||
|
||||
pub fn receive<L, C, K>(
|
||||
|
@ -452,35 +655,71 @@ pub fn receive<L, C, K>(
|
|||
keychain_mask: Option<&SecretKey>,
|
||||
g_args: &GlobalArgs,
|
||||
args: ReceiveArgs,
|
||||
tor_config: Option<TorConfig>,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K>,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let (mut slate, was_bin) = PathToSlate((&args.input).into()).get_tx()?;
|
||||
let (mut slate, ret_address) = parse_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
args.input_file,
|
||||
args.input_slatepack_message,
|
||||
)?;
|
||||
|
||||
let km = match keychain_mask.as_ref() {
|
||||
None => None,
|
||||
Some(&m) => Some(m.to_owned()),
|
||||
};
|
||||
|
||||
controller::foreign_single_use(owner_api.wallet_inst.clone(), km, |api| {
|
||||
slate = api.receive_tx(&slate, Some(&g_args.account))?;
|
||||
slate = api.receive_tx(&slate, Some(&g_args.account), None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
PathToSlate(format!("{}.response", args.input).into()).put_tx(&slate, was_bin)?;
|
||||
info!(
|
||||
"Response file {}.response generated, and can be sent back to the transaction originator.",
|
||||
args.input
|
||||
|
||||
let dest = match ret_address {
|
||||
Some(a) => String::try_from(&a).unwrap(),
|
||||
None => String::from(""),
|
||||
};
|
||||
|
||||
let res = try_slatepack_sync_workflow(&slate, &dest, tor_config, None, true, test_mode);
|
||||
|
||||
match res {
|
||||
Ok(Some(_)) => {
|
||||
println!();
|
||||
println!(
|
||||
"Transaction recieved and sent back to sender at {} for finalization.",
|
||||
dest
|
||||
);
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
Ok(None) => {
|
||||
output_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
&slate,
|
||||
&dest,
|
||||
false,
|
||||
false,
|
||||
slate.version_info.version < 4,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Finalize command args
|
||||
#[derive(Clone)]
|
||||
pub struct FinalizeArgs {
|
||||
pub input: String,
|
||||
pub input_file: Option<String>,
|
||||
pub input_slatepack_message: Option<String>,
|
||||
pub fluff: bool,
|
||||
pub nopost: bool,
|
||||
pub dest: Option<String>,
|
||||
}
|
||||
|
||||
pub fn finalize<L, C, K>(
|
||||
|
@ -493,7 +732,12 @@ where
|
|||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let (mut slate, was_bin) = PathToSlate((&args.input).into()).get_tx()?;
|
||||
let (mut slate, _ret_address) = parse_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
args.input_file.clone(),
|
||||
args.input_slatepack_message.clone(),
|
||||
)?;
|
||||
|
||||
// Rather than duplicating the entire command, we'll just
|
||||
// try to determine what kind of finalization this is
|
||||
|
@ -512,7 +756,7 @@ where
|
|||
Some(&m) => Some(m.to_owned()),
|
||||
};
|
||||
controller::foreign_single_use(owner_api.wallet_inst.clone(), km, |api| {
|
||||
slate = api.finalize_invoice_tx(&slate)?;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
} else {
|
||||
|
@ -522,7 +766,7 @@ where
|
|||
})?;
|
||||
}
|
||||
|
||||
if !args.nopost {
|
||||
if !&args.nopost {
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
let result = api.post_tx(m, &slate, args.fluff);
|
||||
match result {
|
||||
|
@ -530,6 +774,7 @@ where
|
|||
info!(
|
||||
"Transaction sent successfully, check the wallet again for confirmation."
|
||||
);
|
||||
println!("Transaction posted");
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
|
@ -540,9 +785,17 @@ where
|
|||
})?;
|
||||
}
|
||||
|
||||
if args.dest.is_some() {
|
||||
PathToSlate((&args.dest.unwrap()).into()).put_tx(&slate, was_bin)?;
|
||||
}
|
||||
println!("Transaction finalized successfully");
|
||||
|
||||
output_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
&slate,
|
||||
"",
|
||||
false,
|
||||
true,
|
||||
slate.version_info.version < 4,
|
||||
)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -553,9 +806,6 @@ pub struct IssueInvoiceArgs {
|
|||
pub dest: String,
|
||||
/// issue invoice tx args
|
||||
pub issue_args: IssueInvoiceTxArgs,
|
||||
/// whether to output as bin
|
||||
pub bin: bool,
|
||||
// TODO: Remove HF3
|
||||
/// whether to output a V4 slate
|
||||
pub output_v4_slate: bool,
|
||||
}
|
||||
|
@ -571,24 +821,52 @@ where
|
|||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
//TODO: Remove block HF3
|
||||
let args = {
|
||||
let mut a = args;
|
||||
let wallet_inst = owner_api.wallet_inst.clone();
|
||||
let is_pre_fork = {
|
||||
let cur_height = {
|
||||
let wallet_inst = owner_api.wallet_inst.clone();
|
||||
libwallet::wallet_lock!(wallet_inst, w);
|
||||
w.w2n_client().get_chain_tip()?.0
|
||||
};
|
||||
// TODO: Floonet HF4
|
||||
if cur_height < 786240 && !a.output_v4_slate && !a.bin {
|
||||
a.issue_args.target_slate_version = Some(3);
|
||||
match global::get_chain_type() {
|
||||
global::ChainTypes::Mainnet => {
|
||||
if cur_height < 786240 && !&args.output_v4_slate {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
global::ChainTypes::Floonet => {
|
||||
if cur_height < 552960 && !&args.output_v4_slate {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
a
|
||||
};
|
||||
|
||||
let mut issue_args = args.issue_args.clone();
|
||||
|
||||
if is_pre_fork {
|
||||
issue_args.target_slate_version = Some(3);
|
||||
}
|
||||
|
||||
let mut slate = Slate::blank(2, false);
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
let slate = api.issue_invoice_tx(m, args.issue_args)?;
|
||||
PathToSlate((&args.dest).into()).put_tx(&slate, args.bin)?;
|
||||
slate = api.issue_invoice_tx(m, issue_args)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
output_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
&slate,
|
||||
args.dest.as_str(),
|
||||
false,
|
||||
false,
|
||||
is_pre_fork,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -596,10 +874,9 @@ where
|
|||
pub struct ProcessInvoiceArgs {
|
||||
pub minimum_confirmations: u64,
|
||||
pub selection_strategy: String,
|
||||
pub method: String,
|
||||
pub dest: String,
|
||||
pub ret_address: Option<SlatepackAddress>,
|
||||
pub max_outputs: usize,
|
||||
pub input: String,
|
||||
pub slate: Slate,
|
||||
pub estimate_selection_strategies: bool,
|
||||
pub ttl_blocks: Option<u64>,
|
||||
}
|
||||
|
@ -611,14 +888,19 @@ pub fn process_invoice<L, C, K>(
|
|||
tor_config: Option<TorConfig>,
|
||||
args: ProcessInvoiceArgs,
|
||||
dark_scheme: bool,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let (slate, _) = PathToSlate((&args.input).into()).get_tx()?;
|
||||
let wallet_inst = owner_api.wallet_inst.clone();
|
||||
let mut slate = args.slate.clone();
|
||||
let dest = match args.ret_address.clone() {
|
||||
Some(a) => String::try_from(&a).unwrap(),
|
||||
None => String::from(""),
|
||||
};
|
||||
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
if args.estimate_selection_strategies {
|
||||
let strategies = vec!["smallest", "all"]
|
||||
|
@ -639,6 +921,7 @@ where
|
|||
})
|
||||
.collect();
|
||||
display::estimate(slate.amount, strategies, dark_scheme);
|
||||
return Ok(());
|
||||
} else {
|
||||
let init_args = InitTxArgs {
|
||||
src_acct_name: None,
|
||||
|
@ -652,12 +935,11 @@ where
|
|||
..Default::default()
|
||||
};
|
||||
let result = api.process_invoice_tx(m, &slate, init_args);
|
||||
let mut slate = match result {
|
||||
slate = match result {
|
||||
Ok(s) => {
|
||||
info!(
|
||||
"Invoice processed: {} grin to {} (strategy '{}')",
|
||||
"Invoice processed: {} grin (strategy '{}')",
|
||||
core::amount_to_hr_string(slate.amount, false),
|
||||
args.dest,
|
||||
args.selection_strategy,
|
||||
);
|
||||
s
|
||||
|
@ -667,40 +949,38 @@ where
|
|||
return Err(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
match args.method.as_str() {
|
||||
"file" => {
|
||||
let slate_putter = PathToSlate((&args.dest).into());
|
||||
slate_putter.put_tx(&slate, false)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
}
|
||||
"filebin" => {
|
||||
let slate_putter = PathToSlate((&args.dest).into());
|
||||
slate_putter.put_tx(&slate, true)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
}
|
||||
"self" => {
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
let km = match keychain_mask.as_ref() {
|
||||
None => None,
|
||||
Some(&m) => Some(m.to_owned()),
|
||||
};
|
||||
controller::foreign_single_use(wallet_inst, km, |api| {
|
||||
slate = api.finalize_invoice_tx(&slate)?;
|
||||
Ok(())
|
||||
})?;
|
||||
}
|
||||
method => {
|
||||
let sender = create_sender(method, &args.dest, tor_config)?;
|
||||
slate = sender.send_tx(&slate)?;
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
})?;
|
||||
let res = try_slatepack_sync_workflow(&slate, &dest, tor_config, None, true, test_mode);
|
||||
|
||||
match res {
|
||||
Ok(Some(_)) => {
|
||||
println!();
|
||||
println!(
|
||||
"Transaction paid and sent back to initiator at {} for finalization.",
|
||||
dest
|
||||
);
|
||||
println!();
|
||||
Ok(())
|
||||
}
|
||||
Ok(None) => {
|
||||
output_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
&slate,
|
||||
&dest,
|
||||
true,
|
||||
false,
|
||||
slate.version_info.version < 4,
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Info command args
|
||||
pub struct InfoArgs {
|
||||
pub minimum_confirmations: u64,
|
||||
|
@ -828,8 +1108,10 @@ where
|
|||
}
|
||||
|
||||
/// Post
|
||||
#[derive(Clone)]
|
||||
pub struct PostArgs {
|
||||
pub input: String,
|
||||
pub input_file: Option<String>,
|
||||
pub input_slatepack_message: Option<String>,
|
||||
pub fluff: bool,
|
||||
}
|
||||
|
||||
|
@ -843,10 +1125,16 @@ where
|
|||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let slate = PathToSlate((&args.input).into()).get_tx()?.0;
|
||||
let (slate, _ret_address) = parse_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
args.input_file,
|
||||
args.input_slatepack_message,
|
||||
)?;
|
||||
|
||||
let fluff = args.fluff;
|
||||
controller::owner_single_use(None, keychain_mask, Some(owner_api), |api, m| {
|
||||
api.post_tx(m, &slate, args.fluff)?;
|
||||
api.post_tx(m, &slate, fluff)?;
|
||||
info!("Posted transaction");
|
||||
return Ok(());
|
||||
})?;
|
||||
|
|
|
@ -18,8 +18,8 @@ use crate::api::{self, ApiServer, BasicAuthMiddleware, ResponseFuture, Router, T
|
|||
use crate::config::TorConfig;
|
||||
use crate::keychain::Keychain;
|
||||
use crate::libwallet::{
|
||||
address, Error, ErrorKind, NodeClient, NodeVersionInfo, Slate, WalletInst, WalletLCProvider,
|
||||
GRIN_BLOCK_HEADER_VERSION,
|
||||
address, Error, ErrorKind, NodeClient, NodeVersionInfo, Slate, SlatepackAddress, WalletInst,
|
||||
WalletLCProvider, GRIN_BLOCK_HEADER_VERSION,
|
||||
};
|
||||
use crate::util::secp::key::SecretKey;
|
||||
use crate::util::{from_hex, static_secp_instance, to_base64, Mutex};
|
||||
|
@ -32,6 +32,7 @@ use hyper::{Body, Request, Response, StatusCode};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use serde_json;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryFrom;
|
||||
use std::net::SocketAddr;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -59,12 +60,12 @@ fn check_middleware(
|
|||
// allow coinbases to be built regardless
|
||||
ForeignCheckMiddlewareFn::BuildCoinbase => Ok(()),
|
||||
_ => {
|
||||
let mut bhv = 2;
|
||||
let mut bhv = 3;
|
||||
if let Some(n) = node_version_info {
|
||||
bhv = n.block_header_version;
|
||||
}
|
||||
if let Some(s) = slate {
|
||||
if bhv > 3 && s.version_info.block_header_version < GRIN_BLOCK_HEADER_VERSION {
|
||||
if bhv > 4 && s.version_info.block_header_version < GRIN_BLOCK_HEADER_VERSION {
|
||||
Err(ErrorKind::Compatibility(
|
||||
"Incoming Slate is not compatible with this wallet. \
|
||||
Please upgrade the node or use a different one."
|
||||
|
@ -82,7 +83,7 @@ fn init_tor_listener<L, C, K>(
|
|||
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
|
||||
keychain_mask: Arc<Mutex<Option<SecretKey>>>,
|
||||
addr: &str,
|
||||
) -> Result<tor_process::TorProcess, Error>
|
||||
) -> Result<(tor_process::TorProcess, SlatepackAddress), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
C: NodeClient + 'static,
|
||||
|
@ -101,6 +102,7 @@ where
|
|||
.map_err(|e| ErrorKind::TorConfig(format!("{:?}", e).into()))?;
|
||||
let onion_address = OnionV3Address::from_private(&sec_key.0)
|
||||
.map_err(|e| ErrorKind::TorConfig(format!("{:?}", e).into()))?;
|
||||
let sp_address = SlatepackAddress::try_from(onion_address.clone())?;
|
||||
warn!(
|
||||
"Starting TOR Hidden Service for API listener at address {}, binding to {}",
|
||||
onion_address, addr
|
||||
|
@ -115,7 +117,7 @@ where
|
|||
.completion_percent(100)
|
||||
.launch()
|
||||
.map_err(|e| ErrorKind::TorProcess(format!("{:?}", e).into()))?;
|
||||
Ok(process)
|
||||
Ok((process, sp_address))
|
||||
}
|
||||
|
||||
/// Instantiate wallet Owner API for a single-use (command line) call
|
||||
|
@ -167,6 +169,7 @@ where
|
|||
wallet,
|
||||
keychain_mask,
|
||||
Some(check_middleware),
|
||||
false,
|
||||
))?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -183,6 +186,7 @@ pub fn owner_listener<L, C, K>(
|
|||
tls_config: Option<TLSConfig>,
|
||||
owner_api_include_foreign: Option<bool>,
|
||||
tor_config: Option<TorConfig>,
|
||||
test_mode: bool,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
|
@ -208,7 +212,7 @@ where
|
|||
let api_handler_v3 = OwnerAPIHandlerV3::new(
|
||||
wallet.clone(),
|
||||
keychain_mask.clone(),
|
||||
tor_config,
|
||||
tor_config.clone(),
|
||||
running_foreign,
|
||||
);
|
||||
|
||||
|
@ -219,7 +223,8 @@ where
|
|||
// If so configured, add the foreign API to the same port
|
||||
if running_foreign {
|
||||
warn!("Starting HTTP Foreign API on Owner server at {}.", addr);
|
||||
let foreign_api_handler_v2 = ForeignAPIHandlerV2::new(wallet, keychain_mask);
|
||||
let foreign_api_handler_v2 =
|
||||
ForeignAPIHandlerV2::new(wallet, keychain_mask, test_mode, Mutex::new(tor_config));
|
||||
router
|
||||
.add_route("/v2/foreign", Arc::new(foreign_api_handler_v2))
|
||||
.map_err(|_| ErrorKind::GenericError("Router failed to add route".to_string()))?;
|
||||
|
@ -247,6 +252,8 @@ pub fn foreign_listener<L, C, K>(
|
|||
addr: &str,
|
||||
tls_config: Option<TLSConfig>,
|
||||
use_tor: bool,
|
||||
test_mode: bool,
|
||||
tor_config: Option<TorConfig>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K> + 'static,
|
||||
|
@ -260,20 +267,21 @@ where
|
|||
let _ = lc.wallet_inst()?;
|
||||
}
|
||||
// need to keep in scope while the main listener is running
|
||||
let _tor_process = match use_tor {
|
||||
let (_tor_process, address) = match use_tor {
|
||||
true => match init_tor_listener(wallet.clone(), keychain_mask.clone(), addr) {
|
||||
Ok(tp) => Some(tp),
|
||||
Ok((tp, addr)) => (Some(tp), Some(addr)),
|
||||
Err(e) => {
|
||||
warn!("Unable to start TOR listener; Check that TOR executable is installed and on your path");
|
||||
warn!("Tor Error: {}", e);
|
||||
warn!("Listener will be available via HTTP only");
|
||||
None
|
||||
(None, None)
|
||||
}
|
||||
},
|
||||
false => None,
|
||||
false => (None, None),
|
||||
};
|
||||
|
||||
let api_handler_v2 = ForeignAPIHandlerV2::new(wallet, keychain_mask);
|
||||
let api_handler_v2 =
|
||||
ForeignAPIHandlerV2::new(wallet, keychain_mask, test_mode, Mutex::new(tor_config));
|
||||
let mut router = Router::new();
|
||||
|
||||
router
|
||||
|
@ -290,6 +298,9 @@ where
|
|||
))?;
|
||||
|
||||
warn!("HTTP Foreign listener started.");
|
||||
if let Some(a) = address {
|
||||
warn!("Slatepack Address is: {}", a);
|
||||
}
|
||||
|
||||
api_thread
|
||||
.join()
|
||||
|
@ -669,6 +680,10 @@ where
|
|||
pub wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
|
||||
/// Keychain mask
|
||||
pub keychain_mask: Arc<Mutex<Option<SecretKey>>>,
|
||||
/// run in doctest mode
|
||||
pub test_mode: bool,
|
||||
/// tor config
|
||||
pub tor_config: Mutex<Option<TorConfig>>,
|
||||
}
|
||||
|
||||
impl<L, C, K> ForeignAPIHandlerV2<L, C, K>
|
||||
|
@ -681,10 +696,14 @@ where
|
|||
pub fn new(
|
||||
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
|
||||
keychain_mask: Arc<Mutex<Option<SecretKey>>>,
|
||||
test_mode: bool,
|
||||
tor_config: Mutex<Option<TorConfig>>,
|
||||
) -> ForeignAPIHandlerV2<L, C, K> {
|
||||
ForeignAPIHandlerV2 {
|
||||
wallet,
|
||||
keychain_mask,
|
||||
test_mode,
|
||||
tor_config,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -707,8 +726,11 @@ where
|
|||
req: Request<Body>,
|
||||
mask: Option<SecretKey>,
|
||||
wallet: Arc<Mutex<Box<dyn WalletInst<'static, L, C, K> + 'static>>>,
|
||||
test_mode: bool,
|
||||
tor_config: Option<TorConfig>,
|
||||
) -> Result<Response<Body>, Error> {
|
||||
let api = Foreign::new(wallet, mask, Some(check_middleware));
|
||||
let api = Foreign::new(wallet, mask, Some(check_middleware), test_mode);
|
||||
api.set_tor_config(tor_config);
|
||||
let res = Self::call_api(req, api).await?;
|
||||
Ok(json_response_pretty(&res))
|
||||
}
|
||||
|
@ -723,9 +745,11 @@ where
|
|||
fn post(&self, req: Request<Body>) -> ResponseFuture {
|
||||
let mask = self.keychain_mask.lock().clone();
|
||||
let wallet = self.wallet.clone();
|
||||
let test_mode = self.test_mode;
|
||||
let tor_config = self.tor_config.lock().clone();
|
||||
|
||||
Box::pin(async move {
|
||||
match Self::handle_post_request(req, mask, wallet).await {
|
||||
match Self::handle_post_request(req, mask, wallet, test_mode, tor_config).await {
|
||||
Ok(v) => Ok(v),
|
||||
Err(e) => {
|
||||
error!("Request Error: {:?}", e);
|
||||
|
|
|
@ -143,7 +143,7 @@ fn file_exchange_test_impl(test_dir: &'static str, use_bin: bool) -> Result<(),
|
|||
|
||||
// wallet 2 receives file, completes, sends file back
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
slate = api.receive_tx(&slate, None)?;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
@ -226,7 +226,7 @@ fn file_exchange_test_impl(test_dir: &'static str, use_bin: bool) -> Result<(),
|
|||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = PathToSlate((&receive_file).into()).get_tx()?.0;
|
||||
slate = api.finalize_invoice_tx(&slate)?;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
PathToSlate((&final_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
@ -276,7 +276,7 @@ fn file_exchange_test_impl(test_dir: &'static str, use_bin: bool) -> Result<(),
|
|||
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
slate = api.receive_tx(&slate, None)?;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, use_bin)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
|
|
@ -128,7 +128,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
// wallet 2 finalizes and posts
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = api.finalize_invoice_tx(&slate)?;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
assert_eq!(slate.state, SlateState::Invoice3);
|
||||
|
@ -200,7 +200,7 @@ fn invoice_tx_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
// wallet 1 finalizes and posts
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = api.finalize_invoice_tx(&slate)?;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ fn no_change_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
// wallet 2 finalizes and posts
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
// Wallet 2 receives the invoice transaction
|
||||
slate = api.finalize_invoice_tx(&slate)?;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
wallet::controller::owner_single_use(Some(wallet2.clone()), mask1, None, |api, m| {
|
||||
|
|
|
@ -134,7 +134,7 @@ fn file_repost_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error>
|
|||
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
slate = PathToSlate((&send_file).into()).get_tx()?.0;
|
||||
slate = api.receive_tx(&slate, None)?;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
PathToSlate((&receive_file).into()).put_tx(&slate, false)?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
|
|
@ -97,7 +97,7 @@ fn self_send_test_impl(test_dir: &'static str) -> Result<(), libwallet::Error> {
|
|||
api.tx_lock_outputs(m, &slate)?;
|
||||
// Send directly to self
|
||||
wallet::controller::foreign_single_use(wallet1.clone(), mask1_i.clone(), |api| {
|
||||
slate = api.receive_tx(&slate, Some("listener"))?;
|
||||
slate = api.receive_tx(&slate, Some("listener"), None)?;
|
||||
Ok(())
|
||||
})?;
|
||||
slate = api.finalize_tx(m, &slate)?;
|
||||
|
|
|
@ -237,7 +237,7 @@ fn slatepack_exchange_test_impl(
|
|||
|
||||
// wallet 2 receives file, completes, sends file back
|
||||
wallet::controller::foreign_single_use(wallet2.clone(), mask2_i.clone(), |api| {
|
||||
slate = api.receive_tx(&slate, None)?;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
output_slatepack(
|
||||
&slate,
|
||||
&receive_file,
|
||||
|
@ -351,7 +351,7 @@ fn slatepack_exchange_test_impl(
|
|||
// Wallet 2 receives the invoice transaction
|
||||
let res = slate_from_packed(&receive_file, use_armored, (&dec_key_2).as_ref())?;
|
||||
slate = res.1;
|
||||
slate = api.finalize_invoice_tx(&slate)?;
|
||||
slate = api.finalize_tx(&slate, false)?;
|
||||
output_slatepack(&slate, &final_file, use_armored, use_bin, None, vec![])?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
@ -411,7 +411,7 @@ fn slatepack_exchange_test_impl(
|
|||
let res = slate_from_packed(&send_file, use_armored, (&dec_key_2).as_ref())?;
|
||||
slatepack = res.0;
|
||||
slate = res.1;
|
||||
slate = api.receive_tx(&slate, None)?;
|
||||
slate = api.receive_tx(&slate, None, None)?;
|
||||
output_slatepack(
|
||||
&slate,
|
||||
&receive_file,
|
||||
|
|
|
@ -33,7 +33,8 @@ hyper-timeout = "0.3"
|
|||
#Socks/Tor
|
||||
byteorder = "1"
|
||||
hyper = "0.13"
|
||||
hyper-socks2 = "0.4"
|
||||
#hyper-socks2 = "0.4"
|
||||
hyper-socks2 = { git = "https://github.com/yeastplume/hyper-socks2", branch = "master" }
|
||||
ed25519-dalek = "1.0.0-pre.1"
|
||||
x25519-dalek = "0.6"
|
||||
data-encoding = "2"
|
||||
|
|
|
@ -21,6 +21,7 @@ use serde::Serialize;
|
|||
use serde_json::{json, Value};
|
||||
use std::net::SocketAddr;
|
||||
use std::path::MAIN_SEPARATOR;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::tor::config as tor_config;
|
||||
use crate::tor::process as tor_process;
|
||||
|
@ -33,6 +34,7 @@ pub struct HttpSlateSender {
|
|||
use_socks: bool,
|
||||
socks_proxy_addr: Option<SocketAddr>,
|
||||
tor_config_dir: String,
|
||||
process: Option<Arc<tor_process::TorProcess>>,
|
||||
}
|
||||
|
||||
impl HttpSlateSender {
|
||||
|
@ -46,6 +48,7 @@ impl HttpSlateSender {
|
|||
use_socks: false,
|
||||
socks_proxy_addr: None,
|
||||
tor_config_dir: String::from(""),
|
||||
process: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -64,8 +67,39 @@ impl HttpSlateSender {
|
|||
Ok(ret)
|
||||
}
|
||||
|
||||
/// launch TOR process
|
||||
pub fn launch_tor(&mut self) -> Result<(), Error> {
|
||||
// set up tor send process if needed
|
||||
let mut tor = tor_process::TorProcess::new();
|
||||
if self.use_socks && self.process.is_none() {
|
||||
let tor_dir = format!(
|
||||
"{}{}{}",
|
||||
&self.tor_config_dir, MAIN_SEPARATOR, TOR_CONFIG_PATH
|
||||
);
|
||||
info!(
|
||||
"Starting TOR Process for send at {:?}",
|
||||
self.socks_proxy_addr
|
||||
);
|
||||
tor_config::output_tor_sender_config(
|
||||
&tor_dir,
|
||||
&self.socks_proxy_addr.unwrap().to_string(),
|
||||
)
|
||||
.map_err(|e| ErrorKind::TorConfig(format!("{:?}", e)))?;
|
||||
// Start TOR process
|
||||
tor.torrc_path(&format!("{}/torrc", &tor_dir))
|
||||
.working_dir(&tor_dir)
|
||||
.timeout(20)
|
||||
.completion_percent(100)
|
||||
.launch()
|
||||
.map_err(|e| ErrorKind::TorProcess(format!("{:?}", e)))?;
|
||||
self.process = Some(Arc::new(tor));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check version of the listening wallet
|
||||
pub fn check_other_version(&self, url: &str) -> Result<SlateVersion, Error> {
|
||||
pub fn check_other_version(&mut self, url: &str) -> Result<SlateVersion, Error> {
|
||||
self.launch_tor()?;
|
||||
let req = json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "check_version",
|
||||
|
@ -81,8 +115,8 @@ impl HttpSlateSender {
|
|||
report = "Other wallet is incompatible and requires an upgrade. \
|
||||
Please urge the other wallet owner to upgrade and try the transaction again."
|
||||
.to_string();
|
||||
}
|
||||
error!("{}", report);
|
||||
}
|
||||
ErrorKind::ClientCallback(report)
|
||||
})?;
|
||||
|
||||
|
@ -144,37 +178,14 @@ impl HttpSlateSender {
|
|||
}
|
||||
|
||||
impl SlateSender for HttpSlateSender {
|
||||
fn send_tx(&self, slate: &Slate) -> Result<Slate, Error> {
|
||||
fn send_tx(&mut self, slate: &Slate, finalize: bool) -> Result<Slate, Error> {
|
||||
let trailing = match self.base_url.ends_with('/') {
|
||||
true => "",
|
||||
false => "/",
|
||||
};
|
||||
let url_str = format!("{}{}v2/foreign", self.base_url, trailing);
|
||||
|
||||
// set up tor send process if needed
|
||||
let mut tor = tor_process::TorProcess::new();
|
||||
if self.use_socks {
|
||||
let tor_dir = format!(
|
||||
"{}{}{}",
|
||||
&self.tor_config_dir, MAIN_SEPARATOR, TOR_CONFIG_PATH
|
||||
);
|
||||
warn!(
|
||||
"Starting TOR Process for send at {:?}",
|
||||
self.socks_proxy_addr
|
||||
);
|
||||
tor_config::output_tor_sender_config(
|
||||
&tor_dir,
|
||||
&self.socks_proxy_addr.unwrap().to_string(),
|
||||
)
|
||||
.map_err(|e| ErrorKind::TorConfig(format!("{:?}", e)))?;
|
||||
// Start TOR process
|
||||
tor.torrc_path(&format!("{}/torrc", &tor_dir))
|
||||
.working_dir(&tor_dir)
|
||||
.timeout(20)
|
||||
.completion_percent(100)
|
||||
.launch()
|
||||
.map_err(|e| ErrorKind::TorProcess(format!("{:?}", e)))?;
|
||||
}
|
||||
self.launch_tor()?;
|
||||
|
||||
let slate_send = match self.check_other_version(&url_str)? {
|
||||
SlateVersion::V4 => VersionedSlate::into_version(slate.clone(), SlateVersion::V4)?,
|
||||
|
@ -192,7 +203,8 @@ impl SlateSender for HttpSlateSender {
|
|||
}
|
||||
};
|
||||
// Note: not using easy-jsonrpc as don't want the dependencies in this crate
|
||||
let req = json!({
|
||||
let req = match finalize {
|
||||
false => json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "receive_tx",
|
||||
"id": 1,
|
||||
|
@ -201,7 +213,17 @@ impl SlateSender for HttpSlateSender {
|
|||
null,
|
||||
null
|
||||
]
|
||||
});
|
||||
}),
|
||||
true => json!({
|
||||
"jsonrpc": "2.0",
|
||||
"method": "finalize_tx",
|
||||
"id": 1,
|
||||
"params": [
|
||||
slate_send
|
||||
]
|
||||
}),
|
||||
};
|
||||
|
||||
trace!("Sending receive_tx request: {}", req);
|
||||
|
||||
let res: String = self.post(&url_str, None, req).map_err(|e| {
|
||||
|
@ -209,7 +231,6 @@ impl SlateSender for HttpSlateSender {
|
|||
"Sending transaction slate to other wallet (is recipient listening?): {}",
|
||||
e
|
||||
);
|
||||
error!("{}", report);
|
||||
ErrorKind::ClientCallback(report)
|
||||
})?;
|
||||
|
||||
|
@ -225,6 +246,7 @@ impl SlateSender for HttpSlateSender {
|
|||
}
|
||||
|
||||
let slate_value = res["result"]["Ok"].clone();
|
||||
|
||||
trace!("slate_value: {}", slate_value);
|
||||
let slate = Slate::deserialize_upgrade(&serde_json::to_string(&slate_value).unwrap())
|
||||
.map_err(|e| {
|
||||
|
|
|
@ -292,7 +292,7 @@ fn poll(nseconds: u64, channel: &str) -> Option<Slate> {
|
|||
|
||||
impl SlateSender for KeybaseChannel {
|
||||
/// Send a slate to a keybase username then wait for a response for TTL seconds.
|
||||
fn send_tx(&self, slate: &Slate) -> Result<Slate, Error> {
|
||||
fn send_tx(&mut self, slate: &Slate, _finalize: bool) -> Result<Slate, Error> {
|
||||
let id = slate.id;
|
||||
|
||||
// Send original slate to recipient with the SLATE_NEW topic
|
||||
|
|
|
@ -31,7 +31,7 @@ use crate::util::ZeroingString;
|
|||
pub trait SlateSender {
|
||||
/// Send a transaction slate to another listening wallet and return result
|
||||
/// TODO: Probably need a slate wrapper type
|
||||
fn send_tx(&self, slate: &Slate) -> Result<Slate, Error>;
|
||||
fn send_tx(&mut self, slate: &Slate, finalize: bool) -> Result<Slate, Error>;
|
||||
}
|
||||
|
||||
pub trait SlateReceiver {
|
||||
|
|
|
@ -76,12 +76,24 @@ impl WalletSeed {
|
|||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn init_new(seed_length: usize) -> WalletSeed {
|
||||
pub fn init_new(
|
||||
seed_length: usize,
|
||||
test_mode: bool,
|
||||
password: Option<util::ZeroingString>,
|
||||
) -> WalletSeed {
|
||||
let mut seed: Vec<u8> = vec![];
|
||||
let mut rng = thread_rng();
|
||||
if !test_mode {
|
||||
for _ in 0..seed_length {
|
||||
seed.push(rng.gen());
|
||||
}
|
||||
} else {
|
||||
// Hash password and use for test seed so we have a way of keeping test wallets unique
|
||||
// but predictable
|
||||
seed = blake2::blake2b::blake2b(32, b"", password.unwrap().as_bytes())
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
}
|
||||
WalletSeed(seed)
|
||||
}
|
||||
|
||||
|
@ -167,7 +179,7 @@ impl WalletSeed {
|
|||
|
||||
let seed = match recovery_phrase {
|
||||
Some(p) => WalletSeed::from_mnemonic(p)?,
|
||||
None => WalletSeed::init_new(seed_length),
|
||||
None => WalletSeed::init_new(seed_length, test_mode, Some(password.clone())),
|
||||
};
|
||||
|
||||
let enc_seed = EncryptedWalletSeed::from_seed(&seed, password)?;
|
||||
|
@ -324,7 +336,7 @@ mod tests {
|
|||
#[test]
|
||||
fn wallet_seed_encrypt() {
|
||||
let password = ZeroingString::from("passwoid");
|
||||
let wallet_seed = WalletSeed::init_new(32);
|
||||
let wallet_seed = WalletSeed::init_new(32, false, None);
|
||||
let mut enc_wallet_seed =
|
||||
EncryptedWalletSeed::from_seed(&wallet_seed, password.clone()).unwrap();
|
||||
println!("EWS: {:?}", enc_wallet_seed);
|
||||
|
|
|
@ -284,7 +284,7 @@ impl TorProcess {
|
|||
impl Drop for TorProcess {
|
||||
// kill the child
|
||||
fn drop(&mut self) {
|
||||
trace!("DROPPING TOR PROCESS");
|
||||
debug!("Dropping TOR process");
|
||||
self.kill().unwrap_or(());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
//! Generic implementation of owner API functions
|
||||
use strum::IntoEnumIterator;
|
||||
|
||||
use crate::api_impl::owner::check_ttl;
|
||||
use crate::api_impl::owner::finalize_tx as owner_finalize;
|
||||
use crate::api_impl::owner::{check_ttl, post_tx};
|
||||
use crate::grin_core::core::transaction::Transaction;
|
||||
use crate::grin_keychain::Keychain;
|
||||
use crate::grin_util::secp::key::SecretKey;
|
||||
|
@ -132,11 +133,12 @@ where
|
|||
Ok(ret_slate)
|
||||
}
|
||||
|
||||
/// Receive an tx that this wallet has issued
|
||||
pub fn finalize_invoice_tx<'a, T: ?Sized, C, K>(
|
||||
/// Receive a tx that this wallet has issued
|
||||
pub fn finalize_tx<'a, T: ?Sized, C, K>(
|
||||
w: &mut T,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
slate: &Slate,
|
||||
post_automatically: bool,
|
||||
) -> Result<Slate, Error>
|
||||
where
|
||||
T: WalletBackend<'a, C, K>,
|
||||
|
@ -144,8 +146,10 @@ where
|
|||
K: Keychain + 'a,
|
||||
{
|
||||
let mut sl = slate.clone();
|
||||
check_ttl(w, &sl)?;
|
||||
let context = w.get_private_context(keychain_mask, sl.id.as_bytes())?;
|
||||
let is_invoice = context.is_invoice;
|
||||
if is_invoice {
|
||||
check_ttl(w, &sl)?;
|
||||
if sl.is_compact() {
|
||||
let mut temp_ctx = context.clone();
|
||||
temp_ctx.sec_key = context.initial_sec_key.clone();
|
||||
|
@ -163,5 +167,11 @@ where
|
|||
if sl.is_compact() {
|
||||
sl.amount = 0;
|
||||
}
|
||||
} else {
|
||||
sl = owner_finalize(w, keychain_mask, slate)?;
|
||||
}
|
||||
if post_automatically {
|
||||
post_tx(w.w2n_client(), sl.tx_or_err()?, true)?;
|
||||
}
|
||||
Ok(sl)
|
||||
}
|
||||
|
|
|
@ -80,12 +80,8 @@ pub struct InitTxArgs {
|
|||
/// in one go
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct InitTxSendArgs {
|
||||
/// The transaction method. Can currently be 'http' or 'keybase'.
|
||||
pub method: String,
|
||||
/// The destination, contents will depend on the particular method
|
||||
pub dest: String,
|
||||
/// Whether to finalize the result immediately if the send was successful
|
||||
pub finalize: bool,
|
||||
/// Whether to post the transasction if the send and finalize were successful
|
||||
pub post_tx: bool,
|
||||
/// Whether to use dandelion when posting. If false, skip the dandelion relay
|
||||
|
|
|
@ -161,6 +161,21 @@ pub enum SlateState {
|
|||
Invoice3,
|
||||
}
|
||||
|
||||
impl fmt::Display for SlateState {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let res = match self {
|
||||
SlateState::Unknown => "UN",
|
||||
SlateState::Standard1 => "S1",
|
||||
SlateState::Standard2 => "S2",
|
||||
SlateState::Standard3 => "S3",
|
||||
SlateState::Invoice1 => "I1",
|
||||
SlateState::Invoice2 => "I2",
|
||||
SlateState::Invoice3 => "I3",
|
||||
};
|
||||
write!(f, "{}", res)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
/// Kernel features arguments definition
|
||||
pub struct KernelFeaturesArgs {
|
||||
|
|
|
@ -118,32 +118,15 @@ subcommands:
|
|||
long: change_outputs
|
||||
default_value: "1"
|
||||
takes_value: true
|
||||
- method:
|
||||
help: Method for sending this transaction
|
||||
short: m
|
||||
long: method
|
||||
possible_values:
|
||||
- http
|
||||
- file
|
||||
- binfile
|
||||
- self
|
||||
- keybase
|
||||
default_value: http
|
||||
takes_value: true
|
||||
- dest:
|
||||
help: Send the transaction to the provided server (start with http://) or save as file.
|
||||
short: d
|
||||
long: dest
|
||||
takes_value: true
|
||||
- request_payment_proof:
|
||||
help: Request a payment proof from the recipient. If sending to a tor address, the address will be filled automatically.
|
||||
help: Request a payment proof from the recipient. If present, the destination must be provided as a slatepack address.
|
||||
short: y
|
||||
long: request_payment_proof
|
||||
- proof_address:
|
||||
help: Recipient proof address. If not using TOR, must be provided seprarately by the recipient
|
||||
short: z
|
||||
long: proof_address
|
||||
takes_value: true
|
||||
- fluff:
|
||||
help: Fluff the transaction (ignore Dandelion relay protocol)
|
||||
short: f
|
||||
|
@ -187,11 +170,6 @@ subcommands:
|
|||
help: Do not post the transaction.
|
||||
short: n
|
||||
long: nopost
|
||||
- dest:
|
||||
help: Specify file to save the finalized slate.
|
||||
short: d
|
||||
long: dest
|
||||
takes_value: true
|
||||
- invoice:
|
||||
about: Initialize an invoice transaction.
|
||||
args:
|
||||
|
@ -203,10 +181,6 @@ subcommands:
|
|||
short: d
|
||||
long: dest
|
||||
takes_value: true
|
||||
- bin:
|
||||
help: Whether to output file as binary
|
||||
short: b
|
||||
long: bin
|
||||
#TODO: Remove HF3
|
||||
- v4:
|
||||
help: Output a V4 slate prior to HF3 block
|
||||
|
@ -234,17 +208,6 @@ subcommands:
|
|||
help: Estimates all possible Coin/Output selection strategies.
|
||||
short: e
|
||||
long: estimate-selection
|
||||
- method:
|
||||
help: Method for sending the processed invoice back to the invoice creator
|
||||
short: m
|
||||
long: method
|
||||
possible_values:
|
||||
- filebin
|
||||
- file
|
||||
- http
|
||||
- self
|
||||
default_value: file
|
||||
takes_value: true
|
||||
- dest:
|
||||
help: Send the transaction to the provided server (start with http://) or save as file.
|
||||
short: d
|
||||
|
|
|
@ -20,7 +20,7 @@ use semver::Version;
|
|||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
const MIN_COMPAT_NODE_VERSION: &str = "3.0.0";
|
||||
const MIN_COMPAT_NODE_VERSION: &str = "4.0.0-alpha.1";
|
||||
|
||||
pub fn wallet_command<C>(
|
||||
wallet_args: &ArgMatches<'_>,
|
||||
|
|
|
@ -25,11 +25,9 @@ use grin_wallet_api::Owner;
|
|||
use grin_wallet_config::{config_file_exists, TorConfig, WalletConfig};
|
||||
use grin_wallet_controller::command;
|
||||
use grin_wallet_controller::{Error, ErrorKind};
|
||||
use grin_wallet_impls::tor::config::is_tor_address;
|
||||
use grin_wallet_impls::{DefaultLCProvider, DefaultWalletImpl};
|
||||
use grin_wallet_impls::{PathToSlate, SlateGetter as _};
|
||||
use grin_wallet_libwallet::{self, Slate, SlatepackAddress, SlatepackArmor};
|
||||
use grin_wallet_libwallet::{IssueInvoiceTxArgs, NodeClient, WalletInst, WalletLCProvider};
|
||||
use grin_wallet_libwallet::{Slate, SlatepackAddress};
|
||||
use grin_wallet_util::grin_core as core;
|
||||
use grin_wallet_util::grin_core::core::amount_to_hr_string;
|
||||
use grin_wallet_util::grin_keychain as keychain;
|
||||
|
@ -137,7 +135,39 @@ where
|
|||
Ok(phrase)
|
||||
}
|
||||
|
||||
fn prompt_pay_invoice(slate: &Slate, method: &str, dest: &str) -> Result<bool, ParseError> {
|
||||
fn prompt_slatepack() -> Result<String, ParseError> {
|
||||
let interface = Arc::new(Interface::new("slatepack_input")?);
|
||||
let mut message = String::from("");
|
||||
interface.set_report_signal(Signal::Interrupt, true);
|
||||
interface.set_prompt("")?;
|
||||
loop {
|
||||
println!("Please paste your encoded slatepack message:");
|
||||
let res = interface.read_line()?;
|
||||
match res {
|
||||
ReadResult::Eof => break,
|
||||
ReadResult::Signal(sig) => {
|
||||
if sig == Signal::Interrupt {
|
||||
interface.cancel_read_line()?;
|
||||
return Err(ParseError::CancelledError);
|
||||
}
|
||||
}
|
||||
ReadResult::Input(line) => {
|
||||
if SlatepackArmor::decode(&line).is_ok() {
|
||||
message = line;
|
||||
break;
|
||||
} else {
|
||||
println!();
|
||||
println!("Input is not a valid slatepack.");
|
||||
println!();
|
||||
interface.set_buffer(&line)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(message)
|
||||
}
|
||||
|
||||
fn prompt_pay_invoice(slate: &Slate, dest: &str) -> Result<bool, ParseError> {
|
||||
let interface = Arc::new(Interface::new("pay")?);
|
||||
let amount = amount_to_hr_string(slate.amount, false);
|
||||
interface.set_report_signal(Signal::Interrupt, true);
|
||||
|
@ -154,10 +184,11 @@ fn prompt_pay_invoice(slate: &Slate, method: &str, dest: &str) -> Result<bool, P
|
|||
"* {} of your wallet funds will be added to the transaction to pay this invoice.",
|
||||
amount
|
||||
);
|
||||
if method == "http" {
|
||||
println!("* The resulting transaction will IMMEDIATELY be sent to the wallet listening at: '{}'.", dest);
|
||||
if dest.len() > 0 {
|
||||
println!("* The wallet will IMMEDIATELY attempt to send the resulting transaction to the wallet listening at: '{}'.", dest);
|
||||
println!("* If other wallet is not listening, the resulting transaction will output as a slatepack which you can manually send back to the invoice creator.");
|
||||
} else {
|
||||
println!("* The resulting transaction will be saved to the file '{}', which you can manually send back to the invoice creator.", dest);
|
||||
println!("* The resulting transaction will output as a slatepack which you can manually send back to the invoice creator.");
|
||||
}
|
||||
println!();
|
||||
println!("Please review the above information carefully before proceeding");
|
||||
|
@ -189,6 +220,39 @@ fn prompt_pay_invoice(slate: &Slate, method: &str, dest: &str) -> Result<bool, P
|
|||
}
|
||||
}
|
||||
|
||||
fn prompt_deprecate_http() -> Result<bool, ParseError> {
|
||||
let interface = Arc::new(Interface::new("http")?);
|
||||
interface.set_report_signal(Signal::Interrupt, true);
|
||||
interface.set_prompt("To proceed, type 'UNDERSTOOD' > ")?;
|
||||
println!();
|
||||
println!("Http(s) is being deprecated in favour of the Slatepack Workflow");
|
||||
println!("This sending method is planned for removal as of the last scheduled Hardfork in Grin 5.0.0");
|
||||
println!("Please see https://github.com/mimblewimble/grin-rfcs/pull/55 for details");
|
||||
loop {
|
||||
let res = interface.read_line()?;
|
||||
match res {
|
||||
ReadResult::Eof => return Ok(false),
|
||||
ReadResult::Signal(sig) => {
|
||||
if sig == Signal::Interrupt {
|
||||
interface.cancel_read_line()?;
|
||||
return Err(ParseError::CancelledError);
|
||||
}
|
||||
}
|
||||
ReadResult::Input(line) => match line.trim() {
|
||||
"Q" | "q" => return Err(ParseError::CancelledError),
|
||||
result => {
|
||||
if result == "UNDERSTOOD" {
|
||||
return Ok(true);
|
||||
} else {
|
||||
println!("Please enter the phrase 'UNDERSTOOD' (without quotes) to continue or Q to quit");
|
||||
println!();
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// instantiate wallet (needed by most functions)
|
||||
|
||||
pub fn inst_wallet<L, C, K>(
|
||||
|
@ -349,13 +413,10 @@ pub fn parse_listen_args(
|
|||
if let Some(port) = args.value_of("port") {
|
||||
config.api_listen_port = port.parse().unwrap();
|
||||
}
|
||||
let method = parse_required(args, "method")?;
|
||||
if args.is_present("no_tor") {
|
||||
tor_config.use_tor_listener = false;
|
||||
}
|
||||
Ok(command::ListenArgs {
|
||||
method: method.to_owned(),
|
||||
})
|
||||
Ok(command::ListenArgs {})
|
||||
}
|
||||
|
||||
pub fn parse_owner_api_args(
|
||||
|
@ -404,36 +465,14 @@ pub fn parse_send_args(args: &ArgMatches) -> Result<command::SendArgs, ParseErro
|
|||
// estimate_selection_strategies
|
||||
let estimate_selection_strategies = args.is_present("estimate_selection_strategies");
|
||||
|
||||
// method
|
||||
let method = parse_required(args, "method")?;
|
||||
|
||||
// dest
|
||||
let dest = {
|
||||
if method == "self" {
|
||||
match args.value_of("dest") {
|
||||
let dest = match args.value_of("dest") {
|
||||
Some(d) => d,
|
||||
None => "default",
|
||||
}
|
||||
} else {
|
||||
if !estimate_selection_strategies {
|
||||
parse_required(args, "dest")?
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if !estimate_selection_strategies
|
||||
&& method == "http"
|
||||
&& !dest.starts_with("http://")
|
||||
&& !dest.starts_with("https://")
|
||||
&& is_tor_address(&dest).is_err()
|
||||
{
|
||||
let msg = format!(
|
||||
"HTTP Destination should start with http://: or https://: {}",
|
||||
dest,
|
||||
);
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
if dest.to_uppercase().starts_with("HTTP") {
|
||||
prompt_deprecate_http()?;
|
||||
}
|
||||
|
||||
// change_outputs
|
||||
|
@ -465,23 +504,25 @@ pub fn parse_send_args(args: &ArgMatches) -> Result<command::SendArgs, ParseErro
|
|||
|
||||
let payment_proof_address = {
|
||||
match args.is_present("request_payment_proof") {
|
||||
true => {
|
||||
// if the destination address is a TOR address, we don't need the address
|
||||
// separately
|
||||
match OnionV3Address::try_from(dest) {
|
||||
true => match OnionV3Address::try_from(dest) {
|
||||
Ok(a) => Some(SlatepackAddress::try_from(a).unwrap()),
|
||||
Err(_) => {
|
||||
let addr = parse_required(args, "proof_address")?;
|
||||
match OnionV3Address::try_from(addr) {
|
||||
Ok(a) => Some(SlatepackAddress::try_from(a).unwrap()),
|
||||
let addr = match parse_required(args, "dest") {
|
||||
Ok(a) => a,
|
||||
Err(_) => {
|
||||
let msg = format!("Destination Slatepack address must be provided (-d) if payment proof is requested");
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
};
|
||||
match SlatepackAddress::try_from(addr) {
|
||||
Ok(a) => Some(a),
|
||||
Err(e) => {
|
||||
let msg = format!("Invalid proof address: {:?}", e);
|
||||
let msg = format!("Invalid slatepack address: {:?}", e);
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
false => None,
|
||||
}
|
||||
};
|
||||
|
@ -491,7 +532,6 @@ pub fn parse_send_args(args: &ArgMatches) -> Result<command::SendArgs, ParseErro
|
|||
minimum_confirmations: min_c,
|
||||
selection_strategy: selection_strategy.to_owned(),
|
||||
estimate_selection_strategies,
|
||||
method: method.to_owned(),
|
||||
dest: dest.to_owned(),
|
||||
change_outputs: change_outputs,
|
||||
fluff: fluff,
|
||||
|
@ -503,41 +543,59 @@ pub fn parse_send_args(args: &ArgMatches) -> Result<command::SendArgs, ParseErro
|
|||
})
|
||||
}
|
||||
|
||||
pub fn parse_receive_args(receive_args: &ArgMatches) -> Result<command::ReceiveArgs, ParseError> {
|
||||
// input
|
||||
let tx_file = parse_required(receive_args, "input")?;
|
||||
|
||||
pub fn parse_receive_args(args: &ArgMatches) -> Result<command::ReceiveArgs, ParseError> {
|
||||
// input file
|
||||
let input_file = match args.is_present("input") {
|
||||
true => {
|
||||
let file = args.value_of("input").unwrap().to_owned();
|
||||
// validate input
|
||||
if !Path::new(&tx_file).is_file() {
|
||||
let msg = format!("File {} not found.", &tx_file);
|
||||
if !Path::new(&file).is_file() {
|
||||
let msg = format!("File {} not found.", &file);
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
Some(file)
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
|
||||
let mut input_slatepack_message = None;
|
||||
if input_file.is_none() {
|
||||
input_slatepack_message = Some(prompt_slatepack()?);
|
||||
}
|
||||
|
||||
Ok(command::ReceiveArgs {
|
||||
input: tx_file.to_owned(),
|
||||
input_file,
|
||||
input_slatepack_message,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn parse_finalize_args(args: &ArgMatches) -> Result<command::FinalizeArgs, ParseError> {
|
||||
let fluff = args.is_present("fluff");
|
||||
let nopost = args.is_present("nopost");
|
||||
let tx_file = parse_required(args, "input")?;
|
||||
|
||||
if !Path::new(&tx_file).is_file() {
|
||||
let msg = format!("File {} not found.", tx_file);
|
||||
let input_file = match args.is_present("input") {
|
||||
true => {
|
||||
let file = args.value_of("input").unwrap().to_owned();
|
||||
// validate input
|
||||
if !Path::new(&file).is_file() {
|
||||
let msg = format!("File {} not found.", &file);
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
|
||||
let dest_file = match args.is_present("dest") {
|
||||
true => Some(args.value_of("dest").unwrap().to_owned()),
|
||||
Some(file)
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
|
||||
let mut input_slatepack_message = None;
|
||||
if input_file.is_none() {
|
||||
input_slatepack_message = Some(prompt_slatepack()?);
|
||||
}
|
||||
|
||||
Ok(command::FinalizeArgs {
|
||||
input: tx_file.to_owned(),
|
||||
input_file,
|
||||
input_slatepack_message,
|
||||
fluff: fluff,
|
||||
nopost: nopost,
|
||||
dest: dest_file.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -556,7 +614,6 @@ pub fn parse_issue_invoice_args(
|
|||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
};
|
||||
let bin = args.is_present("bin");
|
||||
|
||||
// TODO: Remove HF3
|
||||
let output_v4_slate = args.is_present("v4");
|
||||
|
@ -571,11 +628,15 @@ pub fn parse_issue_invoice_args(
|
|||
false => None,
|
||||
}
|
||||
};
|
||||
// dest (output file)
|
||||
let dest = parse_required(args, "dest")?;
|
||||
|
||||
// dest, for encryption
|
||||
let dest = match args.value_of("dest") {
|
||||
Some(d) => d,
|
||||
None => "default",
|
||||
};
|
||||
|
||||
Ok(command::IssueInvoiceArgs {
|
||||
dest: dest.into(),
|
||||
bin,
|
||||
output_v4_slate,
|
||||
issue_args: IssueInvoiceTxArgs {
|
||||
dest_acct_name: None,
|
||||
|
@ -585,9 +646,50 @@ pub fn parse_issue_invoice_args(
|
|||
})
|
||||
}
|
||||
|
||||
fn get_slate<L, C, K>(
|
||||
owner_api: &mut Owner<L, C, K>,
|
||||
keychain_mask: Option<&SecretKey>,
|
||||
args: &ArgMatches,
|
||||
) -> Result<(Slate, Option<SlatepackAddress>), Error>
|
||||
where
|
||||
L: WalletLCProvider<'static, C, K>,
|
||||
C: NodeClient + 'static,
|
||||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let input_file = match args.is_present("input") {
|
||||
true => {
|
||||
let file = args.value_of("input").unwrap().to_owned();
|
||||
// validate input
|
||||
if !Path::new(&file).is_file() {
|
||||
let msg = format!("File {} not found.", &file);
|
||||
return Err(ErrorKind::GenericError(msg).into());
|
||||
}
|
||||
Some(file)
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
|
||||
let mut input_slatepack_message = None;
|
||||
if input_file.is_none() {
|
||||
input_slatepack_message = Some(prompt_slatepack().map_err(|e| {
|
||||
let msg = format!("{}", e);
|
||||
ErrorKind::GenericError(msg)
|
||||
})?)
|
||||
}
|
||||
|
||||
command::parse_slatepack(
|
||||
owner_api,
|
||||
keychain_mask,
|
||||
input_file,
|
||||
input_slatepack_message,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn parse_process_invoice_args(
|
||||
args: &ArgMatches,
|
||||
prompt: bool,
|
||||
slate: Slate,
|
||||
ret_address: Option<SlatepackAddress>,
|
||||
) -> Result<command::ProcessInvoiceArgs, ParseError> {
|
||||
// minimum_confirmations
|
||||
let min_c = parse_required(args, "minimum_confirmations")?;
|
||||
|
@ -599,65 +701,28 @@ pub fn parse_process_invoice_args(
|
|||
// estimate_selection_strategies
|
||||
let estimate_selection_strategies = args.is_present("estimate_selection_strategies");
|
||||
|
||||
// method
|
||||
let method = parse_required(args, "method")?;
|
||||
|
||||
// dest
|
||||
let dest = {
|
||||
if method == "self" {
|
||||
match args.value_of("dest") {
|
||||
Some(d) => d,
|
||||
None => "default",
|
||||
}
|
||||
} else {
|
||||
if !estimate_selection_strategies {
|
||||
parse_required(args, "dest")?
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
};
|
||||
if !estimate_selection_strategies
|
||||
&& method == "http"
|
||||
&& !dest.starts_with("http://")
|
||||
&& !dest.starts_with("https://")
|
||||
{
|
||||
let msg = format!(
|
||||
"HTTP Destination should start with http://: or https://: {}",
|
||||
dest,
|
||||
);
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
|
||||
// ttl_blocks
|
||||
let ttl_blocks = parse_u64_or_none(args.value_of("ttl_blocks"));
|
||||
|
||||
// max_outputs
|
||||
let max_outputs = 500;
|
||||
|
||||
// file input only
|
||||
let tx_file = parse_required(args, "input")?;
|
||||
|
||||
if prompt {
|
||||
// Now we need to prompt the user whether they want to do this,
|
||||
// which requires reading the slate
|
||||
|
||||
let slate = match PathToSlate((&tx_file).into()).get_tx() {
|
||||
Ok(s) => s.0,
|
||||
Err(e) => return Err(ParseError::ArgumentError(format!("{}", e))),
|
||||
let dest = match ret_address.clone() {
|
||||
Some(a) => String::try_from(&a).unwrap(),
|
||||
None => String::from(""),
|
||||
};
|
||||
|
||||
prompt_pay_invoice(&slate, method, dest)?;
|
||||
// Now we need to prompt the user whether they want to do this,
|
||||
prompt_pay_invoice(&slate, &dest)?;
|
||||
}
|
||||
|
||||
Ok(command::ProcessInvoiceArgs {
|
||||
minimum_confirmations: min_c,
|
||||
selection_strategy: selection_strategy.to_owned(),
|
||||
estimate_selection_strategies,
|
||||
method: method.to_owned(),
|
||||
dest: dest.to_owned(),
|
||||
max_outputs: max_outputs,
|
||||
input: tx_file.to_owned(),
|
||||
ret_address,
|
||||
slate,
|
||||
max_outputs,
|
||||
ttl_blocks,
|
||||
})
|
||||
}
|
||||
|
@ -712,12 +777,31 @@ pub fn parse_txs_args(args: &ArgMatches) -> Result<command::TxsArgs, ParseError>
|
|||
}
|
||||
|
||||
pub fn parse_post_args(args: &ArgMatches) -> Result<command::PostArgs, ParseError> {
|
||||
let tx_file = parse_required(args, "input")?;
|
||||
let fluff = args.is_present("fluff");
|
||||
|
||||
// input file
|
||||
let input_file = match args.is_present("input") {
|
||||
true => {
|
||||
let file = args.value_of("input").unwrap().to_owned();
|
||||
// validate input
|
||||
if !Path::new(&file).is_file() {
|
||||
let msg = format!("File {} not found.", &file);
|
||||
return Err(ParseError::ArgumentError(msg));
|
||||
}
|
||||
Some(file)
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
|
||||
let mut input_slatepack_message = None;
|
||||
if input_file.is_none() {
|
||||
input_slatepack_message = Some(prompt_slatepack()?);
|
||||
}
|
||||
|
||||
Ok(command::PostArgs {
|
||||
input: tx_file.to_owned(),
|
||||
fluff: fluff,
|
||||
input_file,
|
||||
input_slatepack_message,
|
||||
fluff,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -973,6 +1057,12 @@ where
|
|||
K: keychain::Keychain + 'static,
|
||||
{
|
||||
let km = (&keychain_mask).as_ref();
|
||||
|
||||
if test_mode {
|
||||
owner_api.doctest_mode = true;
|
||||
owner_api.doctest_retain_tld = true;
|
||||
}
|
||||
|
||||
match wallet_args.subcommand() {
|
||||
("init", Some(args)) => {
|
||||
let a = arg_parse!(parse_init_args(
|
||||
|
@ -982,7 +1072,7 @@ where
|
|||
&args,
|
||||
test_mode,
|
||||
));
|
||||
command::init(owner_api, &global_wallet_args, a)
|
||||
command::init(owner_api, &global_wallet_args, a, test_mode)
|
||||
}
|
||||
("recover", Some(_)) => {
|
||||
let a = arg_parse!(parse_recover_args(&global_wallet_args,));
|
||||
|
@ -1000,6 +1090,7 @@ where
|
|||
&a,
|
||||
&global_wallet_args.clone(),
|
||||
cli_mode,
|
||||
test_mode,
|
||||
)
|
||||
}
|
||||
("owner_api", Some(args)) => {
|
||||
|
@ -1007,7 +1098,7 @@ where
|
|||
let mut g = global_wallet_args.clone();
|
||||
g.tls_conf = None;
|
||||
arg_parse!(parse_owner_api_args(&mut c, &args));
|
||||
command::owner_api(owner_api, keychain_mask, &c, &tor_config, &g)
|
||||
command::owner_api(owner_api, keychain_mask, &c, &tor_config, &g, test_mode)
|
||||
}
|
||||
("web", Some(_)) => command::owner_api(
|
||||
owner_api,
|
||||
|
@ -1015,6 +1106,7 @@ where
|
|||
wallet_config,
|
||||
tor_config,
|
||||
global_wallet_args,
|
||||
test_mode,
|
||||
),
|
||||
("account", Some(args)) => {
|
||||
let a = arg_parse!(parse_account_args(&args));
|
||||
|
@ -1028,11 +1120,19 @@ where
|
|||
Some(tor_config.clone()),
|
||||
a,
|
||||
wallet_config.dark_background_color_scheme.unwrap_or(true),
|
||||
test_mode,
|
||||
)
|
||||
}
|
||||
("receive", Some(args)) => {
|
||||
let a = arg_parse!(parse_receive_args(&args));
|
||||
command::receive(owner_api, km, &global_wallet_args, a)
|
||||
command::receive(
|
||||
owner_api,
|
||||
km,
|
||||
&global_wallet_args,
|
||||
a,
|
||||
Some(tor_config.clone()),
|
||||
test_mode,
|
||||
)
|
||||
}
|
||||
("finalize", Some(args)) => {
|
||||
let a = arg_parse!(parse_finalize_args(&args));
|
||||
|
@ -1043,13 +1143,19 @@ where
|
|||
command::issue_invoice_tx(owner_api, km, a)
|
||||
}
|
||||
("pay", Some(args)) => {
|
||||
let a = arg_parse!(parse_process_invoice_args(&args, !test_mode));
|
||||
// get slate first
|
||||
let (slate, address) = get_slate(owner_api, km, args)?;
|
||||
|
||||
let a = arg_parse!(parse_process_invoice_args(
|
||||
&args, !test_mode, slate, address
|
||||
));
|
||||
command::process_invoice(
|
||||
owner_api,
|
||||
km,
|
||||
Some(tor_config.clone()),
|
||||
a,
|
||||
wallet_config.dark_background_color_scheme.unwrap_or(true),
|
||||
test_mode,
|
||||
)
|
||||
}
|
||||
("info", Some(args)) => {
|
||||
|
|
|
@ -48,7 +48,7 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
let app = App::from_yaml(yml);
|
||||
|
||||
// wallet init
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "init", "-h"];
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password1", "init", "-h"];
|
||||
// should create new wallet file
|
||||
let client1 = LocalWalletClient::new("wallet1", wallet_proxy.tx.clone());
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec.clone())?;
|
||||
|
@ -64,7 +64,7 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
let (wallet1, mask1_i) = instantiate_wallet(
|
||||
wallet_config1.clone(),
|
||||
client1.clone(),
|
||||
"password",
|
||||
"password1",
|
||||
"default",
|
||||
)?;
|
||||
wallet_proxy.add_wallet(
|
||||
|
@ -75,6 +75,7 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
);
|
||||
|
||||
// Create wallet 2
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password2", "init", "-h"];
|
||||
let client2 = LocalWalletClient::new("wallet2", wallet_proxy.tx.clone());
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec.clone())?;
|
||||
|
||||
|
@ -83,7 +84,7 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
let (wallet2, mask2_i) = instantiate_wallet(
|
||||
wallet_config2.clone(),
|
||||
client2.clone(),
|
||||
"password",
|
||||
"password2",
|
||||
"default",
|
||||
)?;
|
||||
wallet_proxy.add_wallet(
|
||||
|
@ -101,13 +102,13 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
});
|
||||
|
||||
// Create some accounts in wallet 1
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "account", "-c", "mining"];
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password1", "account", "-c", "mining"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"password1",
|
||||
"account",
|
||||
"-c",
|
||||
"account_1",
|
||||
|
@ -118,7 +119,7 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"password2",
|
||||
"account",
|
||||
"-c",
|
||||
"account_1",
|
||||
|
@ -130,7 +131,7 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"password2",
|
||||
"account",
|
||||
"-c",
|
||||
"account_2",
|
||||
|
@ -138,18 +139,18 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// let's see those accounts
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "account"];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password1", "account"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// let's see those accounts
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "account"];
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password2", "account"];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// Mine a bit into wallet 1 so we have something to send
|
||||
// (TODO: Be able to stop listeners so we can test this better)
|
||||
let wallet_config1 = config1.clone().members.unwrap().wallet;
|
||||
let (wallet1, mask1_i) =
|
||||
instantiate_wallet(wallet_config1, client1.clone(), "password", "default")?;
|
||||
instantiate_wallet(wallet_config1, client1.clone(), "password1", "default")?;
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
grin_wallet_controller::controller::owner_single_use(
|
||||
Some(wallet1.clone()),
|
||||
|
@ -166,31 +167,32 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
test_framework::award_blocks_to_wallet(&chain, wallet1.clone(), mask1, bh as usize, false);
|
||||
|
||||
// Update info and check
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "mining", "info"];
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password1", "-a", "mining", "info"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// try a file exchange
|
||||
let file_name = format!("{}/tx1.part_tx", test_dir);
|
||||
let response_file_name = format!("{}/tx1.part_tx.response", test_dir);
|
||||
let file_name = format!(
|
||||
"{}/wallet1/slatepack/0436430c-2b02-624c-2032-570501212b00.S1.slatepack",
|
||||
test_dir
|
||||
);
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"password1",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
let arg_vec = vec!["grin-wallet", "-a", "mining", "-p", "password1", "txs"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"password2",
|
||||
"-a",
|
||||
"account_1",
|
||||
"receive",
|
||||
|
@ -202,15 +204,20 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
// shouldn't be allowed to receive twice
|
||||
assert!(execute_command(&app, test_dir, "wallet2", &client2, arg_vec).is_err());
|
||||
|
||||
let file_name = format!(
|
||||
"{}/wallet2/slatepack/0436430c-2b02-624c-2032-570501212b00.S2.slatepack",
|
||||
test_dir
|
||||
);
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-a",
|
||||
"mining",
|
||||
"-p",
|
||||
"password",
|
||||
"password1",
|
||||
"finalize",
|
||||
"-i",
|
||||
&response_file_name,
|
||||
&file_name,
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
bh += 1;
|
||||
|
@ -219,7 +226,7 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
let (wallet1, mask1_i) = instantiate_wallet(
|
||||
wallet_config1.clone(),
|
||||
client1.clone(),
|
||||
"password",
|
||||
"password1",
|
||||
"default",
|
||||
)?;
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
@ -245,10 +252,10 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
bh += 10;
|
||||
|
||||
// update info for each
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "mining", "info"];
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password1", "-a", "mining", "info"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "account_1", "info"];
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password2", "-a", "account_1", "info"];
|
||||
execute_command(&app, test_dir, "wallet2", &client1, arg_vec)?;
|
||||
|
||||
// check results in wallet 2
|
||||
|
@ -256,7 +263,7 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
let (wallet2, mask2_i) = instantiate_wallet(
|
||||
wallet_config2.clone(),
|
||||
client2.clone(),
|
||||
"password",
|
||||
"password2",
|
||||
"default",
|
||||
)?;
|
||||
let mask2 = (&mask2_i).as_ref();
|
||||
|
@ -274,28 +281,113 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
},
|
||||
)?;
|
||||
|
||||
// Self-send to same account, using smallest strategy
|
||||
// Send encrypted from wallet 1 to wallet 2
|
||||
// output wallet 2's address for test creation purposes,
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"password2",
|
||||
"-a",
|
||||
"account_1",
|
||||
"address",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// Send encrypted to wallet 2
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password1",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"file",
|
||||
"-d",
|
||||
"slatepack1ak8aaxpjg6ct5uje4lgzvjp65l0nrmgxndp5xjy74sumzp7wasyspux2f5",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let file_name = format!(
|
||||
"{}/wallet1/slatepack/0436430c-2b02-624c-2032-570501212b01.S1.slatepack",
|
||||
test_dir
|
||||
);
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password2",
|
||||
"-a",
|
||||
"account_1",
|
||||
"receive",
|
||||
"-i",
|
||||
&file_name,
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec.clone())?;
|
||||
|
||||
let file_name = format!(
|
||||
"{}/wallet2/slatepack/0436430c-2b02-624c-2032-570501212b01.S2.slatepack",
|
||||
test_dir
|
||||
);
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-a",
|
||||
"mining",
|
||||
"-p",
|
||||
"password1",
|
||||
"finalize",
|
||||
"-i",
|
||||
&file_name,
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
bh += 1;
|
||||
|
||||
// Check our transaction log, should have bh entries
|
||||
let wallet_config1 = config1.clone().members.unwrap().wallet;
|
||||
let (wallet1, mask1_i) = instantiate_wallet(
|
||||
wallet_config1.clone(),
|
||||
client1.clone(),
|
||||
"password1",
|
||||
"default",
|
||||
)?;
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
grin_wallet_controller::controller::owner_single_use(
|
||||
Some(wallet1.clone()),
|
||||
mask1,
|
||||
None,
|
||||
|api, m| {
|
||||
api.set_active_account(m, "mining")?;
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(txs.len(), bh as usize);
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
// Send to self
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password1",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-o",
|
||||
"3",
|
||||
"-s",
|
||||
"smallest",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let file_name = format!(
|
||||
"{}/wallet1/slatepack/0436430c-2b02-624c-2032-570501212b02.S1.slatepack",
|
||||
test_dir
|
||||
);
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"password1",
|
||||
"-a",
|
||||
"mining",
|
||||
"receive",
|
||||
|
@ -304,25 +396,30 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec.clone())?;
|
||||
|
||||
let file_name = format!(
|
||||
"{}/wallet1/slatepack/0436430c-2b02-624c-2032-570501212b02.S2.slatepack",
|
||||
test_dir
|
||||
);
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-a",
|
||||
"mining",
|
||||
"-p",
|
||||
"password",
|
||||
"password1",
|
||||
"finalize",
|
||||
"-i",
|
||||
&response_file_name,
|
||||
&file_name,
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
bh += 1;
|
||||
|
||||
// Check our transaction log, should have bh entries + one for the self receive
|
||||
// Check our transaction log, should have bh entries + 1 for self-seld
|
||||
let wallet_config1 = config1.clone().members.unwrap().wallet;
|
||||
let (wallet1, mask1_i) = instantiate_wallet(
|
||||
wallet_config1.clone(),
|
||||
client1.clone(),
|
||||
"password",
|
||||
"password1",
|
||||
"default",
|
||||
)?;
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
@ -340,111 +437,55 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
},
|
||||
)?;
|
||||
|
||||
// Try using the self-send method, splitting up outputs for the fun of it
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"self",
|
||||
"-d",
|
||||
"mining",
|
||||
"-o",
|
||||
"3",
|
||||
"-s",
|
||||
"smallest",
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
bh += 1;
|
||||
|
||||
// Check our transaction log, should have bh entries + 2 for the self receives
|
||||
let wallet_config1 = config1.clone().members.unwrap().wallet;
|
||||
let (wallet1, mask1_i) = instantiate_wallet(
|
||||
wallet_config1.clone(),
|
||||
client1.clone(),
|
||||
"password",
|
||||
"default",
|
||||
)?;
|
||||
let mask1 = (&mask1_i).as_ref();
|
||||
|
||||
grin_wallet_controller::controller::owner_single_use(
|
||||
Some(wallet1.clone()),
|
||||
mask1,
|
||||
None,
|
||||
|api, m| {
|
||||
api.set_active_account(m, "mining")?;
|
||||
let (refreshed, txs) = api.retrieve_txs(m, true, None, None)?;
|
||||
assert!(refreshed);
|
||||
assert_eq!(txs.len(), bh as usize + 2);
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
||||
// Another file exchange, don't send, but unlock with repair command
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"password1",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "scan", "-d"];
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password1", "scan", "-d"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// Another file exchange, cancel this time
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"password1",
|
||||
"-a",
|
||||
"mining",
|
||||
"send",
|
||||
"-m",
|
||||
"file",
|
||||
"-d",
|
||||
&file_name,
|
||||
"10",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec!["grin-wallet", "-a", "mining", "-p", "password1", "txs"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"password1",
|
||||
"-a",
|
||||
"mining",
|
||||
"cancel",
|
||||
"-i",
|
||||
"26",
|
||||
"25",
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// issue an invoice tx, wallet 2
|
||||
let file_name = format!("{}/invoice.slate", test_dir);
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"invoice",
|
||||
"-d",
|
||||
&file_name,
|
||||
"-b",
|
||||
"65",
|
||||
];
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password2", "invoice", "65"];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
let output_file_name = format!("{}/invoice.slate.paid", test_dir);
|
||||
let file_name = format!(
|
||||
"{}/wallet2/slatepack/0436430c-2b02-624c-2032-570501212b05.I1.slatepack",
|
||||
test_dir
|
||||
);
|
||||
|
||||
// now pay the invoice tx, wallet 1
|
||||
let arg_vec = vec![
|
||||
|
@ -452,23 +493,26 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
"-a",
|
||||
"mining",
|
||||
"-p",
|
||||
"password",
|
||||
"password1",
|
||||
"pay",
|
||||
"-i",
|
||||
&file_name,
|
||||
"-d",
|
||||
&output_file_name,
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let file_name = format!(
|
||||
"{}/wallet1/slatepack/0436430c-2b02-624c-2032-570501212b05.I2.slatepack",
|
||||
test_dir
|
||||
);
|
||||
|
||||
// and finalize, wallet 2
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"password2",
|
||||
"finalize",
|
||||
"-i",
|
||||
&output_file_name,
|
||||
&file_name,
|
||||
];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
|
@ -477,14 +521,14 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
//bh += 5;
|
||||
|
||||
// txs and outputs (mostly spit out for a visual in test logs)
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "mining", "txs"];
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password1", "-a", "mining", "txs"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// message output (mostly spit out for a visual in test logs)
|
||||
let arg_vec = vec![
|
||||
"grin-wallet",
|
||||
"-p",
|
||||
"password",
|
||||
"password1",
|
||||
"-a",
|
||||
"mining",
|
||||
"txs",
|
||||
|
@ -494,13 +538,13 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
// txs and outputs (mostly spit out for a visual in test logs)
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "-a", "mining", "outputs"];
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password1", "-a", "mining", "outputs"];
|
||||
execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?;
|
||||
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "txs"];
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password2", "txs"];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "outputs"];
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password2", "outputs"];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// get tx output via -tx parameter
|
||||
|
@ -518,7 +562,7 @@ fn command_line_test_impl(test_dir: &str) -> Result<(), grin_wallet_controller::
|
|||
Ok(())
|
||||
},
|
||||
)?;
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password", "txs", "-t", &tx_id[..]];
|
||||
let arg_vec = vec!["grin-wallet", "-p", "password2", "txs", "-t", &tx_id[..]];
|
||||
execute_command(&app, test_dir, "wallet2", &client2, arg_vec)?;
|
||||
|
||||
// let logging finish
|
||||
|
|
|
@ -399,7 +399,6 @@ fn owner_v3_lifecycle() -> Result<(), grin_wallet_controller::Error> {
|
|||
assert!(res.is_ok());
|
||||
slate = res.unwrap();
|
||||
api.tx_lock_outputs(m, &slate)?;
|
||||
|
||||
Ok(())
|
||||
},
|
||||
)?;
|
||||
|
@ -409,9 +408,9 @@ fn owner_v3_lifecycle() -> Result<(), grin_wallet_controller::Error> {
|
|||
let req = serde_json::json!({
|
||||
"jsonrpc": "2.0",
|
||||
"id": 1,
|
||||
"method": "finalize_invoice_tx",
|
||||
"method": "finalize_tx",
|
||||
"params": {
|
||||
"slate": VersionedSlate::into_version(slate, SlateVersion::V3)?,
|
||||
"slate": VersionedSlate::into_version(slate, SlateVersion::V4)?,
|
||||
}
|
||||
});
|
||||
let res =
|
||||
|
|
|
@ -75,7 +75,7 @@ impl OnionV3Address {
|
|||
}
|
||||
|
||||
/// Return as onion v3 address string
|
||||
fn to_ov3_str(&self) -> String {
|
||||
pub fn to_ov3_str(&self) -> String {
|
||||
// calculate checksum
|
||||
let mut hasher = Sha3_256::new();
|
||||
hasher.input(b".onion checksum");
|
||||
|
@ -91,6 +91,11 @@ impl OnionV3Address {
|
|||
let ret = BASE32.encode(&address_bytes);
|
||||
ret.to_lowercase()
|
||||
}
|
||||
|
||||
/// return as http url
|
||||
pub fn to_http_str(&self) -> String {
|
||||
format!("http://{}.onion", self.to_ov3_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for OnionV3Address {
|
||||
|
|
Loading…
Reference in a new issue