From 4bb0398e21fb6a63174c7804d5b3a1cde05a99a9 Mon Sep 17 00:00:00 2001 From: Yeastplume Date: Fri, 14 Feb 2020 09:16:43 +0000 Subject: [PATCH] Convert node client to use V2 Node API (#329) * add beginnings of jsonrpc node client, get_chain_tip implementation * version info and outputs from node * return async split up of get_output requests * add and test get kernel functionality * height range to pmmr indices * test fixes * outputs by pmmr index --- Cargo.lock | 178 ++++++++++++ impls/Cargo.toml | 2 + impls/src/adapters/http.rs | 2 +- impls/src/client_utils/client.rs | 8 +- impls/src/client_utils/json_rpc.rs | 276 ++++++++++++++++++ impls/src/client_utils/mod.rs | 1 + impls/src/lib.rs | 2 + impls/src/node_clients/http.rs | 372 +++++++++++-------------- impls/src/node_clients/mod.rs | 1 + impls/src/node_clients/resp_types.rs | 30 ++ impls/src/test_framework/testclient.rs | 20 +- libwallet/src/api_impl/owner.rs | 7 +- libwallet/src/types.rs | 2 +- 13 files changed, 671 insertions(+), 230 deletions(-) create mode 100644 impls/src/client_utils/json_rpc.rs create mode 100644 impls/src/node_clients/resp_types.rs diff --git a/Cargo.lock b/Cargo.lock index 08a222a8..56fb5304 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -615,6 +615,15 @@ dependencies = [ "termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "error-chain" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "backtrace 0.3.43 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "failure" version = "0.1.6" @@ -1054,6 +1063,8 @@ dependencies = [ "hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-rustls 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-http 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)", "regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1181,6 +1192,32 @@ dependencies = [ "quick-error 1.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hyper" +version = "0.11.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "bytes 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "futures-cpupool 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "hyper" version = "0.12.19" @@ -1288,6 +1325,44 @@ dependencies = [ "libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "jsonrpc-client-core" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-core 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "jsonrpc-client-http" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)", + "jsonrpc-client-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "jsonrpc-core" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.46 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "jsonrpc-core" version = "10.1.0" @@ -1314,6 +1389,11 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "lazy_static" version = "1.4.0" @@ -1407,6 +1487,14 @@ dependencies = [ "scopeguard 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "log" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "log" version = "0.4.8" @@ -1493,6 +1581,11 @@ dependencies = [ "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "miniz_oxide" version = "0.3.6" @@ -2195,6 +2288,14 @@ name = "regex-syntax" version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "relay" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "remove_dir_all" version = "0.5.2" @@ -2448,6 +2549,11 @@ name = "siphasher" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "slab" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "slab" version = "0.4.2" @@ -2461,6 +2567,11 @@ dependencies = [ "smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "smallvec" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "smallvec" version = "0.6.13" @@ -2572,6 +2683,11 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "take" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "tempfile" version = "3.1.0" @@ -2756,6 +2872,23 @@ dependencies = [ "tokio-timer 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tokio-proto" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.23 (registry+https://github.com/rust-lang/crates.io-index)", + "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-core 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", + "tokio-service 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tokio-reactor" version = "0.1.11" @@ -2904,6 +3037,11 @@ name = "traitobject" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "try-lock" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "try-lock" version = "0.2.2" @@ -2922,6 +3060,14 @@ name = "typenum" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicase" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-bidi" version = "0.3.4" @@ -3020,6 +3166,11 @@ name = "version_check" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "version_check" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "void" version = "1.0.2" @@ -3035,6 +3186,16 @@ dependencies = [ "winapi-util 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "want" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "futures 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "want" version = "0.0.6" @@ -3243,6 +3404,7 @@ dependencies = [ "checksum encode_unicode 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" "checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" "checksum env_logger 0.5.13 (registry+https://github.com/rust-lang/crates.io-index)" = "15b0a4d2e39f8420210be8b27eeda28029729e2fd4291019455016c348240c38" +"checksum error-chain 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3ab49e9dcb602294bc42f9a7dfc9bc6e936fca4418ea300dbfb84fe16de0b7d9" "checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" "checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" @@ -3275,6 +3437,7 @@ dependencies = [ "checksum http 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "d6ccf5ede3a895d8856620237b2f02972c1bbc78d2965ad7fe8838d4a0ed41f0" "checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" "checksum humantime 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +"checksum hyper 0.11.27 (registry+https://github.com/rust-lang/crates.io-index)" = "34a590ca09d341e94cddf8e5af0bbccde205d5fbc2fa3c09dd67c7f85cea59d7" "checksum hyper 0.12.19 (registry+https://github.com/rust-lang/crates.io-index)" = "f1ebec079129e43af5e234ef36ee3d7e6085687d145b7ea653b262d16c6b65f1" "checksum hyper-rustls 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "68f2aa6b1681795bf4da8063f718cd23145aa0c9a5143d9787b345aa60d38ee4" "checksum hyper-timeout 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "16ec7c8e526ed674ce148323e1f2010f76f546fcdca99a2c721e04bc7bf5b6f7" @@ -3284,9 +3447,13 @@ dependencies = [ "checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" "checksum itoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" "checksum jobserver 0.1.21 (registry+https://github.com/rust-lang/crates.io-index)" = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" +"checksum jsonrpc-client-core 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f29cb249837420fb0cee7fb0fbf1d22679e121b160e71bb5e0d90b9df241c23e" +"checksum jsonrpc-client-http 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2e642eb74423b9dfcb4512fda167148746b76f788a823cd712fadf409f31d302" "checksum jsonrpc-core 10.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "dc15eef5f8b6bef5ac5f7440a957ff95d036e2f98706947741bfc93d1976db4c" +"checksum jsonrpc-core 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ddf83704f4e79979a424d1082dd2c1e52683058056c9280efa19ac5f6bc9033c" "checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" "checksum libc 0.2.66 (registry+https://github.com/rust-lang/crates.io-index)" = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" "checksum libgit2-sys 0.9.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4870c781f6063efb83150cd22c1ddf6ecf58531419e7570cdcced46970f64a16" @@ -3298,6 +3465,7 @@ dependencies = [ "checksum lmdb-zero 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "13416eee745b087c22934f35f1f24da22da41ba2a5ce197143d168ce055cc58d" "checksum lock_api 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "62ebf1391f6acad60e5c8b43706dde4582df75c06698ab44511d15016bc2442c" "checksum lock_api 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b" +"checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)" = "14b6052be84e6b71ab17edffc2eeabf5c2c3ae1fdb464aae35ac50c67a44e1f7" "checksum log-mdc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7" "checksum log4rs 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)" = "100052474df98158c0738a7d3f4249c99978490178b5f9f68cd835ac57adbd1b" @@ -3308,6 +3476,7 @@ dependencies = [ "checksum memchr 2.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3197e20c7edb283f87c071ddfc7a2cca8f8e0b888c242959846a6fce03c72223" "checksum memmap 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" "checksum memoffset 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" +"checksum mime 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" "checksum miniz_oxide 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "aa679ff6578b1cddee93d7e82e263b94a575e0bfced07284eb0c037c1d2416a5" "checksum mio 0.6.21 (registry+https://github.com/rust-lang/crates.io-index)" = "302dec22bcf6bae6dfb69c647187f4b4d0fb6f535521f7bc022430ce8e12008f" "checksum mio-uds 0.6.7 (registry+https://github.com/rust-lang/crates.io-index)" = "966257a94e196b11bb43aca423754d87429960a768de9414f3691d6957abf125" @@ -3384,6 +3553,7 @@ dependencies = [ "checksum redox_users 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "09b23093265f8d200fa7b4c2c76297f47e681c655f6f1285a8780d6a022f7431" "checksum regex 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "322cf97724bea3ee221b78fe25ac9c46114ebb51747ad5babd51a2fc6a8235a8" "checksum regex-syntax 0.6.14 (registry+https://github.com/rust-lang/crates.io-index)" = "b28dfe3fe9badec5dbf0a79a9cccad2cfc2ab5484bdb3e44cbd1ae8b3ba2be06" +"checksum relay 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1576e382688d7e9deecea24417e350d3062d97e32e45d70b1cde65994ff1489a" "checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e" "checksum ring 0.13.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2c4db68a2e35f3497146b7e4563df7d4773a2433230c5e4b448328e31740458a" "checksum ripemd160 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "482aa56cc68aaeccdaaff1cc5a72c247da8bbad3beb174ca5741f274c22883fb" @@ -3414,8 +3584,10 @@ dependencies = [ "checksum sha2 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "27044adfd2e1f077f649f59deb9490d3941d674002f7d062870a60ebe9bd47a0" "checksum sha3 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" "checksum siphasher 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac" +"checksum slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "17b4fcaed89ab08ef143da37bc52adbcc04d4a69014f4c1208d6b51f0c47bc23" "checksum slab 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" "checksum smallstr 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6aa65bb4d5b2bbc90d36af64e29802f788aa614783fa1d0df011800ddcec6e8e" +"checksum smallvec 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4c8cbcd6df1e117c2210e13ab5109635ad68a929fcbb8964dc965b76cb5ee013" "checksum smallvec 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "f7b0758c52e15a8b5e3691eae6cc559f08eee9406e548a4477ba4e67770a82b6" "checksum smallvec 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5c2fb2ec9bcd216a5b0d0ccf31ab17b5ed1d627960edff65bbe95d3ce221cefc" "checksum stable_deref_trait 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dba1a27d3efae4351c8051072d619e3ade2820635c3958d826bfea39d59b54c8" @@ -3430,6 +3602,7 @@ dependencies = [ "checksum synstructure 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "02353edf96d6e4dc81aea2d8490a7e9db177bf8acb0e951c24940bf866cb313f" "checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" "checksum sysinfo 0.9.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6f4b2468c629cffba39c0a4425849ab3cdb03d9dfacba69684609aea04d08ff9" +"checksum take 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b157868d8ac1f56b64604539990685fa7611d8fa9e5476cf0c02cf34d32917c5" "checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9" "checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" "checksum termcolor 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" @@ -3447,6 +3620,7 @@ dependencies = [ "checksum tokio-fs 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "3fe6dc22b08d6993916647d108a1a7d15b9cd29c4f4496c62b92c45b5041b7af" "checksum tokio-io 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5090db468dad16e1a7a54c8c67280c5e4b544f3d3e018f0b913b400261f85926" "checksum tokio-io-timeout 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "135ce81f15cfd7982fac684f9057a1299eebeb79e98a8a709969b9aa51123129" +"checksum tokio-proto 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8fbb47ae81353c63c487030659494b295f6cb6576242f907f203473b191b0389" "checksum tokio-reactor 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "6732fe6b53c8d11178dcb77ac6d9682af27fc6d4cb87789449152e5377377146" "checksum tokio-retry 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f05746ae87dca83a2016b4f5dba5b237b897dd12fd324f60afe282112f16969a" "checksum tokio-rustls 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "208d62fa3e015426e3c64039d9d20adf054a3c9b4d9445560f1c41c75bef3eab" @@ -3460,9 +3634,11 @@ dependencies = [ "checksum toml 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "758664fc71a3a69038656bee8b6be6477d2a6c315a6b81f7081f591bffa4111f" "checksum toml 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ffc92d160b1eef40665be3a05630d003936a3bc7da7421277846c2613e92c71a" "checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" +"checksum try-lock 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee2aa4715743892880f70885373966c83d73ef1b0838a664ef0c76fffd35e7c2" "checksum try-lock 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e604eb7b43c06650e854be16a2a03155743d3752dd1c943f6829e26b7a36e382" "checksum typemap 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "653be63c80a3296da5551e1bfd2cca35227e13cdd08c6668903ae2f4f77aa1f6" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum unicase 2.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "50f37be617794602aabbeee0be4f259dc1778fabe05e2d67ee8f79326d5cb4f6" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "5479532badd04e128284890390c1e876ef7a993d0570b3597ae43dfa1d59afa4" "checksum unicode-segmentation 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" @@ -3478,8 +3654,10 @@ dependencies = [ "checksum vcpkg 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3fc439f2794e98976c88a2a2dafce96b930fe8010b0a256b3c2199a773933168" "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" "checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" +"checksum version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" "checksum walkdir 2.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "777182bc735b6424e1a57516d35ed72cb8019d85c8c9bf536dccb3445c1a2f7d" +"checksum want 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a05d9d966753fa4b5c8db73fcab5eed4549cfe0e1e4e66911e5564a0085c35d1" "checksum want 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "797464475f30ddb8830cc529aaaae648d581f99e2036a928877dfde027ddf6b3" "checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" "checksum webpki 0.18.1 (registry+https://github.com/rust-lang/crates.io-index)" = "17d7967316d8411ca3b01821ee6c332bde138ba4363becdb492f12e514daa17f" diff --git a/impls/Cargo.toml b/impls/Cargo.toml index f78c8088..dd1155d9 100644 --- a/impls/Cargo.toml +++ b/impls/Cargo.toml @@ -26,6 +26,8 @@ tokio-core = "0.1" tokio-retry = "0.1" uuid = { version = "0.7", features = ["serde", "v4"] } chrono = { version = "0.4.4", features = ["serde"] } +jsonrpc-client-core = "0.5.0" +jsonrpc-client-http = "0.5.0" #http client (copied from grin) http = "0.1.5" diff --git a/impls/src/adapters/http.rs b/impls/src/adapters/http.rs index ab358423..9393ae39 100644 --- a/impls/src/adapters/http.rs +++ b/impls/src/adapters/http.rs @@ -79,7 +79,7 @@ impl HttpSlateSender { if err_string.contains("404") { // Report that the other version of the wallet is out of date report = "Other wallet is incompatible and requires an upgrade. \ - Please urge the other wallet owner to upgrade and try the transaction again." + Please urge the other wallet owner to upgrade and try the transaction again." .to_string(); } error!("{}", report); diff --git a/impls/src/client_utils/client.rs b/impls/src/client_utils/client.rs index fa0aec88..4cf915a4 100644 --- a/impls/src/client_utils/client.rs +++ b/impls/src/client_utils/client.rs @@ -120,7 +120,7 @@ impl Client { /// Helper function to easily issue an async HTTP GET request against a given /// URL that returns a future. Handles request building, JSON deserialization /// and response code checking. - pub fn get_async<'a, T>( + pub fn _get_async<'a, T>( &self, url: &'a str, api_secret: Option, @@ -147,7 +147,7 @@ impl Client { /// object as body on a given URL that returns a JSON object. Handles request /// building, JSON serialization and deserialization, and response code /// checking. - pub fn _post( + pub fn post( &self, url: &str, api_secret: Option, @@ -165,7 +165,7 @@ impl Client { /// provided JSON object as body on a given URL that returns a future. Handles /// request building, JSON serialization and deserialization, and response code /// checking. - pub fn _post_async( + pub fn post_async( &self, url: &str, input: &IN, @@ -186,7 +186,7 @@ impl Client { /// object as body on a given URL that returns nothing. Handles request /// building, JSON serialization, and response code /// checking. - pub fn post_no_ret( + pub fn _post_no_ret( &self, url: &str, api_secret: Option, diff --git a/impls/src/client_utils/json_rpc.rs b/impls/src/client_utils/json_rpc.rs new file mode 100644 index 00000000..3d9ef9ab --- /dev/null +++ b/impls/src/client_utils/json_rpc.rs @@ -0,0 +1,276 @@ +// Copyright 2020 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// Derived from https://github.com/apoelstra/rust-jsonrpc + +//! JSON RPC Client functionality +use std::{error, fmt}; + +use hyper; +use serde_json; + +/// Builds a request +pub fn build_request<'a, 'b>(name: &'a str, params: &'b serde_json::Value) -> Request<'a, 'b> { + Request { + method: name, + params: params, + id: From::from(1), + jsonrpc: Some("2.0"), + } +} + +#[derive(Debug, Clone, PartialEq, Serialize)] +/// A JSONRPC request object +pub struct Request<'a, 'b> { + /// The name of the RPC call + pub method: &'a str, + /// Parameters to the RPC call + pub params: &'b serde_json::Value, + /// Identifier for this Request, which should appear in the response + pub id: serde_json::Value, + /// jsonrpc field, MUST be "2.0" + pub jsonrpc: Option<&'a str>, +} + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] +/// A JSONRPC response object +pub struct Response { + /// A result if there is one, or null + pub result: Option, + /// An error if there is one, or null + pub error: Option, + /// Identifier for this Request, which should match that of the request + pub id: serde_json::Value, + /// jsonrpc field, MUST be "2.0" + pub jsonrpc: Option, +} + +impl Response { + /// Extract the result from a response + pub fn result(&self) -> Result { + if let Some(ref e) = self.error { + return Err(Error::Rpc(e.clone())); + } + + let result = match self.result.clone() { + Some(r) => serde_json::from_value(r["Ok"].clone()).map_err(Error::Json), + None => serde_json::from_value(serde_json::Value::Null).map_err(Error::Json), + }?; + Ok(result) + } + + /// Extract the result from a response, consuming the response + pub fn into_result(self) -> Result { + if let Some(e) = self.error { + return Err(Error::Rpc(e)); + } + self.result() + } + + /// Return the RPC error, if there was one, but do not check the result + pub fn _check_error(self) -> Result<(), Error> { + if let Some(e) = self.error { + Err(Error::Rpc(e)) + } else { + Ok(()) + } + } + + /// Returns whether or not the `result` field is empty + pub fn _is_none(&self) -> bool { + self.result.is_none() + } +} + +/// A library error +#[derive(Debug)] +pub enum Error { + /// Json error + Json(serde_json::Error), + /// Client error + Hyper(hyper::error::Error), + /// Error response + Rpc(RpcError), + /// Response to a request did not have the expected nonce + _NonceMismatch, + /// Response to a request had a jsonrpc field other than "2.0" + _VersionMismatch, + /// Batches can't be empty + _EmptyBatch, + /// Too many responses returned in batch + _WrongBatchResponseSize, + /// Batch response contained a duplicate ID + _BatchDuplicateResponseId(serde_json::Value), + /// Batch response contained an ID that didn't correspond to any request ID + _WrongBatchResponseId(serde_json::Value), +} + +impl From for Error { + fn from(e: serde_json::Error) -> Error { + Error::Json(e) + } +} + +impl From for Error { + fn from(e: hyper::error::Error) -> Error { + Error::Hyper(e) + } +} + +impl From for Error { + fn from(e: RpcError) -> Error { + Error::Rpc(e) + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::Json(ref e) => write!(f, "JSON decode error: {}", e), + Error::Hyper(ref e) => write!(f, "Hyper error: {}", e), + Error::Rpc(ref r) => write!(f, "RPC error response: {:?}", r), + Error::_BatchDuplicateResponseId(ref v) => { + write!(f, "duplicate RPC batch response ID: {}", v) + } + Error::_WrongBatchResponseId(ref v) => write!(f, "wrong RPC batch response ID: {}", v), + _ => f.write_str(std::error::Error::description(self)), + } + } +} + +impl std::error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::Json(_) => "JSON decode error", + Error::Hyper(_) => "Hyper error", + Error::Rpc(_) => "RPC error response", + Error::_NonceMismatch => "Nonce of response did not match nonce of request", + Error::_VersionMismatch => "`jsonrpc` field set to non-\"2.0\"", + Error::_EmptyBatch => "batches can't be empty", + Error::_WrongBatchResponseSize => "too many responses returned in batch", + Error::_BatchDuplicateResponseId(_) => "batch response contained a duplicate ID", + Error::_WrongBatchResponseId(_) => { + "batch response contained an ID that didn't correspond to any request ID" + } + } + } + + fn cause(&self) -> Option<&dyn error::Error> { + match *self { + Error::Json(ref e) => Some(e), + Error::Hyper(ref e) => Some(e), + _ => None, + } + } +} + +/// Standard error responses, as described at at +/// http://www.jsonrpc.org/specification#error_object +/// +/// # Documentation Copyright +/// Copyright (C) 2007-2010 by the JSON-RPC Working Group +/// +/// This document and translations of it may be used to implement JSON-RPC, it +/// may be copied and furnished to others, and derivative works that comment +/// on or otherwise explain it or assist in its implementation may be prepared, +/// copied, published and distributed, in whole or in part, without restriction +/// of any kind, provided that the above copyright notice and this paragraph +/// are included on all such copies and derivative works. However, this document +/// itself may not be modified in any way. +/// +/// The limited permissions granted above are perpetual and will not be revoked. +/// +/// This document and the information contained herein is provided "AS IS" and +/// ALL WARRANTIES, EXPRESS OR IMPLIED are DISCLAIMED, INCLUDING BUT NOT LIMITED +/// TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY +/// RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A +/// PARTICULAR PURPOSE. +/// +#[allow(dead_code)] +#[derive(Debug)] +pub enum StandardError { + /// Invalid JSON was received by the server. + /// An error occurred on the server while parsing the JSON text. + ParseError, + /// The JSON sent is not a valid Request object. + InvalidRequest, + /// The method does not exist / is not available. + MethodNotFound, + /// Invalid method parameter(s). + InvalidParams, + /// Internal JSON-RPC error. + InternalError, +} + +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] +/// A JSONRPC error object +pub struct RpcError { + /// The integer identifier of the error + pub code: i32, + /// A string describing the error + pub message: String, + /// Additional data specific to the error + pub data: Option, +} + +/// Create a standard error responses +pub fn _standard_error(code: StandardError, data: Option) -> RpcError { + match code { + StandardError::ParseError => RpcError { + code: -32700, + message: "Parse error".to_string(), + data: data, + }, + StandardError::InvalidRequest => RpcError { + code: -32600, + message: "Invalid Request".to_string(), + data: data, + }, + StandardError::MethodNotFound => RpcError { + code: -32601, + message: "Method not found".to_string(), + data: data, + }, + StandardError::InvalidParams => RpcError { + code: -32602, + message: "Invalid params".to_string(), + data: data, + }, + StandardError::InternalError => RpcError { + code: -32603, + message: "Internal error".to_string(), + data: data, + }, + } +} + +/// Converts a Rust `Result` to a JSONRPC response object +pub fn _result_to_response( + result: Result, + id: serde_json::Value, +) -> Response { + match result { + Ok(data) => Response { + result: Some(data), + error: None, + id: id, + jsonrpc: Some(String::from("2.0")), + }, + Err(err) => Response { + result: None, + error: Some(err), + id: id, + jsonrpc: Some(String::from("2.0")), + }, + } +} diff --git a/impls/src/client_utils/mod.rs b/impls/src/client_utils/mod.rs index 9966e837..6f73dcbe 100644 --- a/impls/src/client_utils/mod.rs +++ b/impls/src/client_utils/mod.rs @@ -13,6 +13,7 @@ // limitations under the License. mod client; +pub mod json_rpc; mod socksv5; pub use self::socksv5::Socksv5Connector; diff --git a/impls/src/lib.rs b/impls/src/lib.rs index f576bb3e..bee04c5a 100644 --- a/impls/src/lib.rs +++ b/impls/src/lib.rs @@ -22,6 +22,8 @@ use blake2_rfc as blake2; extern crate serde_derive; #[macro_use] extern crate log; +#[macro_use] +extern crate serde_json; use grin_wallet_libwallet as libwallet; use grin_wallet_util::grin_api as api; use grin_wallet_util::grin_chain as chain; diff --git a/impls/src/node_clients/http.rs b/impls/src/node_clients/http.rs index 5a14ca85..8bfcfde1 100644 --- a/impls/src/node_clients/http.rs +++ b/impls/src/node_clients/http.rs @@ -13,14 +13,11 @@ // limitations under the License. //! Client functions, implementations of the NodeClient trait -//! specific to the FileWallet +use crate::api::{self, LocatedTxKernel, OutputListing, OutputPrintable}; +use crate::core::core::{Transaction, TxKernel}; +use crate::libwallet::{NodeClient, NodeVersionInfo}; use futures::{stream, Stream}; - -use crate::api::{self, LocatedTxKernel}; -use crate::core::core::TxKernel; -use crate::libwallet::{NodeClient, NodeVersionInfo, TxWrapper}; -use semver::Version; use std::collections::HashMap; use std::env; use tokio::runtime::Runtime; @@ -30,6 +27,11 @@ use crate::libwallet; use crate::util::secp::pedersen; use crate::util::{self, to_hex}; +use super::resp_types::*; +use crate::client_utils::json_rpc::*; + +const ENDPOINT: &str = "/v2/foreign"; + #[derive(Clone)] pub struct HTTPNodeClient { node_url: String, @@ -51,6 +53,34 @@ impl HTTPNodeClient { pub fn chain_height(&self) -> Result<(u64, String), libwallet::Error> { self.get_chain_tip() } + + fn send_json_request( + &self, + method: &str, + params: &serde_json::Value, + ) -> Result { + let url = format!("{}{}", self.node_url(), ENDPOINT); + let client = Client::new(); + let req = build_request(method, params); + let res = client.post::(url.as_str(), self.node_api_secret(), &req); + + match res { + Err(e) => { + let report = format!("Error calling {}: {}", method, e); + error!("{}", report); + Err(libwallet::ErrorKind::ClientCallback(report).into()) + } + Ok(inner) => match inner.clone().into_result() { + Ok(r) => Ok(r), + Err(e) => { + error!("{:?}", inner); + let report = format!("Unable to parse response for {}: {}", method, e); + error!("{}", report); + Err(libwallet::ErrorKind::ClientCallback(report).into()) + } + }, + } + } } impl NodeClient for HTTPNodeClient { @@ -73,10 +103,14 @@ impl NodeClient for HTTPNodeClient { if let Some(v) = self.node_version_info.as_ref() { return Some(v.clone()); } - let url = format!("{}/v1/version", self.node_url()); - let client = Client::new(); - let mut retval = match client.get::(url.as_str(), self.node_api_secret()) { - Ok(n) => n, + let retval = match self + .send_json_request::("get_version", &serde_json::Value::Null) + { + Ok(n) => NodeVersionInfo { + node_version: n.node_version, + block_header_version: n.block_header_version, + verified: Some(true), + }, Err(e) => { // If node isn't available, allow offline functions // unfortunately have to parse string due to error structure @@ -93,44 +127,21 @@ impl NodeClient for HTTPNodeClient { } } }; - retval.verified = Some(true); self.node_version_info = Some(retval.clone()); Some(retval) } /// Posts a transaction to a grin node - fn post_tx(&self, tx: &TxWrapper, fluff: bool) -> Result<(), libwallet::Error> { - let url; - let dest = self.node_url(); - if fluff { - url = format!("{}/v1/pool/push_tx?fluff", dest); - } else { - url = format!("{}/v1/pool/push_tx", dest); - } - let client = Client::new(); - let res = client.post_no_ret(url.as_str(), self.node_api_secret(), tx); - if let Err(e) = res { - let report = format!("Posting transaction to node: {}", e); - error!("Post TX Error: {}", e); - return Err(libwallet::ErrorKind::ClientCallback(report).into()); - } + fn post_tx(&self, tx: &Transaction, fluff: bool) -> Result<(), libwallet::Error> { + let params = json!([tx, fluff]); + self.send_json_request::("push_transaction", ¶ms)?; Ok(()) } /// Return the chain tip from a given node fn get_chain_tip(&self) -> Result<(u64, String), libwallet::Error> { - let addr = self.node_url(); - let url = format!("{}/v1/chain", addr); - let client = Client::new(); - let res = client.get::(url.as_str(), self.node_api_secret()); - match res { - Err(e) => { - let report = format!("Getting chain height from node: {}", e); - error!("Get chain height error: {}", e); - Err(libwallet::ErrorKind::ClientCallback(report).into()) - } - Ok(r) => Ok((r.height, r.last_block_pushed)), - } + let result = self.send_json_request::("get_tip", &serde_json::Value::Null)?; + Ok((result.height, result.last_block_pushed)) } /// Get kernel implementation @@ -140,44 +151,34 @@ impl NodeClient for HTTPNodeClient { min_height: Option, max_height: Option, ) -> Result, libwallet::Error> { - let version = self - .get_version_info() - .ok_or_else(|| libwallet::ErrorKind::ClientCallback("Unable to get version".into()))?; - let version = Version::parse(&version.node_version) - .map_err(|_| libwallet::ErrorKind::ClientCallback("Unable to parse version".into()))?; - if version <= Version::new(2, 0, 0) { - return Err(libwallet::ErrorKind::ClientCallback( - "Kernel lookup not supported by node, please upgrade it".into(), - ) - .into()); - } - - let mut query = String::new(); - if let Some(h) = min_height { - query += &format!("min_height={}", h); - } - if let Some(h) = max_height { - if !query.is_empty() { - query += "&"; - } - query += &format!("max_height={}", h); - } - if !query.is_empty() { - query.insert_str(0, "?"); - } - - let url = format!( - "{}/v1/chain/kernels/{}{}", - self.node_url(), - to_hex(excess.0.to_vec()), - query - ); + let method = "get_kernel"; + let params = json!([to_hex(excess.0.to_vec()), min_height, max_height]); + // have to handle this manually since the error needs to be parsed + let url = format!("{}{}", self.node_url(), ENDPOINT); let client = Client::new(); - let res: Option = client - .get(url.as_str(), self.node_api_secret()) - .map_err(|e| libwallet::ErrorKind::ClientCallback(format!("Kernel lookup: {}", e)))?; + let req = build_request(method, ¶ms); + let res = client.post::(url.as_str(), self.node_api_secret(), &req); - Ok(res.map(|k| (k.tx_kernel, k.height, k.mmr_index))) + match res { + Err(e) => { + let report = format!("Error calling {}: {}", method, e); + error!("{}", report); + Err(libwallet::ErrorKind::ClientCallback(report).into()) + } + Ok(inner) => match inner.clone().into_result::() { + Ok(r) => Ok(Some((r.tx_kernel, r.height, r.mmr_index))), + Err(e) => { + let contents = format!("{:?}", inner); + if contents.contains("NotFound") { + Ok(None) + } else { + let report = format!("Unable to parse response for {}: {}", method, e); + error!("{}", report); + Err(libwallet::ErrorKind::ClientCallback(report).into()) + } + } + }, + } } /// Retrieve outputs from node @@ -185,24 +186,25 @@ impl NodeClient for HTTPNodeClient { &self, wallet_outputs: Vec, ) -> Result, libwallet::Error> { - let addr = self.node_url(); - // build the necessary query params - - // ?id=xxx&id=yyy&id=zzz - let query_params: Vec = wallet_outputs - .iter() - .map(|commit| format!("id={}", util::to_hex(commit.as_ref().to_vec()))) - .collect(); - // build a map of api outputs by commit so we can look them up efficiently let mut api_outputs: HashMap = HashMap::new(); + + if wallet_outputs.is_empty() { + return Ok(api_outputs); + } + + // build vec of commits for inclusion in query + let query_params: Vec = wallet_outputs + .iter() + .map(|commit| format!("{}", util::to_hex(commit.as_ref().to_vec()))) + .collect(); + let mut tasks = Vec::new(); - - let client = Client::new(); - - // Using an environment variable here, as this is a temporary fix - // and doesn't need to be permeated throughout the application - // configuration - let chunk_default = 200; + // going to leave this here even though we're moving + // to the json RPC api to keep the functionality of + // parallelizing larger requests. Will raise default + // from 200 to 500, however + let chunk_default = 500; let chunk_size = match env::var("GRIN_OUTPUT_QUERY_SIZE") { Ok(s) => match s.parse::() { Ok(c) => c, @@ -220,16 +222,37 @@ impl NodeClient for HTTPNodeClient { trace!("Output query chunk size is: {}", chunk_size); + let url = format!("{}{}", self.node_url(), ENDPOINT); + let client = Client::new(); + /*let res = client.post::(url.as_str(), self.node_api_secret(), &req);*/ + for query_chunk in query_params.chunks(chunk_size) { - let url = format!("{}/v1/chain/outputs/byids?{}", addr, query_chunk.join("&"),); - tasks.push(client.get_async::>(url.as_str(), self.node_api_secret())); + let params = json!([query_chunk, null, null, false, false]); + let req = build_request("get_outputs", ¶ms); + tasks.push(client.post_async::( + url.as_str(), + &req, + self.node_api_secret(), + )); } let task = stream::futures_unordered(tasks).collect(); - let mut rt = Runtime::new().unwrap(); - let results = match rt.block_on(task) { - Ok(outputs) => outputs, + let results: Vec = match rt.block_on(task) { + Ok(resps) => { + let mut results = vec![]; + for r in resps { + match r.into_result::>() { + Ok(mut r) => results.append(&mut r), + Err(e) => { + let report = format!("Unable to parse response for get_outputs: {}", e); + error!("{}", report); + return Err(libwallet::ErrorKind::ClientCallback(report).into()); + } + }; + } + results + } Err(e) => { let report = format!("Getting outputs by id: {}", e); error!("Outputs by id failed: {}", e); @@ -237,13 +260,18 @@ impl NodeClient for HTTPNodeClient { } }; - for res in results { - for out in res { - api_outputs.insert( - out.commit.commit(), - (util::to_hex(out.commit.to_vec()), out.height, out.mmr_index), - ); - } + for out in results.iter() { + let height = match out.block_height { + Some(h) => h, + None => { + let msg = format!("Missing block height for output {:?}", out.commit); + return Err(libwallet::ErrorKind::ClientCallback(msg).into()); + } + }; + api_outputs.insert( + out.commit, + (util::to_hex(out.commit.0.to_vec()), height, out.mmr_index), + ); } Ok(api_outputs) } @@ -261,68 +289,47 @@ impl NodeClient for HTTPNodeClient { ), libwallet::Error, > { - let addr = self.node_url(); - let mut query_param = format!("start_index={}&max={}", start_index, max_outputs); - - if let Some(e) = end_index { - query_param = format!("{}&end_index={}", query_param, e); - }; - - let url = format!("{}/v1/txhashset/outputs?{}", addr, query_param,); - let mut api_outputs: Vec<(pedersen::Commitment, pedersen::RangeProof, bool, u64, u64)> = Vec::new(); - let client = Client::new(); - - match client.get::(url.as_str(), self.node_api_secret()) { - Ok(o) => { - for out in o.outputs { - let is_coinbase = match out.output_type { - api::OutputType::Coinbase => true, - api::OutputType::Transaction => false, - }; - let range_proof = match out.range_proof() { - Ok(r) => r, - Err(e) => { - let msg = format!("Unexpected error in returned output (missing range proof): {:?}. {:?}, {}", - out.commit, - out, - e); - error!("{}", msg); - return Err(libwallet::ErrorKind::ClientCallback(msg).into()); - } - }; - let block_height = match out.block_height { - Some(h) => h, - None => { - let msg = format!("Unexpected error in returned output (missing block height): {:?}. {:?}", - out.commit, - out); - error!("{}", msg); - return Err(libwallet::ErrorKind::ClientCallback(msg).into()); - } - }; - api_outputs.push(( - out.commit, - range_proof, - is_coinbase, - block_height, - out.mmr_index, - )); + let params = json!([start_index, end_index, max_outputs, Some(true)]); + let res = self.send_json_request::("get_unspent_outputs", ¶ms)?; + for out in res.outputs { + let is_coinbase = match out.output_type { + api::OutputType::Coinbase => true, + api::OutputType::Transaction => false, + }; + let range_proof = match out.range_proof() { + Ok(r) => r, + Err(e) => { + let msg = format!( + "Unexpected error in returned output (missing range proof): {:?}. {:?}, {}", + out.commit, out, e + ); + error!("{}", msg); + return Err(libwallet::ErrorKind::ClientCallback(msg).into()); } - Ok((o.highest_index, o.last_retrieved_index, api_outputs)) - } - Err(e) => { - // if we got anything other than 200 back from server, bye - error!( - "get_outputs_by_pmmr_index: error contacting {}. Error: {}", - addr, e - ); - let report = format!("outputs by pmmr index: {}", e); - Err(libwallet::ErrorKind::ClientCallback(report).into()) - } + }; + let block_height = match out.block_height { + Some(h) => h, + None => { + let msg = format!( + "Unexpected error in returned output (missing block height): {:?}. {:?}", + out.commit, out + ); + error!("{}", msg); + return Err(libwallet::ErrorKind::ClientCallback(msg).into()); + } + }; + api_outputs.push(( + out.commit, + range_proof, + is_coinbase, + block_height, + out.mmr_index, + )); } + Ok((res.highest_index, res.last_retrieved_index, api_outputs)) } fn height_range_to_pmmr_indices( @@ -330,52 +337,9 @@ impl NodeClient for HTTPNodeClient { start_height: u64, end_height: Option, ) -> Result<(u64, u64), libwallet::Error> { - debug!("Indices start"); - let addr = self.node_url(); - let mut query_param = format!("start_height={}", start_height); - if let Some(e) = end_height { - query_param = format!("{}&end_height={}", query_param, e); - }; + let params = json!([start_height, end_height]); + let res = self.send_json_request::("get_pmmr_indices", ¶ms)?; - let url = format!("{}/v1/txhashset/heightstopmmr?{}", addr, query_param,); - - let client = Client::new(); - - match client.get::(url.as_str(), self.node_api_secret()) { - Ok(o) => Ok((o.last_retrieved_index, o.highest_index)), - Err(e) => { - // if we got anything other than 200 back from server, bye - error!("heightstopmmr: error contacting {}. Error: {}", addr, e); - let report = format!(": {}", e); - Err(libwallet::ErrorKind::ClientCallback(report).into()) - } - } + Ok((res.last_retrieved_index, res.highest_index)) } } - -/* -/// Call the wallet API to create a coinbase output for the given block_fees. -/// Will retry based on default "retry forever with backoff" behavior. -pub fn create_coinbase(dest: &str, block_fees: &BlockFees) -> Result { - let url = format!("{}/v1/wallet/foreign/build_coinbase", dest); - match single_create_coinbase(&url, &block_fees) { - Err(e) => { - error!( - "Failed to get coinbase from {}. Run grin-wallet listen?", - url - ); - error!("Underlying Error: {}", e.cause().unwrap()); - error!("Backtrace: {}", e.backtrace().unwrap()); - Err(e)? - } - Ok(res) => Ok(res), - } -} - -/// Makes a single request to the wallet API to create a new coinbase output. -fn single_create_coinbase(url: &str, block_fees: &BlockFees) -> Result { - let res = Client::post(url, None, block_fees).context(ErrorKind::GenericError( - "Posting create coinbase".to_string(), - ))?; - Ok(res) -}*/ diff --git a/impls/src/node_clients/mod.rs b/impls/src/node_clients/mod.rs index 2dc12a07..517f04c0 100644 --- a/impls/src/node_clients/mod.rs +++ b/impls/src/node_clients/mod.rs @@ -13,5 +13,6 @@ // limitations under the License. mod http; +mod resp_types; pub use self::http::HTTPNodeClient; diff --git a/impls/src/node_clients/resp_types.rs b/impls/src/node_clients/resp_types.rs new file mode 100644 index 00000000..390151e5 --- /dev/null +++ b/impls/src/node_clients/resp_types.rs @@ -0,0 +1,30 @@ +// Copyright 2020 The Grin Developers +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// Derived from https://github.com/apoelstra/rust-jsonrpc + +//! JSON RPC Types for V2 node client + +#[derive(Debug, Deserialize)] +pub struct GetTipResp { + pub height: u64, + pub last_block_pushed: String, + pub prev_block_to_last: String, + pub total_difficulty: u64, +} + +#[derive(Debug, Deserialize)] +pub struct GetVersionResp { + pub node_version: String, + pub block_header_version: u16, +} diff --git a/impls/src/test_framework/testclient.rs b/impls/src/test_framework/testclient.rs index 54ba9f83..816dddcd 100644 --- a/impls/src/test_framework/testclient.rs +++ b/impls/src/test_framework/testclient.rs @@ -22,14 +22,12 @@ use crate::chain::Chain; use crate::core::core::verifier_cache::LruVerifierCache; use crate::core::core::{Transaction, TxKernel}; use crate::core::global::{set_mining_mode, ChainTypes}; -use crate::core::{pow, ser}; +use crate::core::pow; use crate::keychain::Keychain; use crate::libwallet; use crate::libwallet::api_impl::foreign; use crate::libwallet::slate_versions::v3::SlateV3; -use crate::libwallet::{ - NodeClient, NodeVersionInfo, Slate, TxWrapper, WalletInst, WalletLCProvider, -}; +use crate::libwallet::{NodeClient, NodeVersionInfo, Slate, WalletInst, WalletLCProvider}; use crate::util; use crate::util::secp::key::SecretKey; use crate::util::secp::pedersen; @@ -177,16 +175,8 @@ where fn post_tx(&mut self, m: WalletProxyMessage) -> Result { let dest_wallet = self.wallets.get_mut(&m.sender_id).unwrap().1.clone(); let dest_wallet_mask = self.wallets.get_mut(&m.sender_id).unwrap().2.clone(); - let wrapper: TxWrapper = serde_json::from_str(&m.body).context( - libwallet::ErrorKind::ClientCallback("Error parsing TxWrapper".to_owned()), - )?; - - let tx_bin = util::from_hex(wrapper.tx_hex).context( - libwallet::ErrorKind::ClientCallback("Error parsing TxWrapper: tx_bin".to_owned()), - )?; - - let tx: Transaction = ser::deserialize(&mut &tx_bin[..], ser::ProtocolVersion(1)).context( - libwallet::ErrorKind::ClientCallback("Error parsing TxWrapper: tx".to_owned()), + let tx: Transaction = serde_json::from_str(&m.body).context( + libwallet::ErrorKind::ClientCallback("Error parsing Transaction".to_owned()), )?; super::award_block_to_wallet( @@ -441,7 +431,7 @@ impl NodeClient for LocalWalletClient { } /// Posts a transaction to a grin node /// In this case it will create a new block with award rewarded to - fn post_tx(&self, tx: &TxWrapper, _fluff: bool) -> Result<(), libwallet::Error> { + fn post_tx(&self, tx: &Transaction, _fluff: bool) -> Result<(), libwallet::Error> { let m = WalletProxyMessage { sender_id: self.id.clone(), dest: self.node_url().to_owned(), diff --git a/libwallet/src/api_impl/owner.rs b/libwallet/src/api_impl/owner.rs index 96f21ec2..bbbe8cdd 100644 --- a/libwallet/src/api_impl/owner.rs +++ b/libwallet/src/api_impl/owner.rs @@ -18,8 +18,6 @@ use uuid::Uuid; use crate::grin_core::core::hash::Hashed; use crate::grin_core::core::Transaction; -use crate::grin_core::ser; -use crate::grin_util; use crate::grin_util::secp::key::SecretKey; use crate::grin_util::Mutex; use crate::util::OnionV3Address; @@ -28,7 +26,7 @@ use crate::api_impl::owner_updater::StatusMessage; use crate::grin_keychain::{Identifier, Keychain}; use crate::internal::{keys, scan, selection, tx, updater}; use crate::slate::{PaymentInfo, Slate}; -use crate::types::{AcctPathMapping, NodeClient, TxLogEntry, TxWrapper, WalletBackend, WalletInfo}; +use crate::types::{AcctPathMapping, NodeClient, TxLogEntry, WalletBackend, WalletInfo}; use crate::{ address, wallet_lock, InitTxArgs, IssueInvoiceTxArgs, NodeHeightResult, OutputCommitMapping, PaymentProof, ScannedBlockInfo, TxLogEntryType, WalletInitStatus, WalletInst, WalletLCProvider, @@ -632,8 +630,7 @@ pub fn post_tx<'a, C>(client: &C, tx: &Transaction, fluff: bool) -> Result<(), E where C: NodeClient + 'a, { - let tx_hex = grin_util::to_hex(ser::ser_vec(tx, ser::ProtocolVersion(1)).unwrap()); - let res = client.post_tx(&TxWrapper { tx_hex: tx_hex }, fluff); + let res = client.post_tx(tx, fluff); if let Err(e) = res { error!("api: post_tx: failed with error: {}", e); Err(e) diff --git a/libwallet/src/types.rs b/libwallet/src/types.rs index 55166848..d25c0bf0 100644 --- a/libwallet/src/types.rs +++ b/libwallet/src/types.rs @@ -329,7 +329,7 @@ pub trait NodeClient: Send + Sync + Clone { fn set_node_api_secret(&mut self, node_api_secret: Option); /// Posts a transaction to a grin node - fn post_tx(&self, tx: &TxWrapper, fluff: bool) -> Result<(), Error>; + fn post_tx(&self, tx: &Transaction, fluff: bool) -> Result<(), Error>; /// Returns the api version string and block header version as reported /// by the node. Result can be cached for later use