]>
Commit | Line | Data |
---|---|---|
3fc68461 | 1 | // Copyright (c) 2013-2014 The Bitcoin Core developers |
78253fcb | 2 | // Distributed under the MIT software license, see the accompanying |
bc909a7a | 3 | // file COPYING or https://www.opensource.org/licenses/mit-license.php . |
3fc68461 | 4 | |
4519a766 DC |
5 | #include "rpc/server.h" |
6 | #include "rpc/client.h" | |
5094f8d4 | 7 | |
93bd00a0 | 8 | #include "key_io.h" |
8a893c94 | 9 | #include "main.h" |
50c72f23 | 10 | #include "wallet/wallet.h" |
5094f8d4 | 11 | |
51598b26 PW |
12 | #include "test/test_bitcoin.h" |
13 | ||
60f762a5 S |
14 | #include "zcash/Address.hpp" |
15 | ||
b922924d S |
16 | #include "asyncrpcqueue.h" |
17 | #include "asyncrpcoperation.h" | |
d3d8fb72 | 18 | #include "wallet/asyncrpcoperation_common.h" |
6e9c7629 | 19 | #include "wallet/asyncrpcoperation_mergetoaddress.h" |
b922924d | 20 | #include "wallet/asyncrpcoperation_sendmany.h" |
06c19063 S |
21 | #include "wallet/asyncrpcoperation_shieldcoinbase.h" |
22 | ||
7b79275e | 23 | #include "init.h" |
c3e2de59 | 24 | #include "utiltest.h" |
b922924d | 25 | |
a6bbb26e | 26 | #include <array> |
b922924d S |
27 | #include <chrono> |
28 | #include <thread> | |
29 | ||
60f762a5 S |
30 | #include <fstream> |
31 | #include <unordered_set> | |
32 | ||
5094f8d4 WL |
33 | #include <boost/algorithm/string.hpp> |
34 | #include <boost/test/unit_test.hpp> | |
60f762a5 | 35 | #include <boost/format.hpp> |
6be367ea | 36 | #include <boost/filesystem.hpp> |
5094f8d4 | 37 | |
a10a6e2a | 38 | #include <univalue.h> |
d014114d | 39 | |
5094f8d4 | 40 | using namespace std; |
5094f8d4 | 41 | |
851f58f9 | 42 | extern UniValue createArgs(int nRequired, const char* address1 = NULL, const char* address2 = NULL); |
d014114d | 43 | extern UniValue CallRPC(string args); |
5094f8d4 | 44 | |
fd67424c GA |
45 | extern CWallet* pwalletMain; |
46 | ||
0d37ae3a | 47 | bool find_error(const UniValue& objError, const std::string& expected) { |
da5e7e51 S |
48 | return find_value(objError, "message").get_str().find(expected) != string::npos; |
49 | } | |
50 | ||
bf69507c | 51 | static UniValue ValueFromString(const std::string &str) |
52 | { | |
53 | UniValue value; | |
54 | BOOST_CHECK(value.setNumStr(str)); | |
55 | return value; | |
56 | } | |
57 | ||
51598b26 | 58 | BOOST_FIXTURE_TEST_SUITE(rpc_wallet_tests, TestingSetup) |
5094f8d4 WL |
59 | |
60 | BOOST_AUTO_TEST_CASE(rpc_addmultisig) | |
61 | { | |
fd67424c GA |
62 | LOCK(pwalletMain->cs_wallet); |
63 | ||
5094f8d4 WL |
64 | rpcfn_type addmultisig = tableRPC["addmultisigaddress"]->actor; |
65 | ||
66 | // old, 65-byte-long: | |
67 | const char address1Hex[] = "0434e3e09f49ea168c5bbf53f877ff4206923858aab7c7e1df25bc263978107c95e35065a27ef6f1b27222db0ec97e0e895eaca603d3ee0d4c060ce3d8a00286c8"; | |
68 | // new, compressed: | |
69 | const char address2Hex[] = "0388c2037017c62240b6b72ac1a2a5f94da790596ebd06177c8572752922165cb4"; | |
70 | ||
d014114d | 71 | UniValue v; |
b6be3e88 | 72 | CTxDestination address; |
5094f8d4 | 73 | BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex), false)); |
b6be3e88 JG |
74 | address = DecodeDestination(v.get_str()); |
75 | BOOST_CHECK(IsValidDestination(address) && boost::get<CScriptID>(&address) != nullptr); | |
5094f8d4 WL |
76 | |
77 | BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(1, address1Hex, address2Hex), false)); | |
b6be3e88 JG |
78 | address = DecodeDestination(v.get_str()); |
79 | BOOST_CHECK(IsValidDestination(address) && boost::get<CScriptID>(&address) != nullptr); | |
5094f8d4 WL |
80 | |
81 | BOOST_CHECK_NO_THROW(v = addmultisig(createArgs(2, address1Hex, address2Hex), false)); | |
b6be3e88 JG |
82 | address = DecodeDestination(v.get_str()); |
83 | BOOST_CHECK(IsValidDestination(address) && boost::get<CScriptID>(&address) != nullptr); | |
5094f8d4 WL |
84 | |
85 | BOOST_CHECK_THROW(addmultisig(createArgs(0), false), runtime_error); | |
86 | BOOST_CHECK_THROW(addmultisig(createArgs(1), false), runtime_error); | |
87 | BOOST_CHECK_THROW(addmultisig(createArgs(2, address1Hex), false), runtime_error); | |
88 | ||
89 | BOOST_CHECK_THROW(addmultisig(createArgs(1, ""), false), runtime_error); | |
90 | BOOST_CHECK_THROW(addmultisig(createArgs(1, "NotAValidPubkey"), false), runtime_error); | |
91 | ||
bc470c43 | 92 | string short1(address1Hex, address1Hex + sizeof(address1Hex) - 2); // last byte missing |
5094f8d4 WL |
93 | BOOST_CHECK_THROW(addmultisig(createArgs(2, short1.c_str()), false), runtime_error); |
94 | ||
bc470c43 | 95 | string short2(address1Hex + 1, address1Hex + sizeof(address1Hex)); // first byte missing |
5094f8d4 WL |
96 | BOOST_CHECK_THROW(addmultisig(createArgs(2, short2.c_str()), false), runtime_error); |
97 | } | |
98 | ||
99 | BOOST_AUTO_TEST_CASE(rpc_wallet) | |
100 | { | |
101 | // Test RPC calls for various wallet statistics | |
d014114d | 102 | UniValue r; |
5094f8d4 | 103 | |
ed671005 | 104 | LOCK2(cs_main, pwalletMain->cs_wallet); |
fd67424c | 105 | |
75ebced4 | 106 | CPubKey demoPubkey = pwalletMain->GenerateNewKey(); |
b6be3e88 | 107 | CTxDestination demoAddress(CTxDestination(demoPubkey.GetID())); |
d014114d | 108 | UniValue retValue; |
b6f100cf | 109 | string strAccount = ""; |
bc470c43 ES |
110 | string strPurpose = "receive"; |
111 | BOOST_CHECK_NO_THROW({ /*Initialize Wallet with an account */ | |
112 | CWalletDB walletdb(pwalletMain->strWalletFile); | |
113 | CAccount account; | |
114 | account.vchPubKey = demoPubkey; | |
115 | pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, strPurpose); | |
116 | walletdb.WriteAccount(strAccount, account); | |
117 | }); | |
75ebced4 | 118 | |
31d6390f | 119 | CPubKey setaccountDemoPubkey = pwalletMain->GenerateNewKey(); |
b6be3e88 | 120 | CTxDestination setaccountDemoAddress(CTxDestination(setaccountDemoPubkey.GetID())); |
bc470c43 ES |
121 | |
122 | /********************************* | |
123 | * setaccount | |
124 | *********************************/ | |
b6be3e88 | 125 | BOOST_CHECK_NO_THROW(CallRPC("setaccount " + EncodeDestination(setaccountDemoAddress) + " \"\"")); |
b6f100cf | 126 | /* Accounts are disabled */ |
b6be3e88 | 127 | BOOST_CHECK_THROW(CallRPC("setaccount " + EncodeDestination(setaccountDemoAddress) + " nullaccount"), runtime_error); |
ee64b5e7 JG |
128 | /* t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1GpgV is not owned by the test wallet. */ |
129 | BOOST_CHECK_THROW(CallRPC("setaccount t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1GpgV nullaccount"), runtime_error); | |
bc470c43 | 130 | BOOST_CHECK_THROW(CallRPC("setaccount"), runtime_error); |
ee64b5e7 JG |
131 | /* t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1Gpg (34 chars) is an illegal address (should be 35 chars) */ |
132 | BOOST_CHECK_THROW(CallRPC("setaccount t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1Gpg nullaccount"), runtime_error); | |
75ebced4 | 133 | |
7c5dd603 EF |
134 | |
135 | /********************************* | |
136 | * getbalance | |
137 | *********************************/ | |
138 | BOOST_CHECK_NO_THROW(CallRPC("getbalance")); | |
b6be3e88 | 139 | BOOST_CHECK_THROW(CallRPC("getbalance " + EncodeDestination(demoAddress)), runtime_error); |
7c5dd603 | 140 | |
75ebced4 AM |
141 | /********************************* |
142 | * listunspent | |
143 | *********************************/ | |
5094f8d4 WL |
144 | BOOST_CHECK_NO_THROW(CallRPC("listunspent")); |
145 | BOOST_CHECK_THROW(CallRPC("listunspent string"), runtime_error); | |
146 | BOOST_CHECK_THROW(CallRPC("listunspent 0 string"), runtime_error); | |
147 | BOOST_CHECK_THROW(CallRPC("listunspent 0 1 not_array"), runtime_error); | |
148 | BOOST_CHECK_THROW(CallRPC("listunspent 0 1 [] extra"), runtime_error); | |
bc470c43 | 149 | BOOST_CHECK_NO_THROW(r = CallRPC("listunspent 0 1 []")); |
5094f8d4 WL |
150 | BOOST_CHECK(r.get_array().empty()); |
151 | ||
75ebced4 | 152 | /********************************* |
bc470c43 ES |
153 | * listreceivedbyaddress |
154 | *********************************/ | |
5094f8d4 WL |
155 | BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress")); |
156 | BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0")); | |
157 | BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress not_int"), runtime_error); | |
158 | BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 not_bool"), runtime_error); | |
159 | BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaddress 0 true")); | |
160 | BOOST_CHECK_THROW(CallRPC("listreceivedbyaddress 0 true extra"), runtime_error); | |
161 | ||
75ebced4 | 162 | /********************************* |
bc470c43 ES |
163 | * listreceivedbyaccount |
164 | *********************************/ | |
5094f8d4 WL |
165 | BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount")); |
166 | BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0")); | |
167 | BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount not_int"), runtime_error); | |
168 | BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 not_bool"), runtime_error); | |
169 | BOOST_CHECK_NO_THROW(CallRPC("listreceivedbyaccount 0 true")); | |
170 | BOOST_CHECK_THROW(CallRPC("listreceivedbyaccount 0 true extra"), runtime_error); | |
75ebced4 | 171 | |
7c5dd603 EF |
172 | /********************************* |
173 | * listsinceblock | |
174 | *********************************/ | |
175 | BOOST_CHECK_NO_THROW(CallRPC("listsinceblock")); | |
176 | ||
177 | /********************************* | |
178 | * listtransactions | |
179 | *********************************/ | |
180 | BOOST_CHECK_NO_THROW(CallRPC("listtransactions")); | |
b6be3e88 JG |
181 | BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + EncodeDestination(demoAddress))); |
182 | BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + EncodeDestination(demoAddress) + " 20")); | |
183 | BOOST_CHECK_NO_THROW(CallRPC("listtransactions " + EncodeDestination(demoAddress) + " 20 0")); | |
184 | BOOST_CHECK_THROW(CallRPC("listtransactions " + EncodeDestination(demoAddress) + " not_int"), runtime_error); | |
7c5dd603 EF |
185 | |
186 | /********************************* | |
187 | * listlockunspent | |
188 | *********************************/ | |
189 | BOOST_CHECK_NO_THROW(CallRPC("listlockunspent")); | |
190 | ||
191 | /********************************* | |
192 | * listaccounts | |
193 | *********************************/ | |
194 | BOOST_CHECK_NO_THROW(CallRPC("listaccounts")); | |
195 | ||
196 | /********************************* | |
197 | * listaddressgroupings | |
198 | *********************************/ | |
199 | BOOST_CHECK_NO_THROW(CallRPC("listaddressgroupings")); | |
200 | ||
75ebced4 | 201 | /********************************* |
bc470c43 ES |
202 | * getrawchangeaddress |
203 | *********************************/ | |
75ebced4 AM |
204 | BOOST_CHECK_NO_THROW(CallRPC("getrawchangeaddress")); |
205 | ||
206 | /********************************* | |
bc470c43 ES |
207 | * getnewaddress |
208 | *********************************/ | |
75ebced4 | 209 | BOOST_CHECK_NO_THROW(CallRPC("getnewaddress")); |
b6f100cf JG |
210 | BOOST_CHECK_NO_THROW(CallRPC("getnewaddress \"\"")); |
211 | /* Accounts are deprecated */ | |
212 | BOOST_CHECK_THROW(CallRPC("getnewaddress getnewaddress_demoaccount"), runtime_error); | |
75ebced4 AM |
213 | |
214 | /********************************* | |
bc470c43 ES |
215 | * getaccountaddress |
216 | *********************************/ | |
75ebced4 | 217 | BOOST_CHECK_NO_THROW(CallRPC("getaccountaddress \"\"")); |
b6f100cf JG |
218 | /* Accounts are deprecated */ |
219 | BOOST_CHECK_THROW(CallRPC("getaccountaddress accountThatDoesntExists"), runtime_error); | |
bc470c43 | 220 | BOOST_CHECK_NO_THROW(retValue = CallRPC("getaccountaddress " + strAccount)); |
b6be3e88 | 221 | BOOST_CHECK(DecodeDestination(retValue.get_str()) == demoAddress); |
75ebced4 | 222 | |
bc470c43 ES |
223 | /********************************* |
224 | * getaccount | |
225 | *********************************/ | |
226 | BOOST_CHECK_THROW(CallRPC("getaccount"), runtime_error); | |
b6be3e88 | 227 | BOOST_CHECK_NO_THROW(CallRPC("getaccount " + EncodeDestination(demoAddress))); |
bc470c43 ES |
228 | |
229 | /********************************* | |
230 | * signmessage + verifymessage | |
231 | *********************************/ | |
b6be3e88 | 232 | BOOST_CHECK_NO_THROW(retValue = CallRPC("signmessage " + EncodeDestination(demoAddress) + " mymessage")); |
bc470c43 ES |
233 | BOOST_CHECK_THROW(CallRPC("signmessage"), runtime_error); |
234 | /* Should throw error because this address is not loaded in the wallet */ | |
ee64b5e7 | 235 | BOOST_CHECK_THROW(CallRPC("signmessage t1h8SqgtM3QM5e2M8EzhhT1yL2PXXtA6oqe mymessage"), runtime_error); |
bc470c43 ES |
236 | |
237 | /* missing arguments */ | |
b6be3e88 JG |
238 | BOOST_CHECK_THROW(CallRPC("verifymessage " + EncodeDestination(demoAddress)), runtime_error); |
239 | BOOST_CHECK_THROW(CallRPC("verifymessage " + EncodeDestination(demoAddress) + " " + retValue.get_str()), runtime_error); | |
bc470c43 | 240 | /* Illegal address */ |
ee64b5e7 | 241 | BOOST_CHECK_THROW(CallRPC("verifymessage t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1Gpg " + retValue.get_str() + " mymessage"), runtime_error); |
bc470c43 | 242 | /* wrong address */ |
ee64b5e7 | 243 | BOOST_CHECK(CallRPC("verifymessage t1VtArtnn1dGPiD2WFfMXYXW5mHM3q1GpgV " + retValue.get_str() + " mymessage").get_bool() == false); |
bc470c43 | 244 | /* Correct address and signature but wrong message */ |
b6be3e88 | 245 | BOOST_CHECK(CallRPC("verifymessage " + EncodeDestination(demoAddress) + " " + retValue.get_str() + " wrongmessage").get_bool() == false); |
bc470c43 | 246 | /* Correct address, message and signature*/ |
b6be3e88 | 247 | BOOST_CHECK(CallRPC("verifymessage " + EncodeDestination(demoAddress) + " " + retValue.get_str() + " mymessage").get_bool() == true); |
bc470c43 ES |
248 | |
249 | /********************************* | |
250 | * getaddressesbyaccount | |
251 | *********************************/ | |
252 | BOOST_CHECK_THROW(CallRPC("getaddressesbyaccount"), runtime_error); | |
253 | BOOST_CHECK_NO_THROW(retValue = CallRPC("getaddressesbyaccount " + strAccount)); | |
851f58f9 | 254 | UniValue arr = retValue.get_array(); |
b6f100cf JG |
255 | BOOST_CHECK_EQUAL(4, arr.size()); |
256 | bool notFound = true; | |
0d37ae3a | 257 | for (auto a : arr.getValues()) { |
b6be3e88 | 258 | notFound &= DecodeDestination(a.get_str()) != demoAddress; |
b6f100cf JG |
259 | } |
260 | BOOST_CHECK(!notFound); | |
5d50130b | 261 | |
3d8013a0 MC |
262 | /********************************* |
263 | * fundrawtransaction | |
264 | *********************************/ | |
265 | BOOST_CHECK_THROW(CallRPC("fundrawtransaction 28z"), runtime_error); | |
266 | BOOST_CHECK_THROW(CallRPC("fundrawtransaction 01000000000180969800000000001976a91450ce0a4b0ee0ddeb633da85199728b940ac3fe9488ac00000000"), runtime_error); | |
267 | ||
5d50130b S |
268 | /* |
269 | * getblocksubsidy | |
270 | */ | |
271 | BOOST_CHECK_THROW(CallRPC("getblocksubsidy too many args"), runtime_error); | |
272 | BOOST_CHECK_THROW(CallRPC("getblocksubsidy -1"), runtime_error); | |
273 | BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 50000")); | |
0d37ae3a JG |
274 | UniValue obj = retValue.get_obj(); |
275 | BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 10.0); | |
276 | BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 2.5); | |
5d50130b S |
277 | BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 1000000")); |
278 | obj = retValue.get_obj(); | |
0d37ae3a JG |
279 | BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 6.25); |
280 | BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 0.0); | |
5d50130b S |
281 | BOOST_CHECK_NO_THROW(retValue = CallRPC("getblocksubsidy 2000000")); |
282 | obj = retValue.get_obj(); | |
0d37ae3a JG |
283 | BOOST_CHECK_EQUAL(find_value(obj, "miner").get_real(), 3.125); |
284 | BOOST_CHECK_EQUAL(find_value(obj, "founders").get_real(), 0.0); | |
7d3b1528 S |
285 | |
286 | /* | |
287 | * getblock | |
288 | */ | |
289 | BOOST_CHECK_THROW(CallRPC("getblock too many args"), runtime_error); | |
290 | BOOST_CHECK_THROW(CallRPC("getblock -1"), runtime_error); | |
291 | BOOST_CHECK_THROW(CallRPC("getblock 2147483647"), runtime_error); // allowed, but > height of active chain tip | |
292 | BOOST_CHECK_THROW(CallRPC("getblock 2147483648"), runtime_error); // not allowed, > int32 used for nHeight | |
293 | BOOST_CHECK_THROW(CallRPC("getblock 100badchars"), runtime_error); | |
294 | BOOST_CHECK_NO_THROW(CallRPC("getblock 0")); | |
9bd8f092 S |
295 | BOOST_CHECK_NO_THROW(CallRPC("getblock 0 0")); |
296 | BOOST_CHECK_NO_THROW(CallRPC("getblock 0 1")); | |
297 | BOOST_CHECK_NO_THROW(CallRPC("getblock 0 2")); | |
298 | BOOST_CHECK_THROW(CallRPC("getblock 0 -1"), runtime_error); // bad verbosity | |
299 | BOOST_CHECK_THROW(CallRPC("getblock 0 3"), runtime_error); // bad verbosity | |
4cbe0a9d LR |
300 | |
301 | /* | |
302 | * migration (sprout to sapling) | |
303 | */ | |
304 | BOOST_CHECK_NO_THROW(CallRPC("z_setmigration true")); | |
305 | BOOST_CHECK_NO_THROW(CallRPC("z_setmigration false")); | |
306 | BOOST_CHECK_THROW(CallRPC("z_setmigration"), runtime_error); | |
307 | BOOST_CHECK_THROW(CallRPC("z_setmigration nonboolean"), runtime_error); | |
308 | BOOST_CHECK_THROW(CallRPC("z_setmigration 1"), runtime_error); | |
5094f8d4 WL |
309 | } |
310 | ||
e883ffef S |
311 | BOOST_AUTO_TEST_CASE(rpc_wallet_getbalance) |
312 | { | |
313 | SelectParams(CBaseChainParams::TESTNET); | |
314 | ||
315 | LOCK(pwalletMain->cs_wallet); | |
316 | ||
06c19063 | 317 | |
e883ffef S |
318 | BOOST_CHECK_THROW(CallRPC("z_getbalance too many args"), runtime_error); |
319 | BOOST_CHECK_THROW(CallRPC("z_getbalance invalidaddress"), runtime_error); | |
ee64b5e7 JG |
320 | BOOST_CHECK_NO_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab")); |
321 | BOOST_CHECK_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab -1"), runtime_error); | |
322 | BOOST_CHECK_NO_THROW(CallRPC("z_getbalance tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab 0")); | |
e883ffef | 323 | BOOST_CHECK_THROW(CallRPC("z_getbalance tnRZ8bPq2pff3xBWhTJhNkVUkm2uhzksDeW5PvEa7aFKGT9Qi3YgTALZfjaY4jU3HLVKBtHdSXxoPoLA3naMPcHBcY88FcF 1"), runtime_error); |
06c19063 S |
324 | |
325 | ||
e883ffef S |
326 | BOOST_CHECK_THROW(CallRPC("z_gettotalbalance too manyargs"), runtime_error); |
327 | BOOST_CHECK_THROW(CallRPC("z_gettotalbalance -1"), runtime_error); | |
328 | BOOST_CHECK_NO_THROW(CallRPC("z_gettotalbalance 0")); | |
06c19063 S |
329 | |
330 | ||
e883ffef S |
331 | BOOST_CHECK_THROW(CallRPC("z_listreceivedbyaddress too many args"), runtime_error); |
332 | // negative minconf not allowed | |
ee64b5e7 | 333 | BOOST_CHECK_THROW(CallRPC("z_listreceivedbyaddress tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab -1"), runtime_error); |
e883ffef | 334 | // invalid zaddr, taddr not allowed |
ee64b5e7 | 335 | BOOST_CHECK_THROW(CallRPC("z_listreceivedbyaddress tmC6YZnCUhm19dEXxh3Jb7srdBJxDawaCab 0"), runtime_error); |
e883ffef S |
336 | // don't have the spending key |
337 | BOOST_CHECK_THROW(CallRPC("z_listreceivedbyaddress tnRZ8bPq2pff3xBWhTJhNkVUkm2uhzksDeW5PvEa7aFKGT9Qi3YgTALZfjaY4jU3HLVKBtHdSXxoPoLA3naMPcHBcY88FcF 1"), runtime_error); | |
338 | } | |
339 | ||
4e16a724 S |
340 | /** |
341 | * This test covers RPC command z_validateaddress | |
342 | */ | |
343 | BOOST_AUTO_TEST_CASE(rpc_wallet_z_validateaddress) | |
344 | { | |
345 | SelectParams(CBaseChainParams::MAIN); | |
346 | ||
347 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
348 | ||
0d37ae3a | 349 | UniValue retValue; |
4e16a724 S |
350 | |
351 | // Check number of args | |
352 | BOOST_CHECK_THROW(CallRPC("z_validateaddress"), runtime_error); | |
353 | BOOST_CHECK_THROW(CallRPC("z_validateaddress toomany args"), runtime_error); | |
354 | ||
355 | // Wallet should be empty | |
e5eab182 | 356 | std::set<libzcash::SproutPaymentAddress> addrs; |
25d5e80c | 357 | pwalletMain->GetSproutPaymentAddresses(addrs); |
4e16a724 S |
358 | BOOST_CHECK(addrs.size()==0); |
359 | ||
360 | // This address is not valid, it belongs to another network | |
361 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress ztaaga95QAPyp1kSQ1hD2kguCpzyMHjxWZqaYDEkzbvo7uYQYAw2S8X4Kx98AvhhofMtQL8PAXKHuZsmhRcanavKRKmdCzk")); | |
0d37ae3a | 362 | UniValue resultObj = retValue.get_obj(); |
4e16a724 S |
363 | bool b = find_value(resultObj, "isvalid").get_bool(); |
364 | BOOST_CHECK_EQUAL(b, false); | |
365 | ||
366 | // This address is valid, but the spending key is not in this wallet | |
367 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress zcfA19SDAKRYHLoRDoShcoz4nPohqWxuHcqg8WAxsiB2jFrrs6k7oSvst3UZvMYqpMNSRBkxBsnyjjngX5L55FxMzLKach8")); | |
368 | resultObj = retValue.get_obj(); | |
369 | b = find_value(resultObj, "isvalid").get_bool(); | |
370 | BOOST_CHECK_EQUAL(b, true); | |
bea87915 | 371 | BOOST_CHECK_EQUAL(find_value(resultObj, "type").get_str(), "sprout"); |
4e16a724 S |
372 | b = find_value(resultObj, "ismine").get_bool(); |
373 | BOOST_CHECK_EQUAL(b, false); | |
374 | ||
375 | // Let's import a spending key to the wallet and validate its payment address | |
376 | BOOST_CHECK_NO_THROW(CallRPC("z_importkey SKxoWv77WGwFnUJitQKNEcD636bL4X5Gd6wWmgaA4Q9x8jZBPJXT")); | |
377 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress zcWsmqT4X2V4jgxbgiCzyrAfRT1vi1F4sn7M5Pkh66izzw8Uk7LBGAH3DtcSMJeUb2pi3W4SQF8LMKkU2cUuVP68yAGcomL")); | |
378 | resultObj = retValue.get_obj(); | |
379 | b = find_value(resultObj, "isvalid").get_bool(); | |
380 | BOOST_CHECK_EQUAL(b, true); | |
bea87915 | 381 | BOOST_CHECK_EQUAL(find_value(resultObj, "type").get_str(), "sprout"); |
4e16a724 S |
382 | b = find_value(resultObj, "ismine").get_bool(); |
383 | BOOST_CHECK_EQUAL(b, true); | |
384 | BOOST_CHECK_EQUAL(find_value(resultObj, "payingkey").get_str(), "f5bb3c888ccc9831e3f6ba06e7528e26a312eec3acc1823be8918b6a3a5e20ad"); | |
385 | BOOST_CHECK_EQUAL(find_value(resultObj, "transmissionkey").get_str(), "7a58c7132446564e6b810cf895c20537b3528357dc00150a8e201f491efa9c1a"); | |
bea87915 JG |
386 | |
387 | // This Sapling address is not valid, it belongs to another network | |
388 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress ztestsapling1knww2nyjc62njkard0jmx7hlsj6twxmxwprn7anvrv4dc2zxanl3nemc0qx2hvplxmd2uau8gyw")); | |
389 | resultObj = retValue.get_obj(); | |
390 | b = find_value(resultObj, "isvalid").get_bool(); | |
391 | BOOST_CHECK_EQUAL(b, false); | |
392 | ||
393 | // This Sapling address is valid, but the spending key is not in this wallet | |
394 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_validateaddress zs1z7rejlpsa98s2rrrfkwmaxu53e4ue0ulcrw0h4x5g8jl04tak0d3mm47vdtahatqrlkngh9slya")); | |
395 | resultObj = retValue.get_obj(); | |
396 | b = find_value(resultObj, "isvalid").get_bool(); | |
34e222c1 JG |
397 | BOOST_CHECK_EQUAL(b, true); |
398 | BOOST_CHECK_EQUAL(find_value(resultObj, "type").get_str(), "sapling"); | |
399 | b = find_value(resultObj, "ismine").get_bool(); | |
bea87915 | 400 | BOOST_CHECK_EQUAL(b, false); |
34e222c1 JG |
401 | BOOST_CHECK_EQUAL(find_value(resultObj, "diversifier").get_str(), "1787997c30e94f050c634d"); |
402 | BOOST_CHECK_EQUAL(find_value(resultObj, "diversifiedtransmissionkey").get_str(), "34ed1f60f5db5763beee1ddbb37dd5f7e541d4d4fbdcc09fbfcc6b8e949bbe9d"); | |
4e16a724 S |
403 | } |
404 | ||
60f762a5 S |
405 | /* |
406 | * This test covers RPC command z_exportwallet | |
407 | */ | |
408 | BOOST_AUTO_TEST_CASE(rpc_wallet_z_exportwallet) | |
409 | { | |
410 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
4e16a724 | 411 | |
4b2e5571 | 412 | // wallet should be empty |
e5eab182 | 413 | std::set<libzcash::SproutPaymentAddress> addrs; |
25d5e80c | 414 | pwalletMain->GetSproutPaymentAddresses(addrs); |
60f762a5 S |
415 | BOOST_CHECK(addrs.size()==0); |
416 | ||
417 | // wallet should have one key | |
6829be19 | 418 | libzcash::SproutPaymentAddress addr = pwalletMain->GenerateNewSproutZKey(); |
25d5e80c | 419 | pwalletMain->GetSproutPaymentAddresses(addrs); |
60f762a5 | 420 | BOOST_CHECK(addrs.size()==1); |
06c19063 | 421 | |
9064d73b S |
422 | // Set up paths |
423 | boost::filesystem::path tmppath = boost::filesystem::temp_directory_path(); | |
424 | boost::filesystem::path tmpfilename = boost::filesystem::unique_path("%%%%%%%%"); | |
425 | boost::filesystem::path exportfilepath = tmppath / tmpfilename; | |
426 | ||
427 | // export will fail since exportdir is not set | |
428 | BOOST_CHECK_THROW(CallRPC(string("z_exportwallet ") + tmpfilename.string()), runtime_error); | |
429 | ||
430 | // set exportdir | |
c5b26aca | 431 | mapArgs["-exportdir"] = tmppath.string(); |
9064d73b S |
432 | |
433 | // run some tests | |
60f762a5 | 434 | BOOST_CHECK_THROW(CallRPC("z_exportwallet"), runtime_error); |
e346a0b3 S |
435 | |
436 | BOOST_CHECK_THROW(CallRPC("z_exportwallet toomany args"), runtime_error); | |
437 | ||
9064d73b S |
438 | BOOST_CHECK_THROW(CallRPC(string("z_exportwallet invalid!*/_chars.txt")), runtime_error); |
439 | ||
440 | BOOST_CHECK_NO_THROW(CallRPC(string("z_exportwallet ") + tmpfilename.string())); | |
60f762a5 | 441 | |
60f762a5 | 442 | |
e5eab182 | 443 | libzcash::SproutSpendingKey key; |
25d5e80c | 444 | BOOST_CHECK(pwalletMain->GetSproutSpendingKey(addr, key)); |
60f762a5 | 445 | |
80ed13d5 | 446 | std::string s1 = EncodePaymentAddress(addr); |
472f75bc | 447 | std::string s2 = EncodeSpendingKey(key); |
06c19063 | 448 | |
60f762a5 S |
449 | // There's no way to really delete a private key so we will read in the |
450 | // exported wallet file and search for the spending key and payment address. | |
06c19063 | 451 | |
60f762a5 S |
452 | EnsureWalletIsUnlocked(); |
453 | ||
454 | ifstream file; | |
9064d73b | 455 | file.open(exportfilepath.string().c_str(), std::ios::in | std::ios::ate); |
60f762a5 S |
456 | BOOST_CHECK(file.is_open()); |
457 | bool fVerified = false; | |
458 | int64_t nFilesize = std::max((int64_t)1, (int64_t)file.tellg()); | |
459 | file.seekg(0, file.beg); | |
460 | while (file.good()) { | |
461 | std::string line; | |
462 | std::getline(file, line); | |
463 | if (line.empty() || line[0] == '#') | |
464 | continue; | |
465 | if (line.find(s1) != std::string::npos && line.find(s2) != std::string::npos) { | |
466 | fVerified = true; | |
467 | break; | |
468 | } | |
469 | } | |
470 | BOOST_CHECK(fVerified); | |
471 | } | |
472 | ||
473 | ||
474 | /* | |
475 | * This test covers RPC command z_importwallet | |
476 | */ | |
477 | BOOST_AUTO_TEST_CASE(rpc_wallet_z_importwallet) | |
478 | { | |
479 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
06c19063 | 480 | |
60f762a5 S |
481 | // error if no args |
482 | BOOST_CHECK_THROW(CallRPC("z_importwallet"), runtime_error); | |
483 | ||
e346a0b3 S |
484 | // error if too many args |
485 | BOOST_CHECK_THROW(CallRPC("z_importwallet toomany args"), runtime_error); | |
486 | ||
60f762a5 | 487 | // create a random key locally |
e5eab182 | 488 | auto testSpendingKey = libzcash::SproutSpendingKey::random(); |
60f762a5 | 489 | auto testPaymentAddress = testSpendingKey.address(); |
80ed13d5 | 490 | std::string testAddr = EncodePaymentAddress(testPaymentAddress); |
472f75bc | 491 | std::string testKey = EncodeSpendingKey(testSpendingKey); |
06c19063 | 492 | |
60f762a5 | 493 | // create test data using the random key |
2e500f50 | 494 | std::string format_str = "# Wallet dump created by Komodo v0.11.2.0.z8-9155cc6-dirty (2016-08-11 11:37:00 -0700)\n" |
60f762a5 S |
495 | "# * Created on 2016-08-12T21:55:36Z\n" |
496 | "# * Best block at time of backup was 0 (0de0a3851fef2d433b9b4f51d4342bdd24c5ddd793eb8fba57189f07e9235d52),\n" | |
497 | "# mined on 2009-01-03T18:15:05Z\n" | |
498 | "\n" | |
499 | "# Zkeys\n" | |
500 | "\n" | |
501 | "%s 2016-08-12T21:55:36Z # zaddr=%s\n" | |
502 | "\n" | |
503 | "\n# End of dump"; | |
06c19063 | 504 | |
60f762a5 S |
505 | boost::format formatobject(format_str); |
506 | std::string testWalletDump = (formatobject % testKey % testAddr).str(); | |
06c19063 | 507 | |
60f762a5 S |
508 | // write test data to file |
509 | boost::filesystem::path temp = boost::filesystem::temp_directory_path() / | |
510 | boost::filesystem::unique_path(); | |
c5b26aca | 511 | const std::string path = temp.string(); |
60f762a5 S |
512 | std::ofstream file(path); |
513 | file << testWalletDump; | |
514 | file << std::flush; | |
515 | ||
516 | // wallet should currently be empty | |
e5eab182 | 517 | std::set<libzcash::SproutPaymentAddress> addrs; |
25d5e80c | 518 | pwalletMain->GetSproutPaymentAddresses(addrs); |
60f762a5 | 519 | BOOST_CHECK(addrs.size()==0); |
06c19063 | 520 | |
60f762a5 S |
521 | // import test data from file into wallet |
522 | BOOST_CHECK_NO_THROW(CallRPC(string("z_importwallet ") + path)); | |
06c19063 | 523 | |
60f762a5 | 524 | // wallet should now have one zkey |
25d5e80c | 525 | pwalletMain->GetSproutPaymentAddresses(addrs); |
60f762a5 | 526 | BOOST_CHECK(addrs.size()==1); |
06c19063 | 527 | |
60f762a5 | 528 | // check that we have the spending key for the address |
e5eab182 JG |
529 | auto address = DecodePaymentAddress(testAddr); |
530 | BOOST_CHECK(IsValidPaymentAddress(address)); | |
531 | BOOST_ASSERT(boost::get<libzcash::SproutPaymentAddress>(&address) != nullptr); | |
532 | auto addr = boost::get<libzcash::SproutPaymentAddress>(address); | |
25d5e80c | 533 | BOOST_CHECK(pwalletMain->HaveSproutSpendingKey(addr)); |
06c19063 | 534 | |
4b2e5571 | 535 | // Verify the spending key is the same as the test data |
e5eab182 | 536 | libzcash::SproutSpendingKey k; |
25d5e80c | 537 | BOOST_CHECK(pwalletMain->GetSproutSpendingKey(addr, k)); |
472f75bc | 538 | BOOST_CHECK_EQUAL(testKey, EncodeSpendingKey(k)); |
60f762a5 S |
539 | } |
540 | ||
541 | ||
542 | /* | |
4b2e5571 | 543 | * This test covers RPC commands z_listaddresses, z_importkey, z_exportkey |
60f762a5 S |
544 | */ |
545 | BOOST_AUTO_TEST_CASE(rpc_wallet_z_importexport) | |
546 | { | |
547 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
0d37ae3a | 548 | UniValue retValue; |
60f762a5 S |
549 | int n1 = 1000; // number of times to import/export |
550 | int n2 = 1000; // number of addresses to create and list | |
06c19063 | 551 | |
60f762a5 | 552 | // error if no args |
06c19063 S |
553 | BOOST_CHECK_THROW(CallRPC("z_importkey"), runtime_error); |
554 | BOOST_CHECK_THROW(CallRPC("z_exportkey"), runtime_error); | |
60f762a5 | 555 | |
e346a0b3 | 556 | // error if too many args |
a31ba7a0 | 557 | BOOST_CHECK_THROW(CallRPC("z_importkey way too many args"), runtime_error); |
e346a0b3 S |
558 | BOOST_CHECK_THROW(CallRPC("z_exportkey toomany args"), runtime_error); |
559 | ||
33589401 | 560 | // error if invalid args |
e5eab182 | 561 | auto sk = libzcash::SproutSpendingKey::random(); |
472f75bc | 562 | std::string prefix = std::string("z_importkey ") + EncodeSpendingKey(sk) + " yes "; |
33589401 JG |
563 | BOOST_CHECK_THROW(CallRPC(prefix + "-1"), runtime_error); |
564 | BOOST_CHECK_THROW(CallRPC(prefix + "2147483647"), runtime_error); // allowed, but > height of active chain tip | |
565 | BOOST_CHECK_THROW(CallRPC(prefix + "2147483648"), runtime_error); // not allowed, > int32 used for nHeight | |
566 | BOOST_CHECK_THROW(CallRPC(prefix + "100badchars"), runtime_error); | |
567 | ||
60f762a5 | 568 | // wallet should currently be empty |
e5eab182 | 569 | std::set<libzcash::SproutPaymentAddress> addrs; |
25d5e80c | 570 | pwalletMain->GetSproutPaymentAddresses(addrs); |
60f762a5 | 571 | BOOST_CHECK(addrs.size()==0); |
40dc060c JG |
572 | std::set<libzcash::SaplingPaymentAddress> saplingAddrs; |
573 | pwalletMain->GetSaplingPaymentAddresses(saplingAddrs); | |
574 | BOOST_CHECK(saplingAddrs.empty()); | |
60f762a5 | 575 | |
8a1d1930 | 576 | auto m = GetTestMasterSaplingSpendingKey(); |
60f762a5 S |
577 | |
578 | // verify import and export key | |
579 | for (int i = 0; i < n1; i++) { | |
8dd1dbcf | 580 | // create a random Sprout key locally |
e5eab182 | 581 | auto testSpendingKey = libzcash::SproutSpendingKey::random(); |
60f762a5 | 582 | auto testPaymentAddress = testSpendingKey.address(); |
80ed13d5 | 583 | std::string testAddr = EncodePaymentAddress(testPaymentAddress); |
472f75bc | 584 | std::string testKey = EncodeSpendingKey(testSpendingKey); |
60f762a5 S |
585 | BOOST_CHECK_NO_THROW(CallRPC(string("z_importkey ") + testKey)); |
586 | BOOST_CHECK_NO_THROW(retValue = CallRPC(string("z_exportkey ") + testAddr)); | |
587 | BOOST_CHECK_EQUAL(retValue.get_str(), testKey); | |
70b4ad2d | 588 | |
8dd1dbcf | 589 | // create a random Sapling key locally |
70b4ad2d JG |
590 | auto testSaplingSpendingKey = m.Derive(i); |
591 | auto testSaplingPaymentAddress = testSaplingSpendingKey.DefaultAddress(); | |
8dd1dbcf JG |
592 | std::string testSaplingAddr = EncodePaymentAddress(testSaplingPaymentAddress); |
593 | std::string testSaplingKey = EncodeSpendingKey(testSaplingSpendingKey); | |
594 | BOOST_CHECK_NO_THROW(CallRPC(string("z_importkey ") + testSaplingKey)); | |
595 | BOOST_CHECK_NO_THROW(retValue = CallRPC(string("z_exportkey ") + testSaplingAddr)); | |
596 | BOOST_CHECK_EQUAL(retValue.get_str(), testSaplingKey); | |
60f762a5 S |
597 | } |
598 | ||
599 | // Verify we can list the keys imported | |
600 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); | |
0d37ae3a | 601 | UniValue arr = retValue.get_array(); |
40dc060c | 602 | BOOST_CHECK(arr.size() == (2 * n1)); |
60f762a5 S |
603 | |
604 | // Put addresses into a set | |
605 | std::unordered_set<std::string> myaddrs; | |
0d37ae3a | 606 | for (UniValue element : arr.getValues()) { |
60f762a5 S |
607 | myaddrs.insert(element.get_str()); |
608 | } | |
06c19063 | 609 | |
60f762a5 S |
610 | // Make new addresses for the set |
611 | for (int i=0; i<n2; i++) { | |
92fc29a3 | 612 | myaddrs.insert(EncodePaymentAddress(pwalletMain->GenerateNewSproutZKey())); |
60f762a5 S |
613 | } |
614 | ||
615 | // Verify number of addresses stored in wallet is n1+n2 | |
616 | int numAddrs = myaddrs.size(); | |
40dc060c | 617 | BOOST_CHECK(numAddrs == (2 * n1) + n2); |
25d5e80c | 618 | pwalletMain->GetSproutPaymentAddresses(addrs); |
40dc060c JG |
619 | pwalletMain->GetSaplingPaymentAddresses(saplingAddrs); |
620 | BOOST_CHECK(addrs.size() + saplingAddrs.size() == numAddrs); | |
06c19063 | 621 | |
60f762a5 S |
622 | // Ask wallet to list addresses |
623 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); | |
624 | arr = retValue.get_array(); | |
625 | BOOST_CHECK(arr.size() == numAddrs); | |
06c19063 | 626 | |
4b2e5571 | 627 | // Create a set from them |
60f762a5 | 628 | std::unordered_set<std::string> listaddrs; |
0d37ae3a | 629 | for (UniValue element : arr.getValues()) { |
60f762a5 S |
630 | listaddrs.insert(element.get_str()); |
631 | } | |
06c19063 | 632 | |
60f762a5 S |
633 | // Verify the two sets of addresses are the same |
634 | BOOST_CHECK(listaddrs.size() == numAddrs); | |
635 | BOOST_CHECK(myaddrs == listaddrs); | |
badb9a9c S |
636 | |
637 | // Add one more address | |
471138a9 | 638 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_getnewaddress sprout")); |
badb9a9c | 639 | std::string newaddress = retValue.get_str(); |
e5eab182 JG |
640 | auto address = DecodePaymentAddress(newaddress); |
641 | BOOST_CHECK(IsValidPaymentAddress(address)); | |
642 | BOOST_ASSERT(boost::get<libzcash::SproutPaymentAddress>(&address) != nullptr); | |
643 | auto newAddr = boost::get<libzcash::SproutPaymentAddress>(address); | |
25d5e80c | 644 | BOOST_CHECK(pwalletMain->HaveSproutSpendingKey(newAddr)); |
e346a0b3 S |
645 | |
646 | // Check if too many args | |
647 | BOOST_CHECK_THROW(CallRPC("z_getnewaddress toomanyargs"), runtime_error); | |
60f762a5 | 648 | } |
b922924d S |
649 | |
650 | ||
651 | ||
652 | /** | |
fc4b127e | 653 | * Test Async RPC operations. |
b922924d S |
654 | * Tip: Create mock operations by subclassing AsyncRPCOperation. |
655 | */ | |
fc4b127e | 656 | |
b922924d S |
657 | class MockSleepOperation : public AsyncRPCOperation { |
658 | public: | |
659 | std::chrono::milliseconds naptime; | |
660 | MockSleepOperation(int t=1000) { | |
661 | this->naptime = std::chrono::milliseconds(t); | |
662 | } | |
663 | virtual ~MockSleepOperation() { | |
664 | } | |
665 | virtual void main() { | |
666 | set_state(OperationStatus::EXECUTING); | |
667 | start_execution_clock(); | |
668 | std::this_thread::sleep_for(std::chrono::milliseconds(naptime)); | |
669 | stop_execution_clock(); | |
0d37ae3a | 670 | set_result(UniValue(UniValue::VSTR, "done")); |
b922924d S |
671 | set_state(OperationStatus::SUCCESS); |
672 | } | |
673 | }; | |
674 | ||
675 | ||
676 | /* | |
677 | * Test Aysnc RPC queue and operations. | |
678 | */ | |
679 | BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations) | |
680 | { | |
681 | std::shared_ptr<AsyncRPCQueue> q = std::make_shared<AsyncRPCQueue>(); | |
682 | BOOST_CHECK(q->getNumberOfWorkers() == 0); | |
683 | std::vector<AsyncRPCOperationId> ids = q->getAllOperationIds(); | |
684 | BOOST_CHECK(ids.size()==0); | |
685 | ||
686 | std::shared_ptr<AsyncRPCOperation> op1 = std::make_shared<AsyncRPCOperation>(); | |
06c19063 | 687 | q->addOperation(op1); |
b922924d | 688 | BOOST_CHECK(q->getOperationCount() == 1); |
06c19063 | 689 | |
b922924d S |
690 | OperationStatus status = op1->getState(); |
691 | BOOST_CHECK(status == OperationStatus::READY); | |
06c19063 | 692 | |
b922924d S |
693 | AsyncRPCOperationId id1 = op1->getId(); |
694 | int64_t creationTime1 = op1->getCreationTime(); | |
06c19063 | 695 | |
b922924d S |
696 | q->addWorker(); |
697 | BOOST_CHECK(q->getNumberOfWorkers() == 1); | |
06c19063 S |
698 | |
699 | // an AsyncRPCOperation doesn't do anything so will finish immediately | |
b922924d S |
700 | std::this_thread::sleep_for(std::chrono::seconds(1)); |
701 | BOOST_CHECK(q->getOperationCount() == 0); | |
702 | ||
703 | // operation should be a success | |
704 | BOOST_CHECK_EQUAL(op1->isCancelled(), false); | |
705 | BOOST_CHECK_EQUAL(op1->isExecuting(), false); | |
706 | BOOST_CHECK_EQUAL(op1->isReady(), false); | |
707 | BOOST_CHECK_EQUAL(op1->isFailed(), false); | |
708 | BOOST_CHECK_EQUAL(op1->isSuccess(), true); | |
0d37ae3a JG |
709 | BOOST_CHECK_EQUAL(op1->getError().isNull(), true); |
710 | BOOST_CHECK_EQUAL(op1->getResult().isNull(), false); | |
b922924d S |
711 | BOOST_CHECK_EQUAL(op1->getStateAsString(), "success"); |
712 | BOOST_CHECK_NE(op1->getStateAsString(), "executing"); | |
06c19063 | 713 | |
b922924d S |
714 | // Create a second operation which just sleeps |
715 | std::shared_ptr<AsyncRPCOperation> op2(new MockSleepOperation(2500)); | |
716 | AsyncRPCOperationId id2 = op2->getId(); | |
717 | int64_t creationTime2 = op2->getCreationTime(); | |
718 | ||
719 | // it's different from the previous operation | |
720 | BOOST_CHECK_NE(op1.get(), op2.get()); | |
721 | BOOST_CHECK_NE(id1, id2); | |
722 | BOOST_CHECK_NE(creationTime1, creationTime2); | |
723 | ||
724 | // Only the first operation has been added to the queue | |
725 | std::vector<AsyncRPCOperationId> v = q->getAllOperationIds(); | |
726 | std::set<AsyncRPCOperationId> opids(v.begin(), v.end()); | |
727 | BOOST_CHECK(opids.size() == 1); | |
728 | BOOST_CHECK(opids.count(id1)==1); | |
729 | BOOST_CHECK(opids.count(id2)==0); | |
730 | std::shared_ptr<AsyncRPCOperation> p1 = q->getOperationForId(id1); | |
731 | BOOST_CHECK_EQUAL(p1.get(), op1.get()); | |
732 | std::shared_ptr<AsyncRPCOperation> p2 = q->getOperationForId(id2); | |
733 | BOOST_CHECK(!p2); // null ptr as not added to queue yet | |
734 | ||
735 | // Add operation 2 and 3 to the queue | |
736 | q->addOperation(op2); | |
737 | std::shared_ptr<AsyncRPCOperation> op3(new MockSleepOperation(1000)); | |
738 | q->addOperation(op3); | |
739 | std::this_thread::sleep_for(std::chrono::milliseconds(500)); | |
740 | BOOST_CHECK_EQUAL(op2->isExecuting(), true); | |
741 | op2->cancel(); // too late, already executing | |
742 | op3->cancel(); | |
743 | std::this_thread::sleep_for(std::chrono::milliseconds(3000)); | |
744 | BOOST_CHECK_EQUAL(op2->isSuccess(), true); | |
745 | BOOST_CHECK_EQUAL(op2->isCancelled(), false); | |
746 | BOOST_CHECK_EQUAL(op3->isCancelled(), true); | |
06c19063 S |
747 | |
748 | ||
b922924d S |
749 | v = q->getAllOperationIds(); |
750 | std::copy( v.begin(), v.end(), std::inserter( opids, opids.end() ) ); | |
751 | BOOST_CHECK(opids.size() == 3); | |
752 | BOOST_CHECK(opids.count(id1)==1); | |
753 | BOOST_CHECK(opids.count(id2)==1); | |
754 | BOOST_CHECK(opids.count(op3->getId())==1); | |
755 | q->finishAndWait(); | |
756 | } | |
757 | ||
758 | ||
759 | // The CountOperation will increment this global | |
760 | std::atomic<int64_t> gCounter(0); | |
761 | ||
762 | class CountOperation : public AsyncRPCOperation { | |
763 | public: | |
764 | CountOperation() {} | |
765 | virtual ~CountOperation() {} | |
06c19063 | 766 | virtual void main() { |
b922924d S |
767 | set_state(OperationStatus::EXECUTING); |
768 | gCounter++; | |
769 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); | |
770 | set_state(OperationStatus::SUCCESS); | |
771 | } | |
772 | }; | |
773 | ||
774 | // This tests the queue waiting for multiple workers to finish | |
775 | BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations_parallel_wait) | |
776 | { | |
777 | gCounter = 0; | |
06c19063 | 778 | |
b922924d S |
779 | std::shared_ptr<AsyncRPCQueue> q = std::make_shared<AsyncRPCQueue>(); |
780 | q->addWorker(); | |
781 | q->addWorker(); | |
782 | q->addWorker(); | |
783 | q->addWorker(); | |
784 | BOOST_CHECK(q->getNumberOfWorkers() == 4); | |
785 | ||
786 | int64_t numOperations = 10; // 10 * 1000ms / 4 = 2.5 secs to finish | |
787 | for (int i=0; i<numOperations; i++) { | |
788 | std::shared_ptr<AsyncRPCOperation> op(new CountOperation()); | |
789 | q->addOperation(op); | |
790 | } | |
791 | ||
792 | std::vector<AsyncRPCOperationId> ids = q->getAllOperationIds(); | |
793 | BOOST_CHECK(ids.size()==numOperations); | |
794 | q->finishAndWait(); | |
795 | BOOST_CHECK_EQUAL(q->isFinishing(), true); | |
796 | BOOST_CHECK_EQUAL(numOperations, gCounter.load()); | |
797 | } | |
798 | ||
799 | // This tests the queue shutting down immediately | |
800 | BOOST_AUTO_TEST_CASE(rpc_wallet_async_operations_parallel_cancel) | |
801 | { | |
802 | gCounter = 0; | |
06c19063 | 803 | |
b922924d S |
804 | std::shared_ptr<AsyncRPCQueue> q = std::make_shared<AsyncRPCQueue>(); |
805 | q->addWorker(); | |
806 | q->addWorker(); | |
807 | BOOST_CHECK(q->getNumberOfWorkers() == 2); | |
808 | ||
fc4b127e | 809 | int numOperations = 10000; // 10000 seconds to complete |
b922924d S |
810 | for (int i=0; i<numOperations; i++) { |
811 | std::shared_ptr<AsyncRPCOperation> op(new CountOperation()); | |
812 | q->addOperation(op); | |
813 | } | |
814 | std::vector<AsyncRPCOperationId> ids = q->getAllOperationIds(); | |
815 | BOOST_CHECK(ids.size()==numOperations); | |
816 | q->closeAndWait(); | |
b922924d S |
817 | |
818 | int numSuccess = 0; | |
06c19063 | 819 | int numCancelled = 0; |
b922924d S |
820 | for (auto & id : ids) { |
821 | std::shared_ptr<AsyncRPCOperation> ptr = q->popOperationForId(id); | |
822 | if (ptr->isCancelled()) { | |
823 | numCancelled++; | |
824 | } else if (ptr->isSuccess()) { | |
825 | numSuccess++; | |
826 | } | |
827 | } | |
06c19063 | 828 | |
b922924d S |
829 | BOOST_CHECK_EQUAL(numOperations, numSuccess+numCancelled); |
830 | BOOST_CHECK_EQUAL(gCounter.load(), numSuccess); | |
831 | BOOST_CHECK(q->getOperationCount() == 0); | |
832 | ids = q->getAllOperationIds(); | |
833 | BOOST_CHECK(ids.size()==0); | |
834 | } | |
835 | ||
fc4b127e S |
836 | // This tests z_getoperationstatus, z_getoperationresult, z_listoperationids |
837 | BOOST_AUTO_TEST_CASE(rpc_z_getoperations) | |
838 | { | |
839 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); | |
840 | std::shared_ptr<AsyncRPCQueue> sharedInstance = AsyncRPCQueue::sharedInstance(); | |
841 | BOOST_CHECK(q == sharedInstance); | |
842 | ||
843 | BOOST_CHECK_NO_THROW(CallRPC("z_getoperationstatus")); | |
844 | BOOST_CHECK_NO_THROW(CallRPC("z_getoperationstatus []")); | |
845 | BOOST_CHECK_NO_THROW(CallRPC("z_getoperationstatus [\"opid-1234\"]")); | |
846 | BOOST_CHECK_THROW(CallRPC("z_getoperationstatus [] toomanyargs"), runtime_error); | |
847 | BOOST_CHECK_THROW(CallRPC("z_getoperationstatus not_an_array"), runtime_error); | |
848 | ||
849 | BOOST_CHECK_NO_THROW(CallRPC("z_getoperationresult")); | |
850 | BOOST_CHECK_NO_THROW(CallRPC("z_getoperationresult []")); | |
851 | BOOST_CHECK_NO_THROW(CallRPC("z_getoperationresult [\"opid-1234\"]")); | |
852 | BOOST_CHECK_THROW(CallRPC("z_getoperationresult [] toomanyargs"), runtime_error); | |
853 | BOOST_CHECK_THROW(CallRPC("z_getoperationresult not_an_array"), runtime_error); | |
06c19063 | 854 | |
fc4b127e S |
855 | std::shared_ptr<AsyncRPCOperation> op1 = std::make_shared<AsyncRPCOperation>(); |
856 | q->addOperation(op1); | |
857 | std::shared_ptr<AsyncRPCOperation> op2 = std::make_shared<AsyncRPCOperation>(); | |
858 | q->addOperation(op2); | |
06c19063 | 859 | |
fc4b127e S |
860 | BOOST_CHECK(q->getOperationCount() == 2); |
861 | BOOST_CHECK(q->getNumberOfWorkers() == 0); | |
862 | q->addWorker(); | |
863 | BOOST_CHECK(q->getNumberOfWorkers() == 1); | |
864 | std::this_thread::sleep_for(std::chrono::milliseconds(1000)); | |
865 | BOOST_CHECK(q->getOperationCount() == 0); | |
06c19063 | 866 | |
e346a0b3 S |
867 | // Check if too many args |
868 | BOOST_CHECK_THROW(CallRPC("z_listoperationids toomany args"), runtime_error); | |
869 | ||
0d37ae3a | 870 | UniValue retValue; |
fc4b127e S |
871 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listoperationids")); |
872 | BOOST_CHECK(retValue.get_array().size() == 2); | |
873 | ||
874 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_getoperationstatus")); | |
0d37ae3a | 875 | UniValue array = retValue.get_array(); |
fc4b127e S |
876 | BOOST_CHECK(array.size() == 2); |
877 | ||
878 | // idempotent | |
879 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_getoperationstatus")); | |
880 | array = retValue.get_array(); | |
06c19063 S |
881 | BOOST_CHECK(array.size() == 2); |
882 | ||
0d37ae3a JG |
883 | for (UniValue v : array.getValues()) { |
884 | UniValue obj = v.get_obj(); | |
885 | UniValue id = find_value(obj, "id"); | |
06c19063 | 886 | |
0d37ae3a | 887 | UniValue result; |
fc4b127e S |
888 | // removes result from internal storage |
889 | BOOST_CHECK_NO_THROW(result = CallRPC("z_getoperationresult [\"" + id.get_str() + "\"]")); | |
0d37ae3a | 890 | UniValue resultArray = result.get_array(); |
fc4b127e | 891 | BOOST_CHECK(resultArray.size() == 1); |
06c19063 | 892 | |
0d37ae3a JG |
893 | UniValue resultObj = resultArray[0].get_obj(); |
894 | UniValue resultId = find_value(resultObj, "id"); | |
fc4b127e | 895 | BOOST_CHECK_EQUAL(id.get_str(), resultId.get_str()); |
06c19063 S |
896 | |
897 | // verify the operation has been removed | |
da5e7e51 S |
898 | BOOST_CHECK_NO_THROW(result = CallRPC("z_getoperationresult [\"" + id.get_str() + "\"]")); |
899 | resultArray = result.get_array(); | |
900 | BOOST_CHECK(resultArray.size() == 0); | |
fc4b127e | 901 | } |
06c19063 | 902 | |
fc4b127e S |
903 | // operations removed |
904 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_getoperationstatus")); | |
905 | array = retValue.get_array(); | |
906 | BOOST_CHECK(array.size() == 0); | |
907 | ||
908 | q->close(); | |
909 | } | |
910 | ||
911 | BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) | |
912 | { | |
913 | SelectParams(CBaseChainParams::TESTNET); | |
914 | ||
915 | LOCK(pwalletMain->cs_wallet); | |
916 | ||
917 | BOOST_CHECK_THROW(CallRPC("z_sendmany"), runtime_error); | |
918 | BOOST_CHECK_THROW(CallRPC("z_sendmany toofewargs"), runtime_error); | |
af53da02 | 919 | BOOST_CHECK_THROW(CallRPC("z_sendmany just too many args here"), runtime_error); |
fc4b127e S |
920 | |
921 | // bad from address | |
da5e7e51 | 922 | BOOST_CHECK_THROW(CallRPC("z_sendmany " |
ee64b5e7 | 923 | "INVALIDtmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ []"), runtime_error); |
fc4b127e | 924 | // empty amounts |
da5e7e51 | 925 | BOOST_CHECK_THROW(CallRPC("z_sendmany " |
ee64b5e7 | 926 | "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ []"), runtime_error); |
fc4b127e S |
927 | |
928 | // don't have the spending key for this address | |
da5e7e51 S |
929 | BOOST_CHECK_THROW(CallRPC("z_sendmany " |
930 | "tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB" | |
931 | "UkJ1oSfbhTJhm72WiZizvkZz5aH1 []"), runtime_error); | |
fc4b127e S |
932 | |
933 | // duplicate address | |
da5e7e51 | 934 | BOOST_CHECK_THROW(CallRPC("z_sendmany " |
ee64b5e7 JG |
935 | "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " |
936 | "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":50.0}," | |
937 | " {\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":12.0} ]" | |
da5e7e51 | 938 | ), runtime_error); |
fc4b127e | 939 | |
af53da02 S |
940 | // invalid fee amount, cannot be negative |
941 | BOOST_CHECK_THROW(CallRPC("z_sendmany " | |
942 | "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " | |
943 | "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":50.0}] " | |
944 | "1 -0.0001" | |
945 | ), runtime_error); | |
946 | ||
947 | // invalid fee amount, bigger than MAX_MONEY | |
948 | BOOST_CHECK_THROW(CallRPC("z_sendmany " | |
949 | "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " | |
950 | "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":50.0}] " | |
951 | "1 21000001" | |
952 | ), runtime_error); | |
953 | ||
954 | // fee amount is bigger than sum of outputs | |
955 | BOOST_CHECK_THROW(CallRPC("z_sendmany " | |
956 | "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " | |
957 | "[{\"address\":\"tmQP9L3s31cLsghVYf2Jb5MhKj1jRBPoeQn\", \"amount\":50.0}] " | |
958 | "1 50.00000001" | |
959 | ), runtime_error); | |
960 | ||
fc4b127e | 961 | // memo bigger than allowed length of ZC_MEMO_SIZE |
cff6f0ac S |
962 | std::vector<char> v (2 * (ZC_MEMO_SIZE+1)); // x2 for hexadecimal string format |
963 | std::fill(v.begin(),v.end(), 'A'); | |
fc4b127e | 964 | std::string badmemo(v.begin(), v.end()); |
92fc29a3 | 965 | auto pa = pwalletMain->GenerateNewSproutZKey(); |
80ed13d5 | 966 | std::string zaddr1 = EncodePaymentAddress(pa); |
ee64b5e7 | 967 | BOOST_CHECK_THROW(CallRPC(string("z_sendmany tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ ") |
da5e7e51 | 968 | + "[{\"address\":\"" + zaddr1 + "\", \"amount\":123.456}]"), runtime_error); |
06c19063 | 969 | |
072099d7 S |
970 | // Mutable tx containing contextual information we need to build tx |
971 | UniValue retValue = CallRPC("getblockcount"); | |
972 | int nHeight = retValue.get_int(); | |
973 | CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1); | |
974 | if (mtx.nVersion == 1) { | |
975 | mtx.nVersion = 2; | |
976 | } | |
977 | ||
06c19063 | 978 | // Test constructor of AsyncRPCOperation_sendmany |
fc4b127e | 979 | try { |
36e2141d | 980 | std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(boost::none, mtx, "",{}, {}, -1)); |
0d37ae3a | 981 | } catch (const UniValue& objError) { |
da5e7e51 | 982 | BOOST_CHECK( find_error(objError, "Minconf cannot be negative")); |
fc4b127e S |
983 | } |
984 | ||
985 | try { | |
36e2141d | 986 | std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(boost::none, mtx, "",{}, {}, 1)); |
0d37ae3a | 987 | } catch (const UniValue& objError) { |
da5e7e51 | 988 | BOOST_CHECK( find_error(objError, "From address parameter missing")); |
fc4b127e S |
989 | } |
990 | ||
991 | try { | |
36e2141d | 992 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(boost::none, mtx, "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ", {}, {}, 1) ); |
0d37ae3a | 993 | } catch (const UniValue& objError) { |
da5e7e51 | 994 | BOOST_CHECK( find_error(objError, "No recipients")); |
fc4b127e S |
995 | } |
996 | ||
997 | try { | |
eed98728 | 998 | std::vector<SendManyRecipient> recipients = { SendManyRecipient("dummy",1.0, "", CScript()) }; |
36e2141d | 999 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(boost::none, mtx, "INVALID", recipients, {}, 1) ); |
0d37ae3a | 1000 | } catch (const UniValue& objError) { |
80ed13d5 | 1001 | BOOST_CHECK( find_error(objError, "Invalid from address")); |
fc4b127e S |
1002 | } |
1003 | ||
f92f0047 | 1004 | // Testnet payment addresses begin with 'zt'. This test detects an incorrect prefix. |
fc4b127e | 1005 | try { |
eed98728 | 1006 | std::vector<SendManyRecipient> recipients = { SendManyRecipient("dummy",1.0, "", CScript()) }; |
36e2141d | 1007 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(boost::none, mtx, "zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U", recipients, {}, 1) ); |
0d37ae3a | 1008 | } catch (const UniValue& objError) { |
80ed13d5 | 1009 | BOOST_CHECK( find_error(objError, "Invalid from address")); |
fc4b127e S |
1010 | } |
1011 | ||
1012 | // Note: The following will crash as a google test because AsyncRPCOperation_sendmany | |
1013 | // invokes a method on pwalletMain, which is undefined in the google test environment. | |
1014 | try { | |
eed98728 | 1015 | std::vector<SendManyRecipient> recipients = { SendManyRecipient("dummy",1.0, "", CScript()) }; |
36e2141d | 1016 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(boost::none, mtx, "ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP", recipients, {}, 1) ); |
0d37ae3a | 1017 | } catch (const UniValue& objError) { |
da5e7e51 | 1018 | BOOST_CHECK( find_error(objError, "no spending key found for zaddr")); |
fc4b127e S |
1019 | } |
1020 | } | |
1021 | ||
d3d8fb72 E |
1022 | BOOST_AUTO_TEST_CASE(asyncrpcoperation_sign_send_raw_transaction) { |
1023 | // Raw joinsplit is a zaddr->zaddr | |
1024 | std::string raw = "020000000000000000000100000000000000001027000000000000183a0d4c46c369078705e39bcfebee59a978dbd210ce8de3efc9555a03fbabfd3cea16693d730c63850d7e48ccde79854c19adcb7e9dcd7b7d18805ee09083f6b16e1860729d2d4a90e2f2acd009cf78b5eb0f4a6ee4bdb64b1262d7ce9eb910c460b02022991e968d0c50ee44908e4ccccbc591d0053bcca154dd6d6fc400a29fa686af4682339832ccea362a62aeb9df0d5aa74f86a1e75ac0f48a8ccc41e0a940643c6c33e1d09223b0a46eaf47a1bb4407cfc12b1dcf83a29c0cef51e45c7876ca5b9e5bae86d92976eb3ef68f29cd29386a8be8451b50f82bf9da10c04651868655194da8f6ed3d241bb5b5ff93a3e2bbe44644544d88bcde5cc35978032ee92699c7a61fcbb395e7583f47e698c4d53ede54f956629400bf510fb5e22d03158cc10bdcaaf29e418ef18eb6480dd9c8b9e2a377809f9f32a556ef872febd0021d4ad013aa9f0b7255e98e408d302abefd33a71180b720271835b487ab309e160b06dfe51932120fb84a7ede16b20c53599a11071592109e10260f265ee60d48c62bfe24074020e9b586ce9e9356e68f2ad1a9538258234afe4b83a209f178f45202270eaeaeecaf2ce3100b2c5a714f75f35777a9ebff5ebf47059d2bbf6f3726190216468f2b152673b766225b093f3a2f827c86d6b48b42117fec1d0ac38dd7af700308dcfb02eba821612b16a2c164c47715b9b0c93900893b1aba2ea03765c94d87022db5be06ab338d1912e0936dfe87586d0a8ee49144a6cd2e306abdcb652faa3e0222739deb23154d778b50de75069a4a2cce1208cd1ced3cb4744c9888ce1c2fcd2e66dc31e62d3aa9e423d7275882525e9981f92e84ac85975b8660739407efbe1e34c2249420fde7e17db3096d5b22e83d051d01f0e6e7690dca7d168db338aadf0897fedac10de310db2b1bff762d322935dddbb60c2efb8b15d231fa17b84630371cb275c209f0c4c7d0c68b150ea5cd514122215e3f7fcfb351d69514788d67c2f3c8922581946e3a04bdf1f07f15696ca76eb95b10698bf1188fd882945c57657515889d042a6fc45d38cbc943540c4f0f6d1c45a1574c81f3e42d1eb8702328b729909adee8a5cfed7c79d54627d1fd389af941d878376f7927b9830ca659bf9ab18c5ca5192d52d02723008728d03701b8ab3e1c4a3109409ec0b13df334c7deec3523eeef4c97b5603e643de3a647b873f4c1b47fbfc6586ba66724f112e51fc93839648005043620aa3ce458e246d77977b19c53d98e3e812de006afc1a79744df236582943631d04cc02941ac4be500e4ed9fb9e3e7cc187b1c4050fad1d9d09d5fd70d5d01d615b439d8c0015d2eb10398bcdbf8c4b2bd559dbe4c288a186aed3f86f608da4d582e120c4a896e015e2241900d1daeccd05db968852677c71d752bec46de9962174b46f980e8cc603654daf8b98a3ee92dac066033954164a89568b70b1780c2ce2410b2f816dbeddb2cd463e0c8f21a52cf6427d9647a6fd4bafa8fb4cd4d47ac057b0160bee86c6b2fb8adce214c2bcdda277512200adf0eaa5d2114a2c077b009836a68ec254bfe56f51d147b9afe2ddd9cb917c0c2de19d81b7b8fd9f4574f51fa1207630dc13976f4d7587c962f761af267de71f3909a576e6bedaf6311633910d291ac292c467cc8331ef577aef7646a5d949322fa0367a49f20597a13def53136ee31610395e3e48d291fd8f58504374031fe9dcfba5e06086ebcf01a9106f6a4d6e16e19e4c5bb893f7da79419c94eca31a384be6fa1747284dee0fc3bbc8b1b860172c10b29c1594bb8c747d7fe05827358ff2160f49050001625ffe2e880bd7fc26cd0ffd89750745379a8e862816e08a5a2008043921ab6a4976064ac18f7ee37b6628bc0127d8d5ebd3548e41d8881a082d86f20b32e33094f15a0e6ea6074b08c6cd28142de94713451640a55985051f5577eb54572699d838cb34a79c8939e981c0c277d06a6e2ce69ccb74f8a691ff08f81d8b99e6a86223d29a2b7c8e7b041aba44ea678ae654277f7e91cbfa79158b989164a3d549d9f4feb0cc43169699c13e321fe3f4b94258c75d198ff9184269cd6986c55409e07528c93f64942c6c283ce3917b4bf4c3be2fe3173c8c38cccb35f1fbda0ca88b35a599c0678cb22aa8eabea8249dbd2e4f849fffe69803d299e435ebcd7df95854003d8eda17a74d98b4be0e62d45d7fe48c06a6f464a14f8e0570077cc631279092802a89823f031eef5e1028a6d6fdbd502869a731ee7d28b4d6c71b419462a30d31442d3ee444ffbcbd16d558c9000c97e949c2b1f9d6f6d8db7b9131ebd963620d3fc8595278d6f8fdf49084325373196d53e64142fa5a23eccd6ef908c4d80b8b3e6cc334b7f7012103a3682e4678e9b518163d262a39a2c1a69bf88514c52b7ccd7cc8dc80e71f7c2ec0701cff982573eb0c2c4daeb47fa0b586f4451c10d1da2e5d182b03dd067a5e971b3a6138ca6667aaf853d2ac03b80a1d5870905f2cfb6c78ec3c3719c02f973d638a0f973424a2b0f2b0023f136d60092fe15fba4bc180b9176bd0ff576e053f1af6939fe9ca256203ffaeb3e569f09774d2a6cbf91873e56651f4d6ff77e0b5374b0a1a201d7e523604e0247644544cc571d48c458a4f96f45580b"; | |
1025 | UniValue obj(UniValue::VOBJ); | |
1026 | obj.push_back(Pair("rawtxn", raw)); | |
1027 | // Verify test mode is returning output (since no input taddrs, signed and unsigned are the same). | |
27f91abf | 1028 | std::pair<CTransaction, UniValue> txAndResult = SignSendRawTransaction(obj, boost::none, true); |
d3d8fb72 E |
1029 | UniValue resultObj = txAndResult.second.get_obj(); |
1030 | std::string hex = find_value(resultObj, "hex").get_str(); | |
1031 | BOOST_CHECK_EQUAL(hex, raw); | |
1032 | } | |
cff6f0ac | 1033 | |
fc4b127e S |
1034 | // TODO: test private methods |
1035 | BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) | |
1036 | { | |
1037 | SelectParams(CBaseChainParams::TESTNET); | |
1038 | ||
1039 | LOCK(pwalletMain->cs_wallet); | |
1040 | ||
0d37ae3a | 1041 | UniValue retValue; |
06c19063 | 1042 | |
072099d7 S |
1043 | // Mutable tx containing contextual information we need to build tx |
1044 | retValue = CallRPC("getblockcount"); | |
1045 | int nHeight = retValue.get_int(); | |
1046 | CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1); | |
1047 | if (mtx.nVersion == 1) { | |
1048 | mtx.nVersion = 2; | |
1049 | } | |
1050 | ||
fc4b127e S |
1051 | // add keys manually |
1052 | BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress")); | |
1053 | std::string taddr1 = retValue.get_str(); | |
92fc29a3 | 1054 | auto pa = pwalletMain->GenerateNewSproutZKey(); |
80ed13d5 | 1055 | std::string zaddr1 = EncodePaymentAddress(pa); |
06c19063 | 1056 | |
fc4b127e S |
1057 | // there are no utxos to spend |
1058 | { | |
eed98728 | 1059 | std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1,100.0, "DEADBEEF", CScript()) }; |
36e2141d | 1060 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(boost::none, mtx, taddr1, {}, recipients, 1) ); |
fc4b127e S |
1061 | operation->main(); |
1062 | BOOST_CHECK(operation->isFailed()); | |
1063 | std::string msg = operation->getErrorMessage(); | |
1064 | BOOST_CHECK( msg.find("Insufficient funds, no UTXOs found") != string::npos); | |
1065 | } | |
b639bb1e S |
1066 | |
1067 | // minconf cannot be zero when sending from zaddr | |
1068 | { | |
1069 | try { | |
eed98728 | 1070 | std::vector<SendManyRecipient> recipients = {SendManyRecipient(taddr1, 100.0, "DEADBEEF", CScript())}; |
36e2141d | 1071 | std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_sendmany(boost::none, mtx, zaddr1, recipients, {}, 0)); |
b639bb1e S |
1072 | BOOST_CHECK(false); // Fail test if an exception is not thrown |
1073 | } catch (const UniValue& objError) { | |
1074 | BOOST_CHECK(find_error(objError, "Minconf cannot be zero when sending from zaddr")); | |
1075 | } | |
1076 | } | |
06c19063 | 1077 | |
b639bb1e | 1078 | |
fc4b127e S |
1079 | // there are no unspent notes to spend |
1080 | { | |
eed98728 | 1081 | std::vector<SendManyRecipient> recipients = { SendManyRecipient(taddr1,100.0, "DEADBEEF", CScript()) }; |
36e2141d | 1082 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(boost::none, mtx, zaddr1, recipients, {}, 1) ); |
fc4b127e S |
1083 | operation->main(); |
1084 | BOOST_CHECK(operation->isFailed()); | |
1085 | std::string msg = operation->getErrorMessage(); | |
1086 | BOOST_CHECK( msg.find("Insufficient funds, no unspent notes") != string::npos); | |
1087 | } | |
1088 | ||
cff6f0ac S |
1089 | // get_memo_from_hex_string()) |
1090 | { | |
eed98728 | 1091 | std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1,100.0, "DEADBEEF", CScript()) }; |
36e2141d | 1092 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(boost::none, mtx, zaddr1, recipients, {}, 1) ); |
cff6f0ac S |
1093 | std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation); |
1094 | TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); | |
06c19063 | 1095 | |
cff6f0ac | 1096 | std::string memo = "DEADBEEF"; |
a6bbb26e | 1097 | std::array<unsigned char, ZC_MEMO_SIZE> array = proxy.get_memo_from_hex_string(memo); |
cff6f0ac S |
1098 | BOOST_CHECK_EQUAL(array[0], 0xDE); |
1099 | BOOST_CHECK_EQUAL(array[1], 0xAD); | |
1100 | BOOST_CHECK_EQUAL(array[2], 0xBE); | |
1101 | BOOST_CHECK_EQUAL(array[3], 0xEF); | |
1102 | for (int i=4; i<ZC_MEMO_SIZE; i++) { | |
1103 | BOOST_CHECK_EQUAL(array[i], 0x00); // zero padding | |
1104 | } | |
06c19063 | 1105 | |
cff6f0ac S |
1106 | // memo is longer than allowed |
1107 | std::vector<char> v (2 * (ZC_MEMO_SIZE+1)); | |
1108 | std::fill(v.begin(),v.end(), 'A'); | |
1109 | std::string bigmemo(v.begin(), v.end()); | |
06c19063 | 1110 | |
cff6f0ac S |
1111 | try { |
1112 | proxy.get_memo_from_hex_string(bigmemo); | |
0d37ae3a | 1113 | } catch (const UniValue& objError) { |
da5e7e51 | 1114 | BOOST_CHECK( find_error(objError, "too big")); |
cff6f0ac | 1115 | } |
06c19063 | 1116 | |
cff6f0ac S |
1117 | // invalid hexadecimal string |
1118 | std::fill(v.begin(),v.end(), '@'); // not a hex character | |
1119 | std::string badmemo(v.begin(), v.end()); | |
06c19063 | 1120 | |
cff6f0ac S |
1121 | try { |
1122 | proxy.get_memo_from_hex_string(badmemo); | |
0d37ae3a | 1123 | } catch (const UniValue& objError) { |
da5e7e51 | 1124 | BOOST_CHECK( find_error(objError, "hexadecimal format")); |
cff6f0ac | 1125 | } |
06c19063 | 1126 | |
cff6f0ac S |
1127 | // odd length hexadecimal string |
1128 | std::fill(v.begin(),v.end(), 'A'); | |
1129 | v.resize(v.size() - 1); | |
da5e7e51 | 1130 | assert(v.size() %2 == 1); // odd length |
cff6f0ac S |
1131 | std::string oddmemo(v.begin(), v.end()); |
1132 | try { | |
1133 | proxy.get_memo_from_hex_string(oddmemo); | |
0d37ae3a | 1134 | } catch (const UniValue& objError) { |
da5e7e51 | 1135 | BOOST_CHECK( find_error(objError, "hexadecimal format")); |
cff6f0ac S |
1136 | } |
1137 | } | |
06c19063 S |
1138 | |
1139 | ||
cff6f0ac S |
1140 | // add_taddr_change_output_to_tx() will append a vout to a raw transaction |
1141 | { | |
eed98728 | 1142 | std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1,100.0, "DEADBEEF", CScript()) }; |
36e2141d | 1143 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(boost::none, mtx, zaddr1, recipients, {}, 1) ); |
cff6f0ac S |
1144 | std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation); |
1145 | TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); | |
06c19063 | 1146 | |
cff6f0ac S |
1147 | CTransaction tx = proxy.getTx(); |
1148 | BOOST_CHECK(tx.vout.size() == 0); | |
06c19063 | 1149 | |
19bd2d0e | 1150 | CReserveKey keyChange(pwalletMain); |
bf69507c | 1151 | CAmount amount = AmountFromValue(ValueFromString("123.456")); |
19bd2d0e | 1152 | proxy.add_taddr_change_output_to_tx(keyChange, amount); |
cff6f0ac S |
1153 | tx = proxy.getTx(); |
1154 | BOOST_CHECK(tx.vout.size() == 1); | |
1155 | CTxOut out = tx.vout[0]; | |
1156 | BOOST_CHECK_EQUAL(out.nValue, amount); | |
bf69507c | 1157 | amount = AmountFromValue(ValueFromString("1.111")); |
19bd2d0e | 1158 | proxy.add_taddr_change_output_to_tx(keyChange, amount); |
cff6f0ac S |
1159 | tx = proxy.getTx(); |
1160 | BOOST_CHECK(tx.vout.size() == 2); | |
1161 | out = tx.vout[1]; | |
1162 | BOOST_CHECK_EQUAL(out.nValue, amount); | |
1163 | } | |
06c19063 | 1164 | |
cff6f0ac S |
1165 | // add_taddr_outputs_to_tx() will append many vouts to a raw transaction |
1166 | { | |
1167 | std::vector<SendManyRecipient> recipients = { | |
eed98728 | 1168 | SendManyRecipient("tmTGScYwiLMzHe4uGZtBYmuqoW4iEoYNMXt",CAmount(1.23), "", CScript()), |
1169 | SendManyRecipient("tmUSbHz3vxnwLvRyNDXbwkZxjVyDodMJEhh",CAmount(4.56), "", CScript()), | |
1170 | SendManyRecipient("tmYZAXYPCP56Xa5JQWWPZuK7o7bfUQW6kkd",CAmount(7.89), "", CScript()), | |
cff6f0ac | 1171 | }; |
36e2141d | 1172 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(boost::none, mtx, zaddr1, recipients, {}, 1) ); |
cff6f0ac S |
1173 | std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation); |
1174 | TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); | |
06c19063 | 1175 | |
cff6f0ac | 1176 | proxy.add_taddr_outputs_to_tx(); |
06c19063 | 1177 | |
cff6f0ac S |
1178 | CTransaction tx = proxy.getTx(); |
1179 | BOOST_CHECK(tx.vout.size() == 3); | |
1180 | BOOST_CHECK_EQUAL(tx.vout[0].nValue, CAmount(1.23)); | |
1181 | BOOST_CHECK_EQUAL(tx.vout[1].nValue, CAmount(4.56)); | |
1182 | BOOST_CHECK_EQUAL(tx.vout[2].nValue, CAmount(7.89)); | |
1183 | } | |
06c19063 | 1184 | |
7b79275e S |
1185 | // Test the perform_joinsplit methods. |
1186 | { | |
1187 | // Dummy input so the operation object can be instantiated. | |
eed98728 | 1188 | std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1, 0.0005, "ABCD", CScript()) }; |
06c19063 | 1189 | |
36e2141d | 1190 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(boost::none, mtx, zaddr1, {}, recipients, 1) ); |
7b79275e | 1191 | std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation); |
06c19063 | 1192 | TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); |
7b79275e S |
1193 | |
1194 | // Enable test mode so tx is not sent and proofs are not generated | |
1195 | static_cast<AsyncRPCOperation_sendmany *>(operation.get())->testmode = true; | |
06c19063 S |
1196 | |
1197 | AsyncJoinSplitInfo info; | |
8ea8ef98 | 1198 | std::vector<boost::optional < SproutWitness>> witnesses; |
7b79275e S |
1199 | uint256 anchor; |
1200 | try { | |
1201 | proxy.perform_joinsplit(info, witnesses, anchor); | |
1202 | } catch (const std::runtime_error & e) { | |
1203 | BOOST_CHECK( string(e.what()).find("anchor is null")!= string::npos); | |
1204 | } | |
1205 | ||
1206 | try { | |
1207 | std::vector<JSOutPoint> v; | |
1208 | proxy.perform_joinsplit(info, v); | |
1209 | } catch (const std::runtime_error & e) { | |
1210 | BOOST_CHECK( string(e.what()).find("anchor is null")!= string::npos); | |
1211 | } | |
1212 | ||
b230fe68 | 1213 | info.notes.push_back(SproutNote()); |
7b79275e S |
1214 | try { |
1215 | proxy.perform_joinsplit(info); | |
1216 | } catch (const std::runtime_error & e) { | |
1217 | BOOST_CHECK( string(e.what()).find("number of notes")!= string::npos); | |
1218 | } | |
06c19063 | 1219 | |
7b79275e S |
1220 | info.notes.clear(); |
1221 | info.vjsin.push_back(JSInput()); | |
1222 | info.vjsin.push_back(JSInput()); | |
1223 | info.vjsin.push_back(JSInput()); | |
1224 | try { | |
1225 | proxy.perform_joinsplit(info); | |
1226 | } catch (const std::runtime_error & e) { | |
1227 | BOOST_CHECK( string(e.what()).find("unsupported joinsplit input")!= string::npos); | |
1228 | } | |
06c19063 | 1229 | |
7b79275e S |
1230 | info.vjsin.clear(); |
1231 | try { | |
1232 | proxy.perform_joinsplit(info); | |
1233 | } catch (const std::runtime_error & e) { | |
bef1b5ce | 1234 | BOOST_CHECK( string(e.what()).find("error verifying joinsplit")!= string::npos); |
7b79275e S |
1235 | } |
1236 | } | |
fc4b127e S |
1237 | } |
1238 | ||
60f762a5 | 1239 | |
bb4b6982 JG |
1240 | BOOST_AUTO_TEST_CASE(rpc_z_sendmany_taddr_to_sapling) |
1241 | { | |
8a1d1930 | 1242 | RegtestActivateSapling(); |
bb4b6982 JG |
1243 | |
1244 | LOCK(pwalletMain->cs_wallet); | |
1245 | ||
1246 | if (!pwalletMain->HaveHDSeed()) { | |
1247 | pwalletMain->GenerateNewSeed(); | |
1248 | } | |
1249 | ||
1250 | UniValue retValue; | |
1251 | ||
1252 | // add keys manually | |
1253 | auto taddr = pwalletMain->GenerateNewKey().GetID(); | |
1254 | std::string taddr1 = EncodeDestination(taddr); | |
1255 | auto pa = pwalletMain->GenerateNewSaplingZKey(); | |
1256 | std::string zaddr1 = EncodePaymentAddress(pa); | |
1257 | ||
1258 | auto consensusParams = Params().GetConsensus(); | |
1259 | retValue = CallRPC("getblockcount"); | |
1260 | int nextBlockHeight = retValue.get_int() + 1; | |
1261 | ||
1262 | // Add a fake transaction to the wallet | |
1263 | CMutableTransaction mtx = CreateNewContextualCMutableTransaction(consensusParams, nextBlockHeight); | |
1264 | CScript scriptPubKey = CScript() << OP_DUP << OP_HASH160 << ToByteVector(taddr) << OP_EQUALVERIFY << OP_CHECKSIG; | |
1265 | mtx.vout.push_back(CTxOut(5 * COIN, scriptPubKey)); | |
1266 | CWalletTx wtx(pwalletMain, mtx); | |
1267 | pwalletMain->AddToWallet(wtx, true, NULL); | |
1268 | ||
1269 | // Fake-mine the transaction | |
1270 | BOOST_CHECK_EQUAL(0, chainActive.Height()); | |
1271 | CBlock block; | |
1272 | block.hashPrevBlock = chainActive.Tip()->GetBlockHash(); | |
1273 | block.vtx.push_back(wtx); | |
1274 | block.hashMerkleRoot = block.BuildMerkleTree(); | |
1275 | auto blockHash = block.GetHash(); | |
1276 | CBlockIndex fakeIndex {block}; | |
4b729ec5 | 1277 | fakeIndex.SetHeight(1); |
bb4b6982 JG |
1278 | mapBlockIndex.insert(std::make_pair(blockHash, &fakeIndex)); |
1279 | chainActive.SetTip(&fakeIndex); | |
1280 | BOOST_CHECK(chainActive.Contains(&fakeIndex)); | |
1281 | BOOST_CHECK_EQUAL(1, chainActive.Height()); | |
1282 | wtx.SetMerkleBranch(block); | |
1283 | pwalletMain->AddToWallet(wtx, true, NULL); | |
1284 | ||
1285 | // Context that z_sendmany requires | |
1286 | auto builder = TransactionBuilder(consensusParams, nextBlockHeight, pwalletMain); | |
1287 | mtx = CreateNewContextualCMutableTransaction(consensusParams, nextBlockHeight); | |
1288 | ||
eed98728 | 1289 | std::vector<SendManyRecipient> recipients = { SendManyRecipient(zaddr1, 1 * COIN, "ABCD", CScript()) }; |
bb4b6982 JG |
1290 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(builder, mtx, taddr1, {}, recipients, 0) ); |
1291 | std::shared_ptr<AsyncRPCOperation_sendmany> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_sendmany> (operation); | |
1292 | ||
1293 | // Enable test mode so tx is not sent | |
1294 | static_cast<AsyncRPCOperation_sendmany *>(operation.get())->testmode = true; | |
1295 | ||
1296 | // Generate the Sapling shielding transaction | |
1297 | operation->main(); | |
1298 | BOOST_CHECK(operation->isSuccess()); | |
1299 | ||
1300 | // Get the transaction | |
1301 | auto result = operation->getResult(); | |
1302 | BOOST_ASSERT(result.isObject()); | |
1303 | auto hexTx = result["hex"].getValStr(); | |
1304 | CDataStream ss(ParseHex(hexTx), SER_NETWORK, PROTOCOL_VERSION); | |
1305 | CTransaction tx; | |
1306 | ss >> tx; | |
1307 | BOOST_ASSERT(!tx.vShieldedOutput.empty()); | |
1308 | ||
1309 | // We shouldn't be able to decrypt with the empty ovk | |
1310 | BOOST_CHECK(!AttemptSaplingOutDecryption( | |
1311 | tx.vShieldedOutput[0].outCiphertext, | |
1312 | uint256(), | |
1313 | tx.vShieldedOutput[0].cv, | |
1314 | tx.vShieldedOutput[0].cm, | |
1315 | tx.vShieldedOutput[0].ephemeralKey)); | |
1316 | ||
1317 | // We should be able to decrypt the outCiphertext with the ovk | |
1318 | // generated for transparent addresses | |
1319 | HDSeed seed; | |
1320 | BOOST_ASSERT(pwalletMain->GetHDSeed(seed)); | |
1321 | BOOST_CHECK(AttemptSaplingOutDecryption( | |
1322 | tx.vShieldedOutput[0].outCiphertext, | |
1323 | ovkForShieldingFromTaddr(seed), | |
1324 | tx.vShieldedOutput[0].cv, | |
1325 | tx.vShieldedOutput[0].cm, | |
1326 | tx.vShieldedOutput[0].ephemeralKey)); | |
1327 | ||
1328 | // Tear down | |
1329 | chainActive.SetTip(NULL); | |
1330 | mapBlockIndex.erase(blockHash); | |
1331 | mapArgs.erase("-developersapling"); | |
1332 | mapArgs.erase("-experimentalfeatures"); | |
1333 | ||
1334 | // Revert to default | |
8a1d1930 | 1335 | RegtestDeactivateSapling(); |
bb4b6982 JG |
1336 | } |
1337 | ||
1338 | ||
73699cea S |
1339 | /* |
1340 | * This test covers storing encrypted zkeys in the wallet. | |
1341 | */ | |
1342 | BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_zkeys) | |
1343 | { | |
1344 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
0d37ae3a | 1345 | UniValue retValue; |
73699cea S |
1346 | int n = 100; |
1347 | ||
1348 | // wallet should currently be empty | |
e5eab182 | 1349 | std::set<libzcash::SproutPaymentAddress> addrs; |
25d5e80c | 1350 | pwalletMain->GetSproutPaymentAddresses(addrs); |
73699cea S |
1351 | BOOST_CHECK(addrs.size()==0); |
1352 | ||
1353 | // create keys | |
1354 | for (int i = 0; i < n; i++) { | |
471138a9 | 1355 | CallRPC("z_getnewaddress sprout"); |
73699cea S |
1356 | } |
1357 | ||
1358 | // Verify we can list the keys imported | |
1359 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); | |
0d37ae3a | 1360 | UniValue arr = retValue.get_array(); |
73699cea S |
1361 | BOOST_CHECK(arr.size() == n); |
1362 | ||
d8e06e3f JG |
1363 | // Verify that the wallet encryption RPC is disabled |
1364 | BOOST_CHECK_THROW(CallRPC("encryptwallet passphrase"), runtime_error); | |
1365 | ||
82bd9ee8 | 1366 | // Encrypt the wallet (we can't call RPC encryptwallet as that shuts down node) |
73699cea S |
1367 | SecureString strWalletPass; |
1368 | strWalletPass.reserve(100); | |
1369 | strWalletPass = "hello"; | |
6be367ea S |
1370 | |
1371 | boost::filesystem::current_path(GetArg("-datadir","/tmp/thisshouldnothappen")); | |
73699cea | 1372 | BOOST_CHECK(pwalletMain->EncryptWallet(strWalletPass)); |
06c19063 | 1373 | |
73699cea S |
1374 | // Verify we can still list the keys imported |
1375 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); | |
1376 | arr = retValue.get_array(); | |
1377 | BOOST_CHECK(arr.size() == n); | |
06c19063 | 1378 | |
73699cea | 1379 | // Try to add a new key, but we can't as the wallet is locked |
471138a9 | 1380 | BOOST_CHECK_THROW(CallRPC("z_getnewaddress sprout"), runtime_error); |
06c19063 | 1381 | |
73699cea S |
1382 | // We can't call RPC walletpassphrase as that invokes RPCRunLater which breaks tests. |
1383 | // So we manually unlock. | |
1384 | BOOST_CHECK(pwalletMain->Unlock(strWalletPass)); | |
06c19063 | 1385 | |
73699cea | 1386 | // Now add a key |
471138a9 | 1387 | BOOST_CHECK_NO_THROW(CallRPC("z_getnewaddress sprout")); |
06c19063 | 1388 | |
73699cea S |
1389 | // Verify the key has been added |
1390 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); | |
1391 | arr = retValue.get_array(); | |
06c19063 | 1392 | BOOST_CHECK(arr.size() == n+1); |
73699cea S |
1393 | |
1394 | // We can't simulate over RPC the wallet closing and being reloaded | |
1395 | // but there are tests for this in gtest. | |
1396 | } | |
c2dc091e | 1397 | |
1398 | BOOST_AUTO_TEST_CASE(rpc_wallet_encrypted_wallet_sapzkeys) | |
1399 | { | |
1400 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
1401 | UniValue retValue; | |
1402 | int n = 100; | |
1403 | ||
1404 | if(!pwalletMain->HaveHDSeed()) | |
1405 | { | |
1406 | pwalletMain->GenerateNewSeed(); | |
1407 | } | |
1408 | ||
1409 | // wallet should currently be empty | |
1410 | std::set<libzcash::SaplingPaymentAddress> addrs; | |
1411 | pwalletMain->GetSaplingPaymentAddresses(addrs); | |
1412 | BOOST_CHECK(addrs.size()==0); | |
1413 | ||
1414 | // create keys | |
1415 | for (int i = 0; i < n; i++) { | |
1416 | CallRPC("z_getnewaddress sapling"); | |
1417 | } | |
1418 | ||
1419 | // Verify we can list the keys imported | |
1420 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); | |
1421 | UniValue arr = retValue.get_array(); | |
1422 | BOOST_CHECK(arr.size() == n); | |
1423 | ||
1424 | // Verify that the wallet encryption RPC is disabled | |
1425 | BOOST_CHECK_THROW(CallRPC("encryptwallet passphrase"), runtime_error); | |
1426 | ||
1427 | // Encrypt the wallet (we can't call RPC encryptwallet as that shuts down node) | |
1428 | SecureString strWalletPass; | |
1429 | strWalletPass.reserve(100); | |
1430 | strWalletPass = "hello"; | |
1431 | ||
1432 | boost::filesystem::current_path(GetArg("-datadir","/tmp/thisshouldnothappen")); | |
1433 | BOOST_CHECK(pwalletMain->EncryptWallet(strWalletPass)); | |
1434 | ||
1435 | // Verify we can still list the keys imported | |
1436 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); | |
1437 | arr = retValue.get_array(); | |
1438 | BOOST_CHECK(arr.size() == n); | |
1439 | ||
1440 | // Try to add a new key, but we can't as the wallet is locked | |
1441 | BOOST_CHECK_THROW(CallRPC("z_getnewaddress sapling"), runtime_error); | |
1442 | ||
1443 | // We can't call RPC walletpassphrase as that invokes RPCRunLater which breaks tests. | |
1444 | // So we manually unlock. | |
1445 | BOOST_CHECK(pwalletMain->Unlock(strWalletPass)); | |
1446 | ||
1447 | // Now add a key | |
1448 | BOOST_CHECK_NO_THROW(CallRPC("z_getnewaddress sapling")); | |
1449 | ||
1450 | // Verify the key has been added | |
1451 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listaddresses")); | |
1452 | arr = retValue.get_array(); | |
1453 | BOOST_CHECK(arr.size() == n+1); | |
1454 | ||
1455 | // We can't simulate over RPC the wallet closing and being reloaded | |
1456 | // but there are tests for this in gtest. | |
1457 | } | |
73699cea | 1458 | |
06c19063 | 1459 | |
d72c19a6 S |
1460 | BOOST_AUTO_TEST_CASE(rpc_z_listunspent_parameters) |
1461 | { | |
1462 | SelectParams(CBaseChainParams::TESTNET); | |
1463 | ||
1464 | LOCK(pwalletMain->cs_wallet); | |
73699cea | 1465 | |
d72c19a6 S |
1466 | UniValue retValue; |
1467 | ||
1468 | // too many args | |
1469 | BOOST_CHECK_THROW(CallRPC("z_listunspent 1 2 3 4 5"), runtime_error); | |
1470 | ||
1471 | // minconf must be >= 0 | |
1472 | BOOST_CHECK_THROW(CallRPC("z_listunspent -1"), runtime_error); | |
1473 | ||
1474 | // maxconf must be > minconf | |
1475 | BOOST_CHECK_THROW(CallRPC("z_listunspent 2 1"), runtime_error); | |
1476 | ||
1477 | // maxconf must not be out of range | |
1478 | BOOST_CHECK_THROW(CallRPC("z_listunspent 1 9999999999"), runtime_error); | |
1479 | ||
1480 | // must be an array of addresses | |
1481 | BOOST_CHECK_THROW(CallRPC("z_listunspent 1 999 false ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP"), runtime_error); | |
1482 | ||
1483 | // address must be string | |
1484 | BOOST_CHECK_THROW(CallRPC("z_listunspent 1 999 false [123456]"), runtime_error); | |
1485 | ||
1486 | // no spending key | |
1487 | BOOST_CHECK_THROW(CallRPC("z_listunspent 1 999 false [\"ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP\"]"), runtime_error); | |
1488 | ||
1489 | // allow watch only | |
1490 | BOOST_CHECK_NO_THROW(CallRPC("z_listunspent 1 999 true [\"ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP\"]")); | |
1491 | ||
1492 | // wrong network, mainnet instead of testnet | |
1493 | BOOST_CHECK_THROW(CallRPC("z_listunspent 1 999 true [\"zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U\"]"), runtime_error); | |
1494 | ||
1495 | // create shielded address so we have the spending key | |
471138a9 | 1496 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_getnewaddress sprout")); |
d72c19a6 S |
1497 | std::string myzaddr = retValue.get_str(); |
1498 | ||
1499 | // return empty array for this address | |
1500 | BOOST_CHECK_NO_THROW(retValue = CallRPC("z_listunspent 1 999 false [\"" + myzaddr + "\"]")); | |
1501 | UniValue arr = retValue.get_array(); | |
1502 | BOOST_CHECK_EQUAL(0, arr.size()); | |
1503 | ||
1504 | // duplicate address error | |
1505 | BOOST_CHECK_THROW(CallRPC("z_listunspent 1 999 false [\"" + myzaddr + "\", \"" + myzaddr + "\"]"), runtime_error); | |
1506 | } | |
06c19063 S |
1507 | |
1508 | ||
1509 | BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_parameters) | |
1510 | { | |
1511 | SelectParams(CBaseChainParams::TESTNET); | |
1512 | ||
1513 | LOCK(pwalletMain->cs_wallet); | |
1514 | ||
1515 | BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase"), runtime_error); | |
1516 | BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase toofewargs"), runtime_error); | |
c5dabd2b | 1517 | BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase too many args shown here"), runtime_error); |
06c19063 S |
1518 | |
1519 | // bad from address | |
1520 | BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase " | |
1521 | "INVALIDtmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB"), runtime_error); | |
1522 | ||
1523 | // bad from address | |
1524 | BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase " | |
1525 | "** tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB"), runtime_error); | |
1526 | ||
1527 | // bad to address | |
1528 | BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase " | |
1529 | "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ INVALIDtnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB"), runtime_error); | |
1530 | ||
1531 | // invalid fee amount, cannot be negative | |
1532 | BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase " | |
1533 | "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " | |
1534 | "tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB " | |
1535 | "-0.0001" | |
1536 | ), runtime_error); | |
1537 | ||
1538 | // invalid fee amount, bigger than MAX_MONEY | |
1539 | BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase " | |
1540 | "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " | |
1541 | "tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB " | |
1542 | "21000001" | |
1543 | ), runtime_error); | |
1544 | ||
c5dabd2b S |
1545 | // invalid limit, must be at least 0 |
1546 | BOOST_CHECK_THROW(CallRPC("z_shieldcoinbase " | |
1547 | "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ " | |
1548 | "tnpoQJVnYBZZqkFadj2bJJLThNCxbADGB5gSGeYTAGGrT5tejsxY9Zc1BtY8nnHmZkB " | |
1549 | "100 -1" | |
1550 | ), runtime_error); | |
1551 | ||
072099d7 S |
1552 | // Mutable tx containing contextual information we need to build tx |
1553 | UniValue retValue = CallRPC("getblockcount"); | |
1554 | int nHeight = retValue.get_int(); | |
1555 | CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1); | |
1556 | if (mtx.nVersion == 1) { | |
1557 | mtx.nVersion = 2; | |
1558 | } | |
1559 | ||
06c19063 S |
1560 | // Test constructor of AsyncRPCOperation_sendmany |
1561 | std::string testnetzaddr = "ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP"; | |
1562 | std::string mainnetzaddr = "zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U"; | |
1563 | ||
1564 | try { | |
5f91a956 | 1565 | std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, {}, testnetzaddr, -1 )); |
06c19063 S |
1566 | } catch (const UniValue& objError) { |
1567 | BOOST_CHECK( find_error(objError, "Fee is out of range")); | |
1568 | } | |
1569 | ||
1570 | try { | |
5f91a956 | 1571 | std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, {}, testnetzaddr, 1)); |
06c19063 S |
1572 | } catch (const UniValue& objError) { |
1573 | BOOST_CHECK( find_error(objError, "Empty inputs")); | |
1574 | } | |
1575 | ||
1576 | // Testnet payment addresses begin with 'zt'. This test detects an incorrect prefix. | |
1577 | try { | |
1578 | std::vector<ShieldCoinbaseUTXO> inputs = { ShieldCoinbaseUTXO{uint256(),0,0} }; | |
5f91a956 | 1579 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, mainnetzaddr, 1) ); |
06c19063 | 1580 | } catch (const UniValue& objError) { |
80ed13d5 | 1581 | BOOST_CHECK( find_error(objError, "Invalid to address")); |
06c19063 S |
1582 | } |
1583 | ||
1584 | } | |
1585 | ||
1586 | ||
1587 | ||
1588 | BOOST_AUTO_TEST_CASE(rpc_z_shieldcoinbase_internals) | |
1589 | { | |
1590 | SelectParams(CBaseChainParams::TESTNET); | |
1591 | ||
1592 | LOCK(pwalletMain->cs_wallet); | |
1593 | ||
072099d7 S |
1594 | // Mutable tx containing contextual information we need to build tx |
1595 | UniValue retValue = CallRPC("getblockcount"); | |
1596 | int nHeight = retValue.get_int(); | |
1597 | CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1); | |
1598 | if (mtx.nVersion == 1) { | |
1599 | mtx.nVersion = 2; | |
1600 | } | |
1601 | ||
06c19063 S |
1602 | // Test that option -mempooltxinputlimit is respected. |
1603 | mapArgs["-mempooltxinputlimit"] = "1"; | |
1604 | ||
1605 | // Add keys manually | |
92fc29a3 | 1606 | auto pa = pwalletMain->GenerateNewSproutZKey(); |
80ed13d5 | 1607 | std::string zaddr = EncodePaymentAddress(pa); |
06c19063 S |
1608 | |
1609 | // Supply 2 inputs when mempool limit is 1 | |
1610 | { | |
1611 | std::vector<ShieldCoinbaseUTXO> inputs = { ShieldCoinbaseUTXO{uint256(),0,0}, ShieldCoinbaseUTXO{uint256(),0,0} }; | |
5f91a956 | 1612 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, zaddr) ); |
06c19063 S |
1613 | operation->main(); |
1614 | BOOST_CHECK(operation->isFailed()); | |
1615 | std::string msg = operation->getErrorMessage(); | |
1616 | BOOST_CHECK( msg.find("Number of inputs 2 is greater than mempooltxinputlimit of 1") != string::npos); | |
1617 | } | |
1618 | ||
1619 | // Insufficient funds | |
1620 | { | |
1621 | std::vector<ShieldCoinbaseUTXO> inputs = { ShieldCoinbaseUTXO{uint256(),0,0} }; | |
5f91a956 | 1622 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, zaddr) ); |
06c19063 S |
1623 | operation->main(); |
1624 | BOOST_CHECK(operation->isFailed()); | |
1625 | std::string msg = operation->getErrorMessage(); | |
1626 | BOOST_CHECK( msg.find("Insufficient coinbase funds") != string::npos); | |
1627 | } | |
1628 | ||
1629 | // Test the perform_joinsplit methods. | |
1630 | { | |
1631 | // Dummy input so the operation object can be instantiated. | |
1632 | std::vector<ShieldCoinbaseUTXO> inputs = { ShieldCoinbaseUTXO{uint256(),0,100000} }; | |
5f91a956 | 1633 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(TransactionBuilder(), mtx, inputs, zaddr) ); |
06c19063 S |
1634 | std::shared_ptr<AsyncRPCOperation_shieldcoinbase> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_shieldcoinbase> (operation); |
1635 | TEST_FRIEND_AsyncRPCOperation_shieldcoinbase proxy(ptr); | |
1636 | static_cast<AsyncRPCOperation_shieldcoinbase *>(operation.get())->testmode = true; | |
1637 | ||
1638 | ShieldCoinbaseJSInfo info; | |
1639 | info.vjsin.push_back(JSInput()); | |
1640 | info.vjsin.push_back(JSInput()); | |
1641 | info.vjsin.push_back(JSInput()); | |
1642 | try { | |
1643 | proxy.perform_joinsplit(info); | |
1644 | } catch (const std::runtime_error & e) { | |
1645 | BOOST_CHECK( string(e.what()).find("unsupported joinsplit input")!= string::npos); | |
1646 | } | |
1647 | ||
1648 | info.vjsin.clear(); | |
1649 | try { | |
1650 | proxy.perform_joinsplit(info); | |
1651 | } catch (const std::runtime_error & e) { | |
bef1b5ce | 1652 | BOOST_CHECK( string(e.what()).find("error verifying joinsplit")!= string::npos); |
06c19063 S |
1653 | } |
1654 | } | |
1655 | ||
1656 | } | |
1657 | ||
6e9c7629 JG |
1658 | BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_parameters) |
1659 | { | |
1660 | SelectParams(CBaseChainParams::TESTNET); | |
1661 | ||
1662 | LOCK(pwalletMain->cs_wallet); | |
1663 | ||
88d134c2 | 1664 | CheckRPCThrows("z_mergetoaddress 1 2", |
25f1f7dc | 1665 | "Error: z_mergetoaddress is disabled. Run './zcash-cli help z_mergetoaddress' for instructions on how to enable this feature."); |
88d134c2 | 1666 | |
9c6b6a2b EOW |
1667 | // Set global state required for z_mergetoaddress |
1668 | fExperimentalMode = true; | |
1669 | mapArgs["-zmergetoaddress"] = "1"; | |
1670 | ||
6e9c7629 JG |
1671 | BOOST_CHECK_THROW(CallRPC("z_mergetoaddress"), runtime_error); |
1672 | BOOST_CHECK_THROW(CallRPC("z_mergetoaddress toofewargs"), runtime_error); | |
1673 | BOOST_CHECK_THROW(CallRPC("z_mergetoaddress just too many args present for this method"), runtime_error); | |
1674 | ||
9c6b6a2b EOW |
1675 | std::string taddr1 = "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ"; |
1676 | std::string taddr2 = "tmYmhvdKqEte49iohoB9utgL1kPbGgWSdNc"; | |
1677 | std::string aSproutAddr = "ztVtBC7vJFXPsZC8S3hXRu51rZysoJkSe6r1t9wk56bELrV9xTK6dx5TgSCH6RTw1dRD7HuApmcY1nhuQW9QfvE4MQXRRYU"; | |
88d134c2 EOW |
1678 | std::string aSaplingAddr = "ztestsapling19rnyu293v44f0kvtmszhx35lpdug574twc0lwyf4s7w0umtkrdq5nfcauxrxcyfmh3m7slemqsj"; |
1679 | ||
1680 | CheckRPCThrows("z_mergetoaddress [] " + taddr1, | |
1681 | "Invalid parameter, fromaddresses array is empty."); | |
9c6b6a2b | 1682 | |
6e9c7629 | 1683 | // bad from address |
9c6b6a2b EOW |
1684 | CheckRPCThrows("z_mergetoaddress [\"INVALID" + taddr1 + "\"] " + taddr2, |
1685 | "Unknown address format: INVALID" + taddr1); | |
6e9c7629 JG |
1686 | |
1687 | // bad from address | |
9c6b6a2b EOW |
1688 | CheckRPCThrows("z_mergetoaddress ** " + taddr2, |
1689 | "Error parsing JSON:**"); | |
6e9c7629 JG |
1690 | |
1691 | // bad from address | |
9c6b6a2b EOW |
1692 | CheckRPCThrows("z_mergetoaddress [\"**\"] " + taddr2, |
1693 | "Unknown address format: **"); | |
6e9c7629 JG |
1694 | |
1695 | // bad from address | |
9c6b6a2b EOW |
1696 | CheckRPCThrows("z_mergetoaddress " + taddr1 + " " + taddr2, |
1697 | "Error parsing JSON:" + taddr1); | |
6e9c7629 JG |
1698 | |
1699 | // bad from address | |
9c6b6a2b EOW |
1700 | CheckRPCThrows("z_mergetoaddress [" + taddr1 + "] " + taddr2, |
1701 | "Error parsing JSON:[" + taddr1 + "]"); | |
6e9c7629 JG |
1702 | |
1703 | // bad to address | |
9c6b6a2b EOW |
1704 | CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] INVALID" + taddr2, |
1705 | "Invalid parameter, unknown address format: INVALID" + taddr2); | |
6e9c7629 JG |
1706 | |
1707 | // duplicate address | |
9c6b6a2b EOW |
1708 | CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\",\"" + taddr1 + "\"] " + taddr2, |
1709 | "Invalid parameter, duplicated address: " + taddr1); | |
6e9c7629 JG |
1710 | |
1711 | // invalid fee amount, cannot be negative | |
9c6b6a2b EOW |
1712 | CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + taddr2 + " -0.0001", |
1713 | "Amount out of range"); | |
6e9c7629 JG |
1714 | |
1715 | // invalid fee amount, bigger than MAX_MONEY | |
9c6b6a2b EOW |
1716 | CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + taddr2 + " 21000001", |
1717 | "Amount out of range"); | |
6e9c7629 JG |
1718 | |
1719 | // invalid transparent limit, must be at least 0 | |
9c6b6a2b EOW |
1720 | CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + taddr2 + " 0.0001 -1", |
1721 | "Limit on maximum number of UTXOs cannot be negative"); | |
6e9c7629 JG |
1722 | |
1723 | // invalid shielded limit, must be at least 0 | |
9c6b6a2b EOW |
1724 | CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + taddr2 + " 0.0001 100 -1", |
1725 | "Limit on maximum number of notes cannot be negative"); | |
6e9c7629 | 1726 | |
88d134c2 | 1727 | CheckRPCThrows("z_mergetoaddress [\"ANY_TADDR\",\"" + taddr1 + "\"] " + taddr2, |
25f1f7dc | 1728 | "Cannot specify specific taddrs when using \"ANY_TADDR\""); |
88d134c2 EOW |
1729 | |
1730 | CheckRPCThrows("z_mergetoaddress [\"ANY_SPROUT\",\"" + aSproutAddr + "\"] " + taddr2, | |
25f1f7dc | 1731 | "Cannot specify specific zaddrs when using \"ANY_SPROUT\" or \"ANY_SAPLING\""); |
88d134c2 EOW |
1732 | |
1733 | CheckRPCThrows("z_mergetoaddress [\"ANY_SAPLING\",\"" + aSaplingAddr + "\"] " + taddr2, | |
25f1f7dc | 1734 | "Cannot specify specific zaddrs when using \"ANY_SPROUT\" or \"ANY_SAPLING\""); |
6e9c7629 JG |
1735 | |
1736 | // memo bigger than allowed length of ZC_MEMO_SIZE | |
1737 | std::vector<char> v (2 * (ZC_MEMO_SIZE+1)); // x2 for hexadecimal string format | |
1738 | std::fill(v.begin(),v.end(), 'A'); | |
1739 | std::string badmemo(v.begin(), v.end()); | |
9c6b6a2b EOW |
1740 | CheckRPCThrows("z_mergetoaddress [\"" + taddr1 + "\"] " + aSproutAddr + " 0.0001 100 100 " + badmemo, |
1741 | "Invalid parameter, size of memo is larger than maximum allowed 512"); | |
6e9c7629 JG |
1742 | |
1743 | // Mutable tx containing contextual information we need to build tx | |
1744 | UniValue retValue = CallRPC("getblockcount"); | |
1745 | int nHeight = retValue.get_int(); | |
1746 | CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1); | |
1747 | ||
1748 | // Test constructor of AsyncRPCOperation_mergetoaddress | |
1749 | MergeToAddressRecipient testnetzaddr( | |
1750 | "ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP", | |
1751 | "testnet memo"); | |
1752 | MergeToAddressRecipient mainnetzaddr( | |
1753 | "zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U", | |
1754 | "mainnet memo"); | |
1755 | ||
1756 | try { | |
487c9020 | 1757 | std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_mergetoaddress(boost::none, mtx, {}, {}, {}, testnetzaddr, -1 )); |
6e9c7629 JG |
1758 | BOOST_FAIL("Should have caused an error"); |
1759 | } catch (const UniValue& objError) { | |
1760 | BOOST_CHECK( find_error(objError, "Fee is out of range")); | |
1761 | } | |
1762 | ||
1763 | try { | |
487c9020 | 1764 | std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_mergetoaddress(boost::none, mtx, {}, {}, {}, testnetzaddr, 1)); |
6e9c7629 JG |
1765 | BOOST_FAIL("Should have caused an error"); |
1766 | } catch (const UniValue& objError) { | |
1767 | BOOST_CHECK( find_error(objError, "No inputs")); | |
1768 | } | |
1769 | ||
487c9020 | 1770 | std::vector<MergeToAddressInputUTXO> inputs = { MergeToAddressInputUTXO{ COutPoint{uint256(), 0}, 0, CScript()} }; |
6e9c7629 JG |
1771 | |
1772 | try { | |
1773 | MergeToAddressRecipient badaddr("", "memo"); | |
487c9020 | 1774 | std::shared_ptr<AsyncRPCOperation> operation(new AsyncRPCOperation_mergetoaddress(boost::none, mtx, inputs, {}, {}, badaddr, 1)); |
6e9c7629 JG |
1775 | BOOST_FAIL("Should have caused an error"); |
1776 | } catch (const UniValue& objError) { | |
1777 | BOOST_CHECK( find_error(objError, "Recipient parameter missing")); | |
1778 | } | |
1779 | ||
487c9020 EOW |
1780 | std::vector<MergeToAddressInputSproutNote> sproutNoteInputs = |
1781 | {MergeToAddressInputSproutNote{JSOutPoint(), SproutNote(), 0, SproutSpendingKey()}}; | |
1782 | std::vector<MergeToAddressInputSaplingNote> saplingNoteInputs = | |
1783 | {MergeToAddressInputSaplingNote{SaplingOutPoint(), SaplingNote(), 0, SaplingExpandedSpendingKey()}}; | |
1784 | ||
1785 | // Sprout and Sapling inputs -> throw | |
1786 | try { | |
1787 | auto operation = new AsyncRPCOperation_mergetoaddress(boost::none, mtx, inputs, sproutNoteInputs, saplingNoteInputs, testnetzaddr, 1); | |
1788 | BOOST_FAIL("Should have caused an error"); | |
1789 | } catch (const UniValue& objError) { | |
1790 | BOOST_CHECK(find_error(objError, "Cannot send from both Sprout and Sapling addresses using z_mergetoaddress")); | |
1791 | } | |
1792 | // Sprout inputs and TransactionBuilder -> throw | |
1793 | try { | |
1794 | auto operation = new AsyncRPCOperation_mergetoaddress(TransactionBuilder(), mtx, inputs, sproutNoteInputs, {}, testnetzaddr, 1); | |
1795 | BOOST_FAIL("Should have caused an error"); | |
1796 | } catch (const UniValue& objError) { | |
1797 | BOOST_CHECK(find_error(objError, "Sprout notes are not supported by the TransactionBuilder")); | |
1798 | } | |
1799 | ||
6e9c7629 JG |
1800 | // Testnet payment addresses begin with 'zt'. This test detects an incorrect prefix. |
1801 | try { | |
487c9020 EOW |
1802 | std::vector<MergeToAddressInputUTXO> inputs = { MergeToAddressInputUTXO{ COutPoint{uint256(), 0}, 0, CScript()} }; |
1803 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_mergetoaddress(boost::none, mtx, inputs, {}, {}, mainnetzaddr, 1) ); | |
6e9c7629 JG |
1804 | BOOST_FAIL("Should have caused an error"); |
1805 | } catch (const UniValue& objError) { | |
80ed13d5 | 1806 | BOOST_CHECK( find_error(objError, "Invalid recipient address")); |
6e9c7629 | 1807 | } |
9c6b6a2b EOW |
1808 | |
1809 | // Un-set global state | |
1810 | fExperimentalMode = false; | |
1811 | mapArgs.erase("-zmergetoaddress"); | |
6e9c7629 JG |
1812 | } |
1813 | ||
1814 | ||
1815 | // TODO: test private methods | |
1816 | BOOST_AUTO_TEST_CASE(rpc_z_mergetoaddress_internals) | |
1817 | { | |
1818 | SelectParams(CBaseChainParams::TESTNET); | |
1819 | ||
1820 | LOCK(pwalletMain->cs_wallet); | |
1821 | ||
1822 | // Mutable tx containing contextual information we need to build tx | |
1823 | UniValue retValue = CallRPC("getblockcount"); | |
1824 | int nHeight = retValue.get_int(); | |
1825 | CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight + 1); | |
1826 | ||
1827 | // Test that option -mempooltxinputlimit is respected. | |
1828 | mapArgs["-mempooltxinputlimit"] = "1"; | |
1829 | ||
1830 | // Add keys manually | |
1831 | BOOST_CHECK_NO_THROW(retValue = CallRPC("getnewaddress")); | |
1832 | MergeToAddressRecipient taddr1(retValue.get_str(), ""); | |
92fc29a3 | 1833 | auto pa = pwalletMain->GenerateNewSproutZKey(); |
80ed13d5 | 1834 | MergeToAddressRecipient zaddr1(EncodePaymentAddress(pa), "DEADBEEF"); |
6e9c7629 JG |
1835 | |
1836 | // Supply 2 inputs when mempool limit is 1 | |
1837 | { | |
1838 | std::vector<MergeToAddressInputUTXO> inputs = { | |
487c9020 EOW |
1839 | MergeToAddressInputUTXO{COutPoint{uint256(),0},0, CScript()}, |
1840 | MergeToAddressInputUTXO{COutPoint{uint256(),0},0, CScript()} | |
6e9c7629 | 1841 | }; |
487c9020 | 1842 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_mergetoaddress(boost::none, mtx, inputs, {}, {}, zaddr1) ); |
6e9c7629 JG |
1843 | operation->main(); |
1844 | BOOST_CHECK(operation->isFailed()); | |
1845 | std::string msg = operation->getErrorMessage(); | |
1846 | BOOST_CHECK( msg.find("Number of transparent inputs 2 is greater than mempooltxinputlimit of 1") != string::npos); | |
1847 | } | |
1848 | ||
1849 | // Insufficient funds | |
1850 | { | |
487c9020 EOW |
1851 | std::vector<MergeToAddressInputUTXO> inputs = { MergeToAddressInputUTXO{COutPoint{uint256(),0},0, CScript()} }; |
1852 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_mergetoaddress(boost::none, mtx, inputs, {}, {}, zaddr1) ); | |
6e9c7629 JG |
1853 | operation->main(); |
1854 | BOOST_CHECK(operation->isFailed()); | |
1855 | std::string msg = operation->getErrorMessage(); | |
1856 | BOOST_CHECK( msg.find("Insufficient funds, have 0.00 and miners fee is 0.0001") != string::npos); | |
1857 | } | |
1858 | ||
1859 | // get_memo_from_hex_string()) | |
1860 | { | |
487c9020 EOW |
1861 | std::vector<MergeToAddressInputUTXO> inputs = { MergeToAddressInputUTXO{COutPoint{uint256(),0},100000, CScript()} }; |
1862 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_mergetoaddress(boost::none, mtx, inputs, {}, {}, zaddr1) ); | |
6e9c7629 JG |
1863 | std::shared_ptr<AsyncRPCOperation_mergetoaddress> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_mergetoaddress> (operation); |
1864 | TEST_FRIEND_AsyncRPCOperation_mergetoaddress proxy(ptr); | |
1865 | ||
1866 | std::string memo = "DEADBEEF"; | |
a6bbb26e | 1867 | std::array<unsigned char, ZC_MEMO_SIZE> array = proxy.get_memo_from_hex_string(memo); |
6e9c7629 JG |
1868 | BOOST_CHECK_EQUAL(array[0], 0xDE); |
1869 | BOOST_CHECK_EQUAL(array[1], 0xAD); | |
1870 | BOOST_CHECK_EQUAL(array[2], 0xBE); | |
1871 | BOOST_CHECK_EQUAL(array[3], 0xEF); | |
1872 | for (int i=4; i<ZC_MEMO_SIZE; i++) { | |
1873 | BOOST_CHECK_EQUAL(array[i], 0x00); // zero padding | |
1874 | } | |
1875 | ||
1876 | // memo is longer than allowed | |
1877 | std::vector<char> v (2 * (ZC_MEMO_SIZE+1)); | |
1878 | std::fill(v.begin(),v.end(), 'A'); | |
1879 | std::string bigmemo(v.begin(), v.end()); | |
1880 | ||
1881 | try { | |
1882 | proxy.get_memo_from_hex_string(bigmemo); | |
1883 | BOOST_FAIL("Should have caused an error"); | |
1884 | } catch (const UniValue& objError) { | |
1885 | BOOST_CHECK( find_error(objError, "too big")); | |
1886 | } | |
1887 | ||
1888 | // invalid hexadecimal string | |
1889 | std::fill(v.begin(),v.end(), '@'); // not a hex character | |
1890 | std::string badmemo(v.begin(), v.end()); | |
1891 | ||
1892 | try { | |
1893 | proxy.get_memo_from_hex_string(badmemo); | |
1894 | BOOST_FAIL("Should have caused an error"); | |
1895 | } catch (const UniValue& objError) { | |
1896 | BOOST_CHECK( find_error(objError, "hexadecimal format")); | |
1897 | } | |
1898 | ||
1899 | // odd length hexadecimal string | |
1900 | std::fill(v.begin(),v.end(), 'A'); | |
1901 | v.resize(v.size() - 1); | |
1902 | assert(v.size() %2 == 1); // odd length | |
1903 | std::string oddmemo(v.begin(), v.end()); | |
1904 | try { | |
1905 | proxy.get_memo_from_hex_string(oddmemo); | |
1906 | BOOST_FAIL("Should have caused an error"); | |
1907 | } catch (const UniValue& objError) { | |
1908 | BOOST_CHECK( find_error(objError, "hexadecimal format")); | |
1909 | } | |
1910 | } | |
1911 | ||
1912 | // Test the perform_joinsplit methods. | |
1913 | { | |
1914 | // Dummy input so the operation object can be instantiated. | |
487c9020 EOW |
1915 | std::vector<MergeToAddressInputUTXO> inputs = { MergeToAddressInputUTXO{COutPoint{uint256(),0},100000, CScript()} }; |
1916 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_mergetoaddress(boost::none, mtx, inputs, {}, {}, zaddr1) ); | |
6e9c7629 JG |
1917 | std::shared_ptr<AsyncRPCOperation_mergetoaddress> ptr = std::dynamic_pointer_cast<AsyncRPCOperation_mergetoaddress> (operation); |
1918 | TEST_FRIEND_AsyncRPCOperation_mergetoaddress proxy(ptr); | |
1919 | ||
1920 | // Enable test mode so tx is not sent and proofs are not generated | |
1921 | static_cast<AsyncRPCOperation_sendmany *>(operation.get())->testmode = true; | |
1922 | ||
1923 | MergeToAddressJSInfo info; | |
8ea8ef98 | 1924 | std::vector<boost::optional < SproutWitness>> witnesses; |
6e9c7629 JG |
1925 | uint256 anchor; |
1926 | try { | |
1927 | proxy.perform_joinsplit(info, witnesses, anchor); | |
1928 | BOOST_FAIL("Should have caused an error"); | |
1929 | } catch (const std::runtime_error & e) { | |
1930 | BOOST_CHECK( string(e.what()).find("anchor is null")!= string::npos); | |
1931 | } | |
1932 | ||
1933 | try { | |
1934 | std::vector<JSOutPoint> v; | |
1935 | proxy.perform_joinsplit(info, v); | |
1936 | BOOST_FAIL("Should have caused an error"); | |
1937 | } catch (const std::runtime_error & e) { | |
1938 | BOOST_CHECK( string(e.what()).find("anchor is null")!= string::npos); | |
1939 | } | |
1940 | ||
b230fe68 | 1941 | info.notes.push_back(SproutNote()); |
6e9c7629 JG |
1942 | try { |
1943 | proxy.perform_joinsplit(info); | |
1944 | BOOST_FAIL("Should have caused an error"); | |
1945 | } catch (const std::runtime_error & e) { | |
1946 | BOOST_CHECK( string(e.what()).find("number of notes")!= string::npos); | |
1947 | } | |
1948 | ||
1949 | info.notes.clear(); | |
1950 | info.vjsin.push_back(JSInput()); | |
1951 | info.vjsin.push_back(JSInput()); | |
1952 | info.vjsin.push_back(JSInput()); | |
1953 | try { | |
1954 | proxy.perform_joinsplit(info); | |
1955 | BOOST_FAIL("Should have caused an error"); | |
1956 | } catch (const std::runtime_error & e) { | |
1957 | BOOST_CHECK( string(e.what()).find("unsupported joinsplit input")!= string::npos); | |
1958 | } | |
1959 | ||
1960 | info.vjsin.clear(); | |
1961 | try { | |
1962 | proxy.perform_joinsplit(info); | |
1963 | BOOST_FAIL("Should have caused an error"); | |
1964 | } catch (const std::runtime_error & e) { | |
1965 | BOOST_CHECK( string(e.what()).find("error verifying joinsplit")!= string::npos); | |
1966 | } | |
1967 | } | |
6e9c7629 JG |
1968 | } |
1969 | ||
1970 | ||
b922924d | 1971 | BOOST_AUTO_TEST_SUITE_END() |