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