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:
Yeastplume 2020-06-03 08:39:23 +01:00 committed by GitHub
parent 4fe6bf1c23
commit 60ab3728ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
28 changed files with 1425 additions and 738 deletions

175
Cargo.lock generated
View file

@ -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",
]

View file

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

View file

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

View file

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

View file

@ -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,
};
if sa.post_tx {
self.post_tx(keychain_mask, &slate, sa.fluff)?;
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.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 {

View file

@ -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,72 +118,51 @@ 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();
let g_args = g_args.clone();
let api_thread = thread::Builder::new()
.name("wallet-http-listener".to_string())
.spawn(move || {
let res = controller::foreign_listener(
wallet_inst,
keychain_mask,
&config.api_listen_addr(),
g_args.tls_conf.clone(),
tor_config.use_tor_listener,
);
if let Err(e) = res {
error!("Error starting listener: {}", e);
}
});
if let Ok(t) = api_thread {
if !cli_mode {
let r = t.join();
if let Err(_) = r {
error!("Error starting listener");
return Err(ErrorKind::ListenerError.into());
}
}
let wallet_inst = owner_api.wallet_inst.clone();
let config = config.clone();
let tor_config = tor_config.clone();
let g_args = g_args.clone();
let api_thread = thread::Builder::new()
.name("wallet-http-listener".to_string())
.spawn(move || {
let res = controller::foreign_listener(
wallet_inst,
keychain_mask,
&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);
}
});
if let Ok(t) = api_thread {
if !cli_mode {
let r = t.join();
if let Err(_) = r {
error!("Error starting listener");
return Err(ErrorKind::ListenerError.into());
}
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(())
}
@ -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
))
is_pre_fork = {
let cur_height = {
libwallet::wallet_lock!(wallet_inst, w);
w.w2n_client().get_chain_tip()?.0
};
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,
}
};
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);
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);
}
}
"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);
// 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
);
}
}
}
"file" => {
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
let cur_height = {
libwallet::wallet_lock!(wallet_inst, w);
w.w2n_client().get_chain_tip()?.0
};
// TODO: Floonet HF4
if cur_height < 786240 && !args.output_v4_slate {
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);
match result {
Ok(_) => {
info!("Tx sent ok",);
return Ok(());
}
Err(e) => {
error!("Tx sent fail: {}", e);
return Err(e);
}
}
}
Ok(())
})?;
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(_) => {
println!("Tx sent successfully",);
Ok(())
}
Err(e) => {
error!("Tx sent fail: {}", e);
Err(e.into())
}
}
})?;
}
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
);
Ok(())
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);
}
};
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(())
})?;
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(());
})?;

View file

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

View file

@ -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(())
})?;

View file

@ -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(())
})?;

View file

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

View file

@ -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(())
})?;

View file

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

View file

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

View 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"

View file

@ -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);
}
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,16 +203,27 @@ impl SlateSender for HttpSlateSender {
}
};
// Note: not using easy-jsonrpc as don't want the dependencies in this crate
let req = json!({
"jsonrpc": "2.0",
"method": "receive_tx",
"id": 1,
"params": [
slate_send,
null,
null
]
});
let req = match finalize {
false => json!({
"jsonrpc": "2.0",
"method": "receive_tx",
"id": 1,
"params": [
slate_send,
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| {

View file

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

View file

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

View file

@ -76,11 +76,23 @@ 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();
for _ in 0..seed_length {
seed.push(rng.gen());
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);

View file

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

View file

@ -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,24 +146,32 @@ 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())?;
if sl.is_compact() {
let mut temp_ctx = context.clone();
temp_ctx.sec_key = context.initial_sec_key.clone();
temp_ctx.sec_nonce = context.initial_sec_nonce.clone();
selection::repopulate_tx(&mut *w, keychain_mask, &mut sl, &temp_ctx, false)?;
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();
temp_ctx.sec_nonce = context.initial_sec_nonce.clone();
selection::repopulate_tx(&mut *w, keychain_mask, &mut sl, &temp_ctx, false)?;
}
tx::complete_tx(&mut *w, keychain_mask, &mut sl, &context)?;
tx::update_stored_tx(&mut *w, keychain_mask, &context, &mut sl, true)?;
{
let mut batch = w.batch(keychain_mask)?;
batch.delete_private_context(sl.id.as_bytes())?;
batch.commit()?;
}
sl.state = SlateState::Invoice3;
if sl.is_compact() {
sl.amount = 0;
}
} else {
sl = owner_finalize(w, keychain_mask, slate)?;
}
tx::complete_tx(&mut *w, keychain_mask, &mut sl, &context)?;
tx::update_stored_tx(&mut *w, keychain_mask, &context, &mut sl, true)?;
{
let mut batch = w.batch(keychain_mask)?;
batch.delete_private_context(sl.id.as_bytes())?;
batch.commit()?;
}
sl.state = SlateState::Invoice3;
if sl.is_compact() {
sl.amount = 0;
if post_automatically {
post_tx(w.w2n_client(), sl.tx_or_err()?, true)?;
}
Ok(sl)
}

View file

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

View file

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

View file

@ -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,11 +181,7 @@ subcommands:
short: d
long: dest
takes_value: true
- bin:
help: Whether to output file as binary
short: b
long: bin
#TODO: Remove HF3
#TODO: Remove HF3
- v4:
help: Output a V4 slate prior to HF3 block
long: v4
@ -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

View file

@ -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<'_>,

View file

@ -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") {
Some(d) => d,
None => "default",
}
} else {
if !estimate_selection_strategies {
parse_required(args, "dest")?
} else {
""
}
}
let dest = match args.value_of("dest") {
Some(d) => d,
None => "default",
};
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) {
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()),
Err(e) => {
let msg = format!("Invalid proof address: {:?}", e);
return Err(ParseError::ArgumentError(msg));
}
true => match OnionV3Address::try_from(dest) {
Ok(a) => Some(SlatepackAddress::try_from(a).unwrap()),
Err(_) => {
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 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(&file).is_file() {
let msg = format!("File {} not found.", &file);
return Err(ParseError::ArgumentError(msg));
}
Some(file)
}
false => None,
};
// validate input
if !Path::new(&tx_file).is_file() {
let msg = format!("File {} not found.", &tx_file);
return Err(ParseError::ArgumentError(msg));
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);
return Err(ParseError::ArgumentError(msg));
}
let dest_file = match args.is_present("dest") {
true => Some(args.value_of("dest").unwrap().to_owned()),
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::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)) => {

View file

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

View file

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

View file

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