]>
Commit | Line | Data |
---|---|---|
e3bc5698 | 1 | // Copyright (c) 2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2014 The Bitcoin Core developers |
72fb3d29 | 3 | // Distributed under the MIT software license, see the accompanying |
e3bc5698 JG |
4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
5 | ||
eda37330 | 6 | #include "amount.h" |
51ed9ec9 | 7 | #include "base58.h" |
ae775b5b | 8 | #include "core_io.h" |
e3bc5698 | 9 | #include "init.h" |
8a893c94 | 10 | #include "main.h" |
51ed9ec9 BD |
11 | #include "net.h" |
12 | #include "netbase.h" | |
8a893c94 | 13 | #include "rpcserver.h" |
14f888ca | 14 | #include "timedata.h" |
51ed9ec9 | 15 | #include "util.h" |
b93173de | 16 | #include "utilmoneystr.h" |
51ed9ec9 BD |
17 | #include "wallet.h" |
18 | #include "walletdb.h" | |
8cb25088 | 19 | #include "primitives/transaction.h" |
6962bb3d | 20 | #include "zcbenchmarks.h" |
6aae9d1a | 21 | #include "script/interpreter.h" |
51ed9ec9 | 22 | |
fc72c078 S |
23 | #include "utiltime.h" |
24 | #include "asyncrpcoperation.h" | |
25 | #include "wallet/asyncrpcoperation_sendmany.h" | |
26 | ||
320f2cc7 SB |
27 | #include "sodium.h" |
28 | ||
51ed9ec9 BD |
29 | #include <stdint.h> |
30 | ||
31 | #include <boost/assign/list_of.hpp> | |
25cf6f3d | 32 | |
51ed9ec9 BD |
33 | #include "json/json_spirit_utils.h" |
34 | #include "json/json_spirit_value.h" | |
fc72c078 | 35 | #include "asyncrpcqueue.h" |
e3bc5698 | 36 | |
e3bc5698 | 37 | using namespace std; |
fdbb537d | 38 | using namespace json_spirit; |
e3bc5698 | 39 | |
2dc35992 SB |
40 | using namespace libzcash; |
41 | ||
f7cfb52d S |
42 | extern Array TxJoinSplitToJSON(const CTransaction& tx); |
43 | ||
51ed9ec9 | 44 | int64_t nWalletUnlockTime; |
e3bc5698 JG |
45 | static CCriticalSection cs_nWalletUnlockTime; |
46 | ||
c1eae280 S |
47 | // Private method: |
48 | Value z_getoperationstatus_IMPL(const Array&, bool); | |
49 | ||
bdab0cf5 | 50 | std::string HelpRequiringPassphrase() |
e3bc5698 | 51 | { |
b0730874 | 52 | return pwalletMain && pwalletMain->IsCrypted() |
a6099ef3 | 53 | ? "\nRequires wallet passphrase to be set with walletpassphrase call." |
e3bc5698 JG |
54 | : ""; |
55 | } | |
56 | ||
b9fb692d JS |
57 | bool EnsureWalletIsAvailable(bool avoidException) |
58 | { | |
59 | if (!pwalletMain) | |
60 | { | |
61 | if (!avoidException) | |
62 | throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); | |
63 | else | |
64 | return false; | |
65 | } | |
66 | return true; | |
67 | } | |
68 | ||
bdab0cf5 | 69 | void EnsureWalletIsUnlocked() |
e3bc5698 JG |
70 | { |
71 | if (pwalletMain->IsLocked()) | |
738835d7 | 72 | throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); |
e3bc5698 JG |
73 | } |
74 | ||
75 | void WalletTxToJSON(const CWalletTx& wtx, Object& entry) | |
76 | { | |
77 | int confirms = wtx.GetDepthInMainChain(); | |
78 | entry.push_back(Pair("confirmations", confirms)); | |
e07c8e91 LD |
79 | if (wtx.IsCoinBase()) |
80 | entry.push_back(Pair("generated", true)); | |
2b72d46f | 81 | if (confirms > 0) |
e3bc5698 JG |
82 | { |
83 | entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); | |
84 | entry.push_back(Pair("blockindex", wtx.nIndex)); | |
209377a7 | 85 | entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); |
e3bc5698 | 86 | } |
805344dc | 87 | uint256 hash = wtx.GetHash(); |
731b89b8 GA |
88 | entry.push_back(Pair("txid", hash.GetHex())); |
89 | Array conflicts; | |
90 | BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts()) | |
91 | conflicts.push_back(conflict.GetHex()); | |
92 | entry.push_back(Pair("walletconflicts", conflicts)); | |
d56e30ca | 93 | entry.push_back(Pair("time", wtx.GetTxTime())); |
4b61a6a4 | 94 | entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); |
e3bc5698 JG |
95 | BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) |
96 | entry.push_back(Pair(item.first, item.second)); | |
f7cfb52d S |
97 | |
98 | entry.push_back(Pair("vjoinsplit", TxJoinSplitToJSON(wtx))); | |
e3bc5698 JG |
99 | } |
100 | ||
101 | string AccountFromValue(const Value& value) | |
102 | { | |
103 | string strAccount = value.get_str(); | |
3c31eb24 | 104 | if (strAccount != "") |
7b3351ff | 105 | throw JSONRPCError(RPC_WALLET_ACCOUNTS_UNSUPPORTED, "Accounts are unsupported"); |
e3bc5698 JG |
106 | return strAccount; |
107 | } | |
108 | ||
109 | Value getnewaddress(const Array& params, bool fHelp) | |
110 | { | |
b9fb692d JS |
111 | if (!EnsureWalletIsAvailable(fHelp)) |
112 | return Value::null; | |
113 | ||
e3bc5698 JG |
114 | if (fHelp || params.size() > 1) |
115 | throw runtime_error( | |
a6099ef3 | 116 | "getnewaddress ( \"account\" )\n" |
58c4c0bb | 117 | "\nReturns a new Zcash address for receiving payments.\n" |
a6099ef3 | 118 | "\nArguments:\n" |
3c31eb24 | 119 | "1. \"account\" (string, optional) DEPRECATED. If provided, it MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" |
a6099ef3 | 120 | "\nResult:\n" |
58c4c0bb | 121 | "\"zcashaddress\" (string) The new zcash address\n" |
a6099ef3 | 122 | "\nExamples:\n" |
123 | + HelpExampleCli("getnewaddress", "") | |
7b782f5b | 124 | + HelpExampleRpc("getnewaddress", "") |
a6099ef3 | 125 | ); |
e3bc5698 | 126 | |
4401b2d7 EL |
127 | LOCK2(cs_main, pwalletMain->cs_wallet); |
128 | ||
e3bc5698 JG |
129 | // Parse the account first so we don't generate a key if there's an error |
130 | string strAccount; | |
131 | if (params.size() > 0) | |
132 | strAccount = AccountFromValue(params[0]); | |
133 | ||
134 | if (!pwalletMain->IsLocked()) | |
135 | pwalletMain->TopUpKeyPool(); | |
136 | ||
137 | // Generate a new key that is added to wallet | |
138 | CPubKey newKey; | |
71ac5052 | 139 | if (!pwalletMain->GetKeyFromPool(newKey)) |
738835d7 | 140 | throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); |
e3bc5698 JG |
141 | CKeyID keyID = newKey.GetID(); |
142 | ||
a41d5fe0 | 143 | pwalletMain->SetAddressBook(keyID, strAccount, "receive"); |
e3bc5698 JG |
144 | |
145 | return CBitcoinAddress(keyID).ToString(); | |
146 | } | |
147 | ||
148 | ||
149 | CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) | |
150 | { | |
151 | CWalletDB walletdb(pwalletMain->strWalletFile); | |
152 | ||
153 | CAccount account; | |
154 | walletdb.ReadAccount(strAccount, account); | |
155 | ||
156 | bool bKeyUsed = false; | |
157 | ||
158 | // Check if the current key has been used | |
159 | if (account.vchPubKey.IsValid()) | |
160 | { | |
0be990ba | 161 | CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID()); |
e3bc5698 JG |
162 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); |
163 | it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid(); | |
164 | ++it) | |
165 | { | |
166 | const CWalletTx& wtx = (*it).second; | |
167 | BOOST_FOREACH(const CTxOut& txout, wtx.vout) | |
168 | if (txout.scriptPubKey == scriptPubKey) | |
169 | bKeyUsed = true; | |
170 | } | |
171 | } | |
172 | ||
173 | // Generate a new key | |
174 | if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed) | |
175 | { | |
71ac5052 | 176 | if (!pwalletMain->GetKeyFromPool(account.vchPubKey)) |
738835d7 | 177 | throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); |
e3bc5698 | 178 | |
a41d5fe0 | 179 | pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); |
e3bc5698 JG |
180 | walletdb.WriteAccount(strAccount, account); |
181 | } | |
182 | ||
183 | return CBitcoinAddress(account.vchPubKey.GetID()); | |
184 | } | |
185 | ||
186 | Value getaccountaddress(const Array& params, bool fHelp) | |
187 | { | |
b9fb692d JS |
188 | if (!EnsureWalletIsAvailable(fHelp)) |
189 | return Value::null; | |
190 | ||
e3bc5698 JG |
191 | if (fHelp || params.size() != 1) |
192 | throw runtime_error( | |
a6099ef3 | 193 | "getaccountaddress \"account\"\n" |
58c4c0bb | 194 | "\nDEPRECATED. Returns the current Zcash address for receiving payments to this account.\n" |
a6099ef3 | 195 | "\nArguments:\n" |
3c31eb24 | 196 | "1. \"account\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" |
a6099ef3 | 197 | "\nResult:\n" |
58c4c0bb | 198 | "\"zcashaddress\" (string) The account zcash address\n" |
a6099ef3 | 199 | "\nExamples:\n" |
200 | + HelpExampleCli("getaccountaddress", "") | |
201 | + HelpExampleCli("getaccountaddress", "\"\"") | |
202 | + HelpExampleCli("getaccountaddress", "\"myaccount\"") | |
203 | + HelpExampleRpc("getaccountaddress", "\"myaccount\"") | |
204 | ); | |
e3bc5698 | 205 | |
4401b2d7 EL |
206 | LOCK2(cs_main, pwalletMain->cs_wallet); |
207 | ||
e3bc5698 JG |
208 | // Parse the account first so we don't generate a key if there's an error |
209 | string strAccount = AccountFromValue(params[0]); | |
210 | ||
211 | Value ret; | |
212 | ||
213 | ret = GetAccountAddress(strAccount).ToString(); | |
e3bc5698 JG |
214 | return ret; |
215 | } | |
216 | ||
217 | ||
e5e9904c JG |
218 | Value getrawchangeaddress(const Array& params, bool fHelp) |
219 | { | |
b9fb692d JS |
220 | if (!EnsureWalletIsAvailable(fHelp)) |
221 | return Value::null; | |
222 | ||
e5e9904c JG |
223 | if (fHelp || params.size() > 1) |
224 | throw runtime_error( | |
225 | "getrawchangeaddress\n" | |
58c4c0bb | 226 | "\nReturns a new Zcash address, for receiving change.\n" |
a6099ef3 | 227 | "This is for use with raw transactions, NOT normal use.\n" |
228 | "\nResult:\n" | |
229 | "\"address\" (string) The address\n" | |
230 | "\nExamples:\n" | |
231 | + HelpExampleCli("getrawchangeaddress", "") | |
232 | + HelpExampleRpc("getrawchangeaddress", "") | |
233 | ); | |
e5e9904c | 234 | |
4401b2d7 EL |
235 | LOCK2(cs_main, pwalletMain->cs_wallet); |
236 | ||
e5e9904c JG |
237 | if (!pwalletMain->IsLocked()) |
238 | pwalletMain->TopUpKeyPool(); | |
239 | ||
240 | CReserveKey reservekey(pwalletMain); | |
241 | CPubKey vchPubKey; | |
242 | if (!reservekey.GetReservedKey(vchPubKey)) | |
6c37f7fd | 243 | throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); |
e5e9904c JG |
244 | |
245 | reservekey.KeepKey(); | |
246 | ||
247 | CKeyID keyID = vchPubKey.GetID(); | |
248 | ||
249 | return CBitcoinAddress(keyID).ToString(); | |
250 | } | |
251 | ||
e3bc5698 JG |
252 | |
253 | Value setaccount(const Array& params, bool fHelp) | |
254 | { | |
b9fb692d JS |
255 | if (!EnsureWalletIsAvailable(fHelp)) |
256 | return Value::null; | |
257 | ||
e3bc5698 JG |
258 | if (fHelp || params.size() < 1 || params.size() > 2) |
259 | throw runtime_error( | |
58c4c0bb | 260 | "setaccount \"zcashaddress\" \"account\"\n" |
7b782f5b | 261 | "\nDEPRECATED. Sets the account associated with the given address.\n" |
a6099ef3 | 262 | "\nArguments:\n" |
58c4c0bb | 263 | "1. \"zcashaddress\" (string, required) The zcash address to be associated with an account.\n" |
3c31eb24 | 264 | "2. \"account\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" |
a6099ef3 | 265 | "\nExamples:\n" |
266 | + HelpExampleCli("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"tabby\"") | |
267 | + HelpExampleRpc("setaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"tabby\"") | |
268 | ); | |
e3bc5698 | 269 | |
4401b2d7 EL |
270 | LOCK2(cs_main, pwalletMain->cs_wallet); |
271 | ||
e3bc5698 JG |
272 | CBitcoinAddress address(params[0].get_str()); |
273 | if (!address.IsValid()) | |
58c4c0bb | 274 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); |
e3bc5698 | 275 | |
e3bc5698 JG |
276 | string strAccount; |
277 | if (params.size() > 1) | |
278 | strAccount = AccountFromValue(params[1]); | |
279 | ||
31d6390f ES |
280 | // Only add the account if the address is yours. |
281 | if (IsMine(*pwalletMain, address.Get())) | |
e3bc5698 | 282 | { |
31d6390f ES |
283 | // Detect when changing the account of an address that is the 'unused current key' of another account: |
284 | if (pwalletMain->mapAddressBook.count(address.Get())) | |
285 | { | |
286 | string strOldAccount = pwalletMain->mapAddressBook[address.Get()].name; | |
287 | if (address == GetAccountAddress(strOldAccount)) | |
288 | GetAccountAddress(strOldAccount, true); | |
289 | } | |
290 | pwalletMain->SetAddressBook(address.Get(), strAccount, "receive"); | |
e3bc5698 | 291 | } |
31d6390f ES |
292 | else |
293 | throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); | |
e3bc5698 JG |
294 | |
295 | return Value::null; | |
296 | } | |
297 | ||
298 | ||
299 | Value getaccount(const Array& params, bool fHelp) | |
300 | { | |
b9fb692d JS |
301 | if (!EnsureWalletIsAvailable(fHelp)) |
302 | return Value::null; | |
303 | ||
e3bc5698 JG |
304 | if (fHelp || params.size() != 1) |
305 | throw runtime_error( | |
58c4c0bb | 306 | "getaccount \"zcashaddress\"\n" |
7b782f5b | 307 | "\nDEPRECATED. Returns the account associated with the given address.\n" |
a6099ef3 | 308 | "\nArguments:\n" |
58c4c0bb | 309 | "1. \"zcashaddress\" (string, required) The zcash address for account lookup.\n" |
a6099ef3 | 310 | "\nResult:\n" |
311 | "\"accountname\" (string) the account address\n" | |
312 | "\nExamples:\n" | |
313 | + HelpExampleCli("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") | |
314 | + HelpExampleRpc("getaccount", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") | |
315 | ); | |
e3bc5698 | 316 | |
4401b2d7 EL |
317 | LOCK2(cs_main, pwalletMain->cs_wallet); |
318 | ||
e3bc5698 JG |
319 | CBitcoinAddress address(params[0].get_str()); |
320 | if (!address.IsValid()) | |
58c4c0bb | 321 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); |
e3bc5698 JG |
322 | |
323 | string strAccount; | |
61885513 GA |
324 | map<CTxDestination, CAddressBookData>::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); |
325 | if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.name.empty()) | |
326 | strAccount = (*mi).second.name; | |
e3bc5698 JG |
327 | return strAccount; |
328 | } | |
329 | ||
330 | ||
331 | Value getaddressesbyaccount(const Array& params, bool fHelp) | |
332 | { | |
b9fb692d JS |
333 | if (!EnsureWalletIsAvailable(fHelp)) |
334 | return Value::null; | |
335 | ||
e3bc5698 JG |
336 | if (fHelp || params.size() != 1) |
337 | throw runtime_error( | |
a6099ef3 | 338 | "getaddressesbyaccount \"account\"\n" |
7b782f5b | 339 | "\nDEPRECATED. Returns the list of addresses for the given account.\n" |
a6099ef3 | 340 | "\nArguments:\n" |
3c31eb24 | 341 | "1. \"account\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" |
a6099ef3 | 342 | "\nResult:\n" |
343 | "[ (json array of string)\n" | |
58c4c0bb | 344 | " \"zcashaddress\" (string) a zcash address associated with the given account\n" |
a6099ef3 | 345 | " ,...\n" |
346 | "]\n" | |
347 | "\nExamples:\n" | |
348 | + HelpExampleCli("getaddressesbyaccount", "\"tabby\"") | |
349 | + HelpExampleRpc("getaddressesbyaccount", "\"tabby\"") | |
350 | ); | |
e3bc5698 | 351 | |
4401b2d7 EL |
352 | LOCK2(cs_main, pwalletMain->cs_wallet); |
353 | ||
e3bc5698 JG |
354 | string strAccount = AccountFromValue(params[0]); |
355 | ||
356 | // Find all addresses that have the given account | |
357 | Array ret; | |
61885513 | 358 | BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) |
e3bc5698 JG |
359 | { |
360 | const CBitcoinAddress& address = item.first; | |
61885513 | 361 | const string& strName = item.second.name; |
e3bc5698 JG |
362 | if (strName == strAccount) |
363 | ret.push_back(address.ToString()); | |
364 | } | |
365 | return ret; | |
366 | } | |
367 | ||
292623ad | 368 | static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) |
b93173de | 369 | { |
25cf6f3d PK |
370 | CAmount curBalance = pwalletMain->GetBalance(); |
371 | ||
b93173de PJ |
372 | // Check amount |
373 | if (nValue <= 0) | |
4be639ea | 374 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); |
b93173de | 375 | |
25cf6f3d | 376 | if (nValue > curBalance) |
b93173de PJ |
377 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); |
378 | ||
58c4c0bb | 379 | // Parse Zcash address |
b93173de PJ |
380 | CScript scriptPubKey = GetScriptForDestination(address); |
381 | ||
382 | // Create and send the transaction | |
383 | CReserveKey reservekey(pwalletMain); | |
384 | CAmount nFeeRequired; | |
25cf6f3d | 385 | std::string strError; |
292623ad CL |
386 | vector<CRecipient> vecSend; |
387 | int nChangePosRet = -1; | |
388 | CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; | |
389 | vecSend.push_back(recipient); | |
390 | if (!pwalletMain->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { | |
391 | if (!fSubtractFeeFromAmount && nValue + nFeeRequired > pwalletMain->GetBalance()) | |
392 | strError = strprintf("Error: This transaction requires a transaction fee of at least %s because of its amount, complexity, or use of recently received funds!", FormatMoney(nFeeRequired)); | |
393 | throw JSONRPCError(RPC_WALLET_ERROR, strError); | |
b93173de PJ |
394 | } |
395 | if (!pwalletMain->CommitTransaction(wtxNew, reservekey)) | |
396 | throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of wallet.dat and coins were spent in the copy but not marked as spent here."); | |
b93173de PJ |
397 | } |
398 | ||
e3bc5698 JG |
399 | Value sendtoaddress(const Array& params, bool fHelp) |
400 | { | |
b9fb692d JS |
401 | if (!EnsureWalletIsAvailable(fHelp)) |
402 | return Value::null; | |
403 | ||
292623ad | 404 | if (fHelp || params.size() < 2 || params.size() > 5) |
e3bc5698 | 405 | throw runtime_error( |
58c4c0bb | 406 | "sendtoaddress \"zcashaddress\" amount ( \"comment\" \"comment-to\" subtractfeefromamount )\n" |
e743678d | 407 | "\nSend an amount to a given address. The amount is a real and is rounded to the nearest 0.00000001\n" |
a6099ef3 | 408 | + HelpRequiringPassphrase() + |
409 | "\nArguments:\n" | |
58c4c0bb | 410 | "1. \"zcashaddress\" (string, required) The zcash address to send to.\n" |
a6099ef3 | 411 | "2. \"amount\" (numeric, required) The amount in btc to send. eg 0.1\n" |
412 | "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" | |
413 | " This is not part of the transaction, just kept in your wallet.\n" | |
414 | "4. \"comment-to\" (string, optional) A comment to store the name of the person or organization \n" | |
415 | " to which you're sending the transaction. This is not part of the \n" | |
416 | " transaction, just kept in your wallet.\n" | |
292623ad | 417 | "5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n" |
58c4c0bb | 418 | " The recipient will receive less zcash than you enter in the amount field.\n" |
a6099ef3 | 419 | "\nResult:\n" |
b5ef85c7 | 420 | "\"transactionid\" (string) The transaction id.\n" |
a6099ef3 | 421 | "\nExamples:\n" |
422 | + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") | |
423 | + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"") | |
292623ad | 424 | + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") |
a6099ef3 | 425 | + HelpExampleRpc("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") |
426 | ); | |
e3bc5698 | 427 | |
4401b2d7 EL |
428 | LOCK2(cs_main, pwalletMain->cs_wallet); |
429 | ||
e3bc5698 JG |
430 | CBitcoinAddress address(params[0].get_str()); |
431 | if (!address.IsValid()) | |
58c4c0bb | 432 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); |
e3bc5698 JG |
433 | |
434 | // Amount | |
a372168e | 435 | CAmount nAmount = AmountFromValue(params[1]); |
e3bc5698 JG |
436 | |
437 | // Wallet comments | |
438 | CWalletTx wtx; | |
439 | if (params.size() > 2 && params[2].type() != null_type && !params[2].get_str().empty()) | |
440 | wtx.mapValue["comment"] = params[2].get_str(); | |
441 | if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) | |
442 | wtx.mapValue["to"] = params[3].get_str(); | |
443 | ||
292623ad CL |
444 | bool fSubtractFeeFromAmount = false; |
445 | if (params.size() > 4) | |
446 | fSubtractFeeFromAmount = params[4].get_bool(); | |
447 | ||
f914c7a1 | 448 | EnsureWalletIsUnlocked(); |
e3bc5698 | 449 | |
292623ad | 450 | SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx); |
e3bc5698 | 451 | |
805344dc | 452 | return wtx.GetHash().GetHex(); |
e3bc5698 JG |
453 | } |
454 | ||
22dfd735 | 455 | Value listaddressgroupings(const Array& params, bool fHelp) |
456 | { | |
b9fb692d JS |
457 | if (!EnsureWalletIsAvailable(fHelp)) |
458 | return Value::null; | |
459 | ||
22dfd735 | 460 | if (fHelp) |
b1093efa GM |
461 | throw runtime_error( |
462 | "listaddressgroupings\n" | |
a6099ef3 | 463 | "\nLists groups of addresses which have had their common ownership\n" |
b1093efa | 464 | "made public by common use as inputs or as the resulting change\n" |
a6099ef3 | 465 | "in past transactions\n" |
466 | "\nResult:\n" | |
467 | "[\n" | |
468 | " [\n" | |
469 | " [\n" | |
58c4c0bb | 470 | " \"zcashaddress\", (string) The zcash address\n" |
a6099ef3 | 471 | " amount, (numeric) The amount in btc\n" |
7b782f5b | 472 | " \"account\" (string, optional) The account (DEPRECATED)\n" |
a6099ef3 | 473 | " ]\n" |
474 | " ,...\n" | |
475 | " ]\n" | |
476 | " ,...\n" | |
477 | "]\n" | |
478 | "\nExamples:\n" | |
479 | + HelpExampleCli("listaddressgroupings", "") | |
480 | + HelpExampleRpc("listaddressgroupings", "") | |
481 | ); | |
22dfd735 | 482 | |
4401b2d7 EL |
483 | LOCK2(cs_main, pwalletMain->cs_wallet); |
484 | ||
22dfd735 | 485 | Array jsonGroupings; |
a372168e | 486 | map<CTxDestination, CAmount> balances = pwalletMain->GetAddressBalances(); |
b1093efa | 487 | BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings()) |
22dfd735 | 488 | { |
489 | Array jsonGrouping; | |
b1093efa | 490 | BOOST_FOREACH(CTxDestination address, grouping) |
22dfd735 | 491 | { |
492 | Array addressInfo; | |
b1093efa | 493 | addressInfo.push_back(CBitcoinAddress(address).ToString()); |
22dfd735 | 494 | addressInfo.push_back(ValueFromAmount(balances[address])); |
495 | { | |
22dfd735 | 496 | if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end()) |
61885513 | 497 | addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name); |
22dfd735 | 498 | } |
499 | jsonGrouping.push_back(addressInfo); | |
500 | } | |
501 | jsonGroupings.push_back(jsonGrouping); | |
502 | } | |
503 | return jsonGroupings; | |
504 | } | |
505 | ||
e3bc5698 JG |
506 | Value signmessage(const Array& params, bool fHelp) |
507 | { | |
b9fb692d JS |
508 | if (!EnsureWalletIsAvailable(fHelp)) |
509 | return Value::null; | |
510 | ||
e3bc5698 JG |
511 | if (fHelp || params.size() != 2) |
512 | throw runtime_error( | |
58c4c0bb | 513 | "signmessage \"zcashaddress\" \"message\"\n" |
a6099ef3 | 514 | "\nSign a message with the private key of an address" |
515 | + HelpRequiringPassphrase() + "\n" | |
516 | "\nArguments:\n" | |
58c4c0bb | 517 | "1. \"zcashaddress\" (string, required) The zcash address to use for the private key.\n" |
a6099ef3 | 518 | "2. \"message\" (string, required) The message to create a signature of.\n" |
519 | "\nResult:\n" | |
520 | "\"signature\" (string) The signature of the message encoded in base 64\n" | |
521 | "\nExamples:\n" | |
522 | "\nUnlock the wallet for 30 seconds\n" | |
523 | + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + | |
524 | "\nCreate the signature\n" | |
525 | + HelpExampleCli("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"my message\"") + | |
526 | "\nVerify the signature\n" | |
527 | + HelpExampleCli("verifymessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" \"signature\" \"my message\"") + | |
528 | "\nAs json rpc\n" | |
529 | + HelpExampleRpc("signmessage", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", \"my message\"") | |
530 | ); | |
e3bc5698 | 531 | |
4401b2d7 EL |
532 | LOCK2(cs_main, pwalletMain->cs_wallet); |
533 | ||
e3bc5698 JG |
534 | EnsureWalletIsUnlocked(); |
535 | ||
536 | string strAddress = params[0].get_str(); | |
537 | string strMessage = params[1].get_str(); | |
538 | ||
539 | CBitcoinAddress addr(strAddress); | |
540 | if (!addr.IsValid()) | |
738835d7 | 541 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); |
e3bc5698 JG |
542 | |
543 | CKeyID keyID; | |
544 | if (!addr.GetKeyID(keyID)) | |
738835d7 | 545 | throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); |
e3bc5698 JG |
546 | |
547 | CKey key; | |
548 | if (!pwalletMain->GetKey(keyID, key)) | |
738835d7 | 549 | throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); |
e3bc5698 | 550 | |
8980a509 | 551 | CHashWriter ss(SER_GETHASH, 0); |
e3bc5698 JG |
552 | ss << strMessageMagic; |
553 | ss << strMessage; | |
554 | ||
555 | vector<unsigned char> vchSig; | |
8980a509 | 556 | if (!key.SignCompact(ss.GetHash(), vchSig)) |
738835d7 | 557 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); |
e3bc5698 JG |
558 | |
559 | return EncodeBase64(&vchSig[0], vchSig.size()); | |
560 | } | |
561 | ||
e3bc5698 JG |
562 | Value getreceivedbyaddress(const Array& params, bool fHelp) |
563 | { | |
b9fb692d JS |
564 | if (!EnsureWalletIsAvailable(fHelp)) |
565 | return Value::null; | |
566 | ||
e3bc5698 JG |
567 | if (fHelp || params.size() < 1 || params.size() > 2) |
568 | throw runtime_error( | |
58c4c0bb SB |
569 | "getreceivedbyaddress \"zcashaddress\" ( minconf )\n" |
570 | "\nReturns the total amount received by the given zcashaddress in transactions with at least minconf confirmations.\n" | |
a6099ef3 | 571 | "\nArguments:\n" |
58c4c0bb | 572 | "1. \"zcashaddress\" (string, required) The zcash address for transactions.\n" |
a6099ef3 | 573 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" |
574 | "\nResult:\n" | |
575 | "amount (numeric) The total amount in btc received at this address.\n" | |
576 | "\nExamples:\n" | |
577 | "\nThe amount from transactions with at least 1 confirmation\n" | |
578 | + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\"") + | |
579 | "\nThe amount including unconfirmed transactions, zero confirmations\n" | |
580 | + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" 0") + | |
581 | "\nThe amount with at least 6 confirmation, very safe\n" | |
582 | + HelpExampleCli("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\" 6") + | |
583 | "\nAs a json rpc call\n" | |
584 | + HelpExampleRpc("getreceivedbyaddress", "\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\", 6") | |
585 | ); | |
e3bc5698 | 586 | |
4401b2d7 EL |
587 | LOCK2(cs_main, pwalletMain->cs_wallet); |
588 | ||
e3bc5698 JG |
589 | // Bitcoin address |
590 | CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); | |
e3bc5698 | 591 | if (!address.IsValid()) |
58c4c0bb | 592 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); |
0be990ba | 593 | CScript scriptPubKey = GetScriptForDestination(address.Get()); |
e3bc5698 JG |
594 | if (!IsMine(*pwalletMain,scriptPubKey)) |
595 | return (double)0.0; | |
596 | ||
597 | // Minimum confirmations | |
598 | int nMinDepth = 1; | |
599 | if (params.size() > 1) | |
600 | nMinDepth = params[1].get_int(); | |
601 | ||
602 | // Tally | |
a372168e | 603 | CAmount nAmount = 0; |
e3bc5698 JG |
604 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) |
605 | { | |
606 | const CWalletTx& wtx = (*it).second; | |
75a4d512 | 607 | if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) |
e3bc5698 JG |
608 | continue; |
609 | ||
610 | BOOST_FOREACH(const CTxOut& txout, wtx.vout) | |
611 | if (txout.scriptPubKey == scriptPubKey) | |
612 | if (wtx.GetDepthInMainChain() >= nMinDepth) | |
613 | nAmount += txout.nValue; | |
614 | } | |
615 | ||
616 | return ValueFromAmount(nAmount); | |
617 | } | |
618 | ||
619 | ||
e3bc5698 JG |
620 | Value getreceivedbyaccount(const Array& params, bool fHelp) |
621 | { | |
b9fb692d JS |
622 | if (!EnsureWalletIsAvailable(fHelp)) |
623 | return Value::null; | |
624 | ||
e3bc5698 JG |
625 | if (fHelp || params.size() < 1 || params.size() > 2) |
626 | throw runtime_error( | |
a6099ef3 | 627 | "getreceivedbyaccount \"account\" ( minconf )\n" |
7b782f5b | 628 | "\nDEPRECATED. Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.\n" |
a6099ef3 | 629 | "\nArguments:\n" |
3c31eb24 | 630 | "1. \"account\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" |
a6099ef3 | 631 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" |
632 | "\nResult:\n" | |
633 | "amount (numeric) The total amount in btc received for this account.\n" | |
634 | "\nExamples:\n" | |
635 | "\nAmount received by the default account with at least 1 confirmation\n" | |
636 | + HelpExampleCli("getreceivedbyaccount", "\"\"") + | |
637 | "\nAmount received at the tabby account including unconfirmed amounts with zero confirmations\n" | |
638 | + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 0") + | |
639 | "\nThe amount with at least 6 confirmation, very safe\n" | |
640 | + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 6") + | |
641 | "\nAs a json rpc call\n" | |
642 | + HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6") | |
643 | ); | |
e3bc5698 | 644 | |
4401b2d7 EL |
645 | LOCK2(cs_main, pwalletMain->cs_wallet); |
646 | ||
e3bc5698 JG |
647 | // Minimum confirmations |
648 | int nMinDepth = 1; | |
649 | if (params.size() > 1) | |
650 | nMinDepth = params[1].get_int(); | |
651 | ||
652 | // Get the set of pub keys assigned to account | |
653 | string strAccount = AccountFromValue(params[0]); | |
3624356e | 654 | set<CTxDestination> setAddress = pwalletMain->GetAccountAddresses(strAccount); |
e3bc5698 JG |
655 | |
656 | // Tally | |
a372168e | 657 | CAmount nAmount = 0; |
e3bc5698 JG |
658 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) |
659 | { | |
660 | const CWalletTx& wtx = (*it).second; | |
75a4d512 | 661 | if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) |
e3bc5698 JG |
662 | continue; |
663 | ||
664 | BOOST_FOREACH(const CTxOut& txout, wtx.vout) | |
665 | { | |
666 | CTxDestination address; | |
667 | if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) | |
668 | if (wtx.GetDepthInMainChain() >= nMinDepth) | |
669 | nAmount += txout.nValue; | |
670 | } | |
671 | } | |
672 | ||
673 | return (double)nAmount / (double)COIN; | |
674 | } | |
675 | ||
676 | ||
a372168e | 677 | CAmount GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter) |
e3bc5698 | 678 | { |
a372168e | 679 | CAmount nBalance = 0; |
e3bc5698 JG |
680 | |
681 | // Tally wallet transactions | |
682 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) | |
683 | { | |
684 | const CWalletTx& wtx = (*it).second; | |
75a4d512 | 685 | if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) |
e3bc5698 JG |
686 | continue; |
687 | ||
a372168e | 688 | CAmount nReceived, nSent, nFee; |
d4640d7d | 689 | wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); |
e3bc5698 JG |
690 | |
691 | if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) | |
692 | nBalance += nReceived; | |
e07c8e91 | 693 | nBalance -= nSent + nFee; |
e3bc5698 JG |
694 | } |
695 | ||
696 | // Tally internal accounting entries | |
697 | nBalance += walletdb.GetAccountCreditDebit(strAccount); | |
698 | ||
699 | return nBalance; | |
700 | } | |
701 | ||
a372168e | 702 | CAmount GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter) |
e3bc5698 JG |
703 | { |
704 | CWalletDB walletdb(pwalletMain->strWalletFile); | |
d4640d7d | 705 | return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); |
e3bc5698 JG |
706 | } |
707 | ||
708 | ||
709 | Value getbalance(const Array& params, bool fHelp) | |
710 | { | |
b9fb692d JS |
711 | if (!EnsureWalletIsAvailable(fHelp)) |
712 | return Value::null; | |
713 | ||
d4640d7d | 714 | if (fHelp || params.size() > 3) |
e3bc5698 | 715 | throw runtime_error( |
d4640d7d | 716 | "getbalance ( \"account\" minconf includeWatchonly )\n" |
3c31eb24 | 717 | "\nReturns the server's total available balance.\n" |
a6099ef3 | 718 | "\nArguments:\n" |
715e5bbe | 719 | "1. \"account\" (string, optional) DEPRECATED. If provided, it MUST be set to the empty string \"\" or to the string \"*\", either of which will give the total available balance. Passing any other string will result in an error.\n" |
a6099ef3 | 720 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" |
d4640d7d | 721 | "3. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n" |
a6099ef3 | 722 | "\nResult:\n" |
723 | "amount (numeric) The total amount in btc received for this account.\n" | |
724 | "\nExamples:\n" | |
7b782f5b | 725 | "\nThe total amount in the wallet\n" |
a6099ef3 | 726 | + HelpExampleCli("getbalance", "") + |
7b782f5b | 727 | "\nThe total amount in the wallet at least 5 blocks confirmed\n" |
3cf1f436 | 728 | + HelpExampleCli("getbalance", "\"*\" 6") + |
a6099ef3 | 729 | "\nAs a json rpc call\n" |
7b782f5b | 730 | + HelpExampleRpc("getbalance", "\"*\", 6") |
a6099ef3 | 731 | ); |
e3bc5698 | 732 | |
4401b2d7 EL |
733 | LOCK2(cs_main, pwalletMain->cs_wallet); |
734 | ||
e3bc5698 JG |
735 | if (params.size() == 0) |
736 | return ValueFromAmount(pwalletMain->GetBalance()); | |
737 | ||
738 | int nMinDepth = 1; | |
739 | if (params.size() > 1) | |
740 | nMinDepth = params[1].get_int(); | |
a3e192a3 | 741 | isminefilter filter = ISMINE_SPENDABLE; |
a5c6c5d6 J |
742 | if(params.size() > 2) |
743 | if(params[2].get_bool()) | |
a3e192a3 | 744 | filter = filter | ISMINE_WATCH_ONLY; |
e3bc5698 JG |
745 | |
746 | if (params[0].get_str() == "*") { | |
747 | // Calculate total balance a different way from GetBalance() | |
748 | // (GetBalance() sums up all unspent TxOuts) | |
c9fd9078 | 749 | // getbalance and "getbalance * 1 true" should return the same number |
a372168e | 750 | CAmount nBalance = 0; |
e3bc5698 JG |
751 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) |
752 | { | |
753 | const CWalletTx& wtx = (*it).second; | |
c9fd9078 | 754 | if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) |
e3bc5698 JG |
755 | continue; |
756 | ||
a372168e | 757 | CAmount allFee; |
e3bc5698 | 758 | string strSentAccount; |
1b4568cb CL |
759 | list<COutputEntry> listReceived; |
760 | list<COutputEntry> listSent; | |
d4640d7d | 761 | wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); |
e3bc5698 JG |
762 | if (wtx.GetDepthInMainChain() >= nMinDepth) |
763 | { | |
1b4568cb CL |
764 | BOOST_FOREACH(const COutputEntry& r, listReceived) |
765 | nBalance += r.amount; | |
e3bc5698 | 766 | } |
1b4568cb CL |
767 | BOOST_FOREACH(const COutputEntry& s, listSent) |
768 | nBalance -= s.amount; | |
e3bc5698 | 769 | nBalance -= allFee; |
e3bc5698 JG |
770 | } |
771 | return ValueFromAmount(nBalance); | |
772 | } | |
773 | ||
774 | string strAccount = AccountFromValue(params[0]); | |
775 | ||
a372168e | 776 | CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, filter); |
e3bc5698 JG |
777 | |
778 | return ValueFromAmount(nBalance); | |
779 | } | |
780 | ||
6027b460 MB |
781 | Value getunconfirmedbalance(const Array ¶ms, bool fHelp) |
782 | { | |
b9fb692d JS |
783 | if (!EnsureWalletIsAvailable(fHelp)) |
784 | return Value::null; | |
785 | ||
6027b460 MB |
786 | if (fHelp || params.size() > 0) |
787 | throw runtime_error( | |
788 | "getunconfirmedbalance\n" | |
789 | "Returns the server's total unconfirmed balance\n"); | |
4401b2d7 EL |
790 | |
791 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
792 | ||
6027b460 MB |
793 | return ValueFromAmount(pwalletMain->GetUnconfirmedBalance()); |
794 | } | |
795 | ||
e3bc5698 JG |
796 | |
797 | Value movecmd(const Array& params, bool fHelp) | |
798 | { | |
b9fb692d JS |
799 | if (!EnsureWalletIsAvailable(fHelp)) |
800 | return Value::null; | |
801 | ||
e3bc5698 JG |
802 | if (fHelp || params.size() < 3 || params.size() > 5) |
803 | throw runtime_error( | |
a6099ef3 | 804 | "move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n" |
7b782f5b | 805 | "\nDEPRECATED. Move a specified amount from one account in your wallet to another.\n" |
a6099ef3 | 806 | "\nArguments:\n" |
3c31eb24 JG |
807 | "1. \"fromaccount\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" |
808 | "2. \"toaccount\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" | |
a6099ef3 | 809 | "3. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" |
810 | "4. \"comment\" (string, optional) An optional comment, stored in the wallet only.\n" | |
811 | "\nResult:\n" | |
45bfa137 | 812 | "true|false (boolean) true if successful.\n" |
a6099ef3 | 813 | "\nExamples:\n" |
814 | "\nMove 0.01 btc from the default account to the account named tabby\n" | |
815 | + HelpExampleCli("move", "\"\" \"tabby\" 0.01") + | |
816 | "\nMove 0.01 btc timotei to akiko with a comment and funds have 6 confirmations\n" | |
817 | + HelpExampleCli("move", "\"timotei\" \"akiko\" 0.01 6 \"happy birthday!\"") + | |
818 | "\nAs a json rpc call\n" | |
819 | + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"") | |
820 | ); | |
e3bc5698 | 821 | |
4401b2d7 EL |
822 | LOCK2(cs_main, pwalletMain->cs_wallet); |
823 | ||
e3bc5698 JG |
824 | string strFrom = AccountFromValue(params[0]); |
825 | string strTo = AccountFromValue(params[1]); | |
a372168e | 826 | CAmount nAmount = AmountFromValue(params[2]); |
e3bc5698 JG |
827 | if (params.size() > 3) |
828 | // unused parameter, used to be nMinDepth, keep type-checking it though | |
829 | (void)params[3].get_int(); | |
830 | string strComment; | |
831 | if (params.size() > 4) | |
832 | strComment = params[4].get_str(); | |
833 | ||
834 | CWalletDB walletdb(pwalletMain->strWalletFile); | |
835 | if (!walletdb.TxnBegin()) | |
738835d7 | 836 | throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); |
e3bc5698 | 837 | |
51ed9ec9 | 838 | int64_t nNow = GetAdjustedTime(); |
e3bc5698 JG |
839 | |
840 | // Debit | |
841 | CAccountingEntry debit; | |
4291e8fe | 842 | debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); |
e3bc5698 JG |
843 | debit.strAccount = strFrom; |
844 | debit.nCreditDebit = -nAmount; | |
845 | debit.nTime = nNow; | |
846 | debit.strOtherAccount = strTo; | |
847 | debit.strComment = strComment; | |
848 | walletdb.WriteAccountingEntry(debit); | |
849 | ||
850 | // Credit | |
851 | CAccountingEntry credit; | |
4291e8fe | 852 | credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); |
e3bc5698 JG |
853 | credit.strAccount = strTo; |
854 | credit.nCreditDebit = nAmount; | |
855 | credit.nTime = nNow; | |
856 | credit.strOtherAccount = strFrom; | |
857 | credit.strComment = strComment; | |
858 | walletdb.WriteAccountingEntry(credit); | |
859 | ||
860 | if (!walletdb.TxnCommit()) | |
738835d7 | 861 | throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); |
e3bc5698 JG |
862 | |
863 | return true; | |
864 | } | |
865 | ||
866 | ||
867 | Value sendfrom(const Array& params, bool fHelp) | |
868 | { | |
b9fb692d JS |
869 | if (!EnsureWalletIsAvailable(fHelp)) |
870 | return Value::null; | |
871 | ||
e3bc5698 JG |
872 | if (fHelp || params.size() < 3 || params.size() > 6) |
873 | throw runtime_error( | |
58c4c0bb SB |
874 | "sendfrom \"fromaccount\" \"tozcashaddress\" amount ( minconf \"comment\" \"comment-to\" )\n" |
875 | "\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a zcash address.\n" | |
a6099ef3 | 876 | "The amount is a real and is rounded to the nearest 0.00000001." |
877 | + HelpRequiringPassphrase() + "\n" | |
878 | "\nArguments:\n" | |
3c31eb24 | 879 | "1. \"fromaccount\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" |
58c4c0bb | 880 | "2. \"tozcashaddress\" (string, required) The zcash address to send funds to.\n" |
a6099ef3 | 881 | "3. amount (numeric, required) The amount in btc. (transaction fee is added on top).\n" |
882 | "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" | |
883 | "5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" | |
884 | " This is not part of the transaction, just kept in your wallet.\n" | |
885 | "6. \"comment-to\" (string, optional) An optional comment to store the name of the person or organization \n" | |
886 | " to which you're sending the transaction. This is not part of the transaction, \n" | |
887 | " it is just kept in your wallet.\n" | |
888 | "\nResult:\n" | |
b5ef85c7 | 889 | "\"transactionid\" (string) The transaction id.\n" |
a6099ef3 | 890 | "\nExamples:\n" |
891 | "\nSend 0.01 btc from the default account to the address, must have at least 1 confirmation\n" | |
892 | + HelpExampleCli("sendfrom", "\"\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01") + | |
893 | "\nSend 0.01 from the tabby account to the given address, funds must have at least 6 confirmations\n" | |
894 | + HelpExampleCli("sendfrom", "\"tabby\" \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01 6 \"donation\" \"seans outpost\"") + | |
895 | "\nAs a json rpc call\n" | |
896 | + HelpExampleRpc("sendfrom", "\"tabby\", \"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"") | |
897 | ); | |
e3bc5698 | 898 | |
4401b2d7 EL |
899 | LOCK2(cs_main, pwalletMain->cs_wallet); |
900 | ||
e3bc5698 JG |
901 | string strAccount = AccountFromValue(params[0]); |
902 | CBitcoinAddress address(params[1].get_str()); | |
903 | if (!address.IsValid()) | |
58c4c0bb | 904 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); |
a372168e | 905 | CAmount nAmount = AmountFromValue(params[2]); |
e3bc5698 JG |
906 | int nMinDepth = 1; |
907 | if (params.size() > 3) | |
908 | nMinDepth = params[3].get_int(); | |
909 | ||
910 | CWalletTx wtx; | |
911 | wtx.strFromAccount = strAccount; | |
912 | if (params.size() > 4 && params[4].type() != null_type && !params[4].get_str().empty()) | |
913 | wtx.mapValue["comment"] = params[4].get_str(); | |
914 | if (params.size() > 5 && params[5].type() != null_type && !params[5].get_str().empty()) | |
915 | wtx.mapValue["to"] = params[5].get_str(); | |
916 | ||
917 | EnsureWalletIsUnlocked(); | |
918 | ||
919 | // Check funds | |
a372168e | 920 | CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); |
e3bc5698 | 921 | if (nAmount > nBalance) |
738835d7 | 922 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); |
e3bc5698 | 923 | |
292623ad | 924 | SendMoney(address.Get(), nAmount, false, wtx); |
e3bc5698 | 925 | |
805344dc | 926 | return wtx.GetHash().GetHex(); |
e3bc5698 JG |
927 | } |
928 | ||
929 | ||
930 | Value sendmany(const Array& params, bool fHelp) | |
931 | { | |
b9fb692d JS |
932 | if (!EnsureWalletIsAvailable(fHelp)) |
933 | return Value::null; | |
934 | ||
292623ad | 935 | if (fHelp || params.size() < 2 || params.size() > 5) |
e3bc5698 | 936 | throw runtime_error( |
40a75733 | 937 | "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n" |
a6099ef3 | 938 | "\nSend multiple times. Amounts are double-precision floating point numbers." |
939 | + HelpRequiringPassphrase() + "\n" | |
940 | "\nArguments:\n" | |
3c31eb24 | 941 | "1. \"fromaccount\" (string, required) MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" |
a6099ef3 | 942 | "2. \"amounts\" (string, required) A json object with addresses and amounts\n" |
943 | " {\n" | |
58c4c0bb | 944 | " \"address\":amount (numeric) The zcash address is the key, the numeric amount in btc is the value\n" |
a6099ef3 | 945 | " ,...\n" |
946 | " }\n" | |
947 | "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n" | |
948 | "4. \"comment\" (string, optional) A comment\n" | |
40a75733 | 949 | "5. subtractfeefromamount (string, optional) A json array with addresses.\n" |
292623ad | 950 | " The fee will be equally deducted from the amount of each selected address.\n" |
58c4c0bb | 951 | " Those recipients will receive less zcashs than you enter in their corresponding amount field.\n" |
40a75733 LD |
952 | " If no addresses are specified here, the sender pays the fee.\n" |
953 | " [\n" | |
954 | " \"address\" (string) Subtract fee from this address\n" | |
292623ad | 955 | " ,...\n" |
40a75733 | 956 | " ]\n" |
a6099ef3 | 957 | "\nResult:\n" |
958 | "\"transactionid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" | |
b5ef85c7 | 959 | " the number of addresses.\n" |
a6099ef3 | 960 | "\nExamples:\n" |
961 | "\nSend two amounts to two different addresses:\n" | |
7b782f5b | 962 | + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + |
a6099ef3 | 963 | "\nSend two amounts to two different addresses setting the confirmation and comment:\n" |
7b782f5b | 964 | + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + |
292623ad | 965 | "\nSend two amounts to two different addresses, subtract fee from amount:\n" |
40a75733 | 966 | + HelpExampleCli("sendmany", "\"\" \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\",\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + |
a6099ef3 | 967 | "\nAs a json rpc call\n" |
7b782f5b | 968 | + HelpExampleRpc("sendmany", "\"\", \"{\\\"1D1ZrZNe3JUo7ZycKEYQQiQAWd9y54F4XZ\\\":0.01,\\\"1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") |
a6099ef3 | 969 | ); |
e3bc5698 | 970 | |
4401b2d7 EL |
971 | LOCK2(cs_main, pwalletMain->cs_wallet); |
972 | ||
e3bc5698 JG |
973 | string strAccount = AccountFromValue(params[0]); |
974 | Object sendTo = params[1].get_obj(); | |
975 | int nMinDepth = 1; | |
976 | if (params.size() > 2) | |
977 | nMinDepth = params[2].get_int(); | |
978 | ||
979 | CWalletTx wtx; | |
980 | wtx.strFromAccount = strAccount; | |
981 | if (params.size() > 3 && params[3].type() != null_type && !params[3].get_str().empty()) | |
982 | wtx.mapValue["comment"] = params[3].get_str(); | |
983 | ||
40a75733 | 984 | Array subtractFeeFromAmount; |
292623ad | 985 | if (params.size() > 4) |
40a75733 | 986 | subtractFeeFromAmount = params[4].get_array(); |
292623ad | 987 | |
e3bc5698 | 988 | set<CBitcoinAddress> setAddress; |
292623ad | 989 | vector<CRecipient> vecSend; |
e3bc5698 | 990 | |
a372168e | 991 | CAmount totalAmount = 0; |
e3bc5698 JG |
992 | BOOST_FOREACH(const Pair& s, sendTo) |
993 | { | |
994 | CBitcoinAddress address(s.name_); | |
995 | if (!address.IsValid()) | |
58c4c0bb | 996 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Zcash address: ")+s.name_); |
e3bc5698 JG |
997 | |
998 | if (setAddress.count(address)) | |
738835d7 | 999 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_); |
e3bc5698 JG |
1000 | setAddress.insert(address); |
1001 | ||
0be990ba | 1002 | CScript scriptPubKey = GetScriptForDestination(address.Get()); |
a372168e | 1003 | CAmount nAmount = AmountFromValue(s.value_); |
e3bc5698 JG |
1004 | totalAmount += nAmount; |
1005 | ||
292623ad | 1006 | bool fSubtractFeeFromAmount = false; |
40a75733 LD |
1007 | BOOST_FOREACH(const Value& addr, subtractFeeFromAmount) |
1008 | if (addr.get_str() == s.name_) | |
292623ad CL |
1009 | fSubtractFeeFromAmount = true; |
1010 | ||
1011 | CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount}; | |
1012 | vecSend.push_back(recipient); | |
e3bc5698 JG |
1013 | } |
1014 | ||
1015 | EnsureWalletIsUnlocked(); | |
1016 | ||
1017 | // Check funds | |
a372168e | 1018 | CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); |
e3bc5698 | 1019 | if (totalAmount > nBalance) |
738835d7 | 1020 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); |
e3bc5698 JG |
1021 | |
1022 | // Send | |
1023 | CReserveKey keyChange(pwalletMain); | |
a372168e | 1024 | CAmount nFeeRequired = 0; |
292623ad | 1025 | int nChangePosRet = -1; |
1f00f4e9 | 1026 | string strFailReason; |
292623ad | 1027 | bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason); |
e3bc5698 | 1028 | if (!fCreated) |
1f00f4e9 | 1029 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); |
e3bc5698 | 1030 | if (!pwalletMain->CommitTransaction(wtx, keyChange)) |
738835d7 | 1031 | throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed"); |
e3bc5698 | 1032 | |
805344dc | 1033 | return wtx.GetHash().GetHex(); |
e3bc5698 JG |
1034 | } |
1035 | ||
723a03d2 | 1036 | // Defined in rpcmisc.cpp |
787ee0c9 | 1037 | extern CScript _createmultisig_redeemScript(const Array& params); |
34226be7 GA |
1038 | |
1039 | Value addmultisigaddress(const Array& params, bool fHelp) | |
1040 | { | |
b9fb692d JS |
1041 | if (!EnsureWalletIsAvailable(fHelp)) |
1042 | return Value::null; | |
1043 | ||
34226be7 GA |
1044 | if (fHelp || params.size() < 2 || params.size() > 3) |
1045 | { | |
a6099ef3 | 1046 | string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n" |
1047 | "\nAdd a nrequired-to-sign multisignature address to the wallet.\n" | |
58c4c0bb | 1048 | "Each key is a Zcash address or hex-encoded public key.\n" |
7b782f5b | 1049 | "If 'account' is specified (DEPRECATED), assign address to that account.\n" |
a6099ef3 | 1050 | |
1051 | "\nArguments:\n" | |
1052 | "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" | |
58c4c0bb | 1053 | "2. \"keysobject\" (string, required) A json array of zcash addresses or hex-encoded public keys\n" |
a6099ef3 | 1054 | " [\n" |
58c4c0bb | 1055 | " \"address\" (string) zcash address or hex-encoded public key\n" |
a6099ef3 | 1056 | " ...,\n" |
1057 | " ]\n" | |
3c31eb24 | 1058 | "3. \"account\" (string, optional) DEPRECATED. If provided, MUST be set to the empty string \"\" to represent the default account. Passing any other string will result in an error.\n" |
a6099ef3 | 1059 | |
1060 | "\nResult:\n" | |
58c4c0bb | 1061 | "\"zcashaddress\" (string) A zcash address associated with the keys.\n" |
a6099ef3 | 1062 | |
1063 | "\nExamples:\n" | |
1064 | "\nAdd a multisig address from 2 addresses\n" | |
1065 | + HelpExampleCli("addmultisigaddress", "2 \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + | |
1066 | "\nAs json rpc call\n" | |
1067 | + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") | |
1068 | ; | |
34226be7 GA |
1069 | throw runtime_error(msg); |
1070 | } | |
1071 | ||
4401b2d7 EL |
1072 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1073 | ||
34226be7 GA |
1074 | string strAccount; |
1075 | if (params.size() > 2) | |
1076 | strAccount = AccountFromValue(params[2]); | |
e3bc5698 JG |
1077 | |
1078 | // Construct using pay-to-script-hash: | |
787ee0c9 | 1079 | CScript inner = _createmultisig_redeemScript(params); |
066e2a14 | 1080 | CScriptID innerID(inner); |
e3bc5698 JG |
1081 | pwalletMain->AddCScript(inner); |
1082 | ||
a41d5fe0 | 1083 | pwalletMain->SetAddressBook(innerID, strAccount, "send"); |
e3bc5698 JG |
1084 | return CBitcoinAddress(innerID).ToString(); |
1085 | } | |
1086 | ||
1087 | ||
1088 | struct tallyitem | |
1089 | { | |
a372168e | 1090 | CAmount nAmount; |
e3bc5698 | 1091 | int nConf; |
62c9b115 | 1092 | vector<uint256> txids; |
0fa2f889 | 1093 | bool fIsWatchonly; |
e3bc5698 JG |
1094 | tallyitem() |
1095 | { | |
1096 | nAmount = 0; | |
1097 | nConf = std::numeric_limits<int>::max(); | |
0fa2f889 | 1098 | fIsWatchonly = false; |
e3bc5698 JG |
1099 | } |
1100 | }; | |
1101 | ||
1102 | Value ListReceived(const Array& params, bool fByAccounts) | |
1103 | { | |
1104 | // Minimum confirmations | |
1105 | int nMinDepth = 1; | |
1106 | if (params.size() > 0) | |
1107 | nMinDepth = params[0].get_int(); | |
1108 | ||
1109 | // Whether to include empty accounts | |
1110 | bool fIncludeEmpty = false; | |
1111 | if (params.size() > 1) | |
1112 | fIncludeEmpty = params[1].get_bool(); | |
1113 | ||
a3e192a3 | 1114 | isminefilter filter = ISMINE_SPENDABLE; |
0fa2f889 J |
1115 | if(params.size() > 2) |
1116 | if(params[2].get_bool()) | |
a3e192a3 | 1117 | filter = filter | ISMINE_WATCH_ONLY; |
0fa2f889 | 1118 | |
e3bc5698 JG |
1119 | // Tally |
1120 | map<CBitcoinAddress, tallyitem> mapTally; | |
1121 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) | |
1122 | { | |
1123 | const CWalletTx& wtx = (*it).second; | |
1124 | ||
75a4d512 | 1125 | if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) |
e3bc5698 JG |
1126 | continue; |
1127 | ||
1128 | int nDepth = wtx.GetDepthInMainChain(); | |
1129 | if (nDepth < nMinDepth) | |
1130 | continue; | |
1131 | ||
1132 | BOOST_FOREACH(const CTxOut& txout, wtx.vout) | |
1133 | { | |
1134 | CTxDestination address; | |
0fa2f889 J |
1135 | if (!ExtractDestination(txout.scriptPubKey, address)) |
1136 | continue; | |
1137 | ||
1138 | isminefilter mine = IsMine(*pwalletMain, address); | |
f28707a8 | 1139 | if(!(mine & filter)) |
e3bc5698 JG |
1140 | continue; |
1141 | ||
1142 | tallyitem& item = mapTally[address]; | |
1143 | item.nAmount += txout.nValue; | |
1144 | item.nConf = min(item.nConf, nDepth); | |
805344dc | 1145 | item.txids.push_back(wtx.GetHash()); |
a3e192a3 | 1146 | if (mine & ISMINE_WATCH_ONLY) |
0fa2f889 | 1147 | item.fIsWatchonly = true; |
e3bc5698 JG |
1148 | } |
1149 | } | |
1150 | ||
1151 | // Reply | |
1152 | Array ret; | |
1153 | map<string, tallyitem> mapAccountTally; | |
61885513 | 1154 | BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) |
e3bc5698 JG |
1155 | { |
1156 | const CBitcoinAddress& address = item.first; | |
61885513 | 1157 | const string& strAccount = item.second.name; |
e3bc5698 JG |
1158 | map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address); |
1159 | if (it == mapTally.end() && !fIncludeEmpty) | |
1160 | continue; | |
1161 | ||
a372168e | 1162 | CAmount nAmount = 0; |
e3bc5698 | 1163 | int nConf = std::numeric_limits<int>::max(); |
0fa2f889 | 1164 | bool fIsWatchonly = false; |
e3bc5698 JG |
1165 | if (it != mapTally.end()) |
1166 | { | |
1167 | nAmount = (*it).second.nAmount; | |
1168 | nConf = (*it).second.nConf; | |
0fa2f889 | 1169 | fIsWatchonly = (*it).second.fIsWatchonly; |
e3bc5698 JG |
1170 | } |
1171 | ||
1172 | if (fByAccounts) | |
1173 | { | |
1174 | tallyitem& item = mapAccountTally[strAccount]; | |
1175 | item.nAmount += nAmount; | |
1176 | item.nConf = min(item.nConf, nConf); | |
0fa2f889 | 1177 | item.fIsWatchonly = fIsWatchonly; |
e3bc5698 JG |
1178 | } |
1179 | else | |
1180 | { | |
1181 | Object obj; | |
0fa2f889 J |
1182 | if(fIsWatchonly) |
1183 | obj.push_back(Pair("involvesWatchonly", true)); | |
e3bc5698 JG |
1184 | obj.push_back(Pair("address", address.ToString())); |
1185 | obj.push_back(Pair("account", strAccount)); | |
1186 | obj.push_back(Pair("amount", ValueFromAmount(nAmount))); | |
1187 | obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); | |
62c9b115 | 1188 | Array transactions; |
1a204694 | 1189 | if (it != mapTally.end()) |
62c9b115 | 1190 | { |
1a204694 A |
1191 | BOOST_FOREACH(const uint256& item, (*it).second.txids) |
1192 | { | |
1193 | transactions.push_back(item.GetHex()); | |
1194 | } | |
62c9b115 A |
1195 | } |
1196 | obj.push_back(Pair("txids", transactions)); | |
e3bc5698 JG |
1197 | ret.push_back(obj); |
1198 | } | |
1199 | } | |
1200 | ||
1201 | if (fByAccounts) | |
1202 | { | |
1203 | for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) | |
1204 | { | |
a372168e | 1205 | CAmount nAmount = (*it).second.nAmount; |
e3bc5698 JG |
1206 | int nConf = (*it).second.nConf; |
1207 | Object obj; | |
0fa2f889 J |
1208 | if((*it).second.fIsWatchonly) |
1209 | obj.push_back(Pair("involvesWatchonly", true)); | |
e3bc5698 JG |
1210 | obj.push_back(Pair("account", (*it).first)); |
1211 | obj.push_back(Pair("amount", ValueFromAmount(nAmount))); | |
1212 | obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); | |
1213 | ret.push_back(obj); | |
1214 | } | |
1215 | } | |
1216 | ||
1217 | return ret; | |
1218 | } | |
1219 | ||
1220 | Value listreceivedbyaddress(const Array& params, bool fHelp) | |
1221 | { | |
b9fb692d JS |
1222 | if (!EnsureWalletIsAvailable(fHelp)) |
1223 | return Value::null; | |
1224 | ||
0fa2f889 | 1225 | if (fHelp || params.size() > 3) |
e3bc5698 | 1226 | throw runtime_error( |
0fa2f889 | 1227 | "listreceivedbyaddress ( minconf includeempty includeWatchonly)\n" |
a6099ef3 | 1228 | "\nList balances by receiving address.\n" |
1229 | "\nArguments:\n" | |
1230 | "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" | |
5617267c | 1231 | "2. includeempty (numeric, optional, default=false) Whether to include addresses that haven't received any payments.\n" |
0fa2f889 | 1232 | "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" |
a6099ef3 | 1233 | |
1234 | "\nResult:\n" | |
1235 | "[\n" | |
1236 | " {\n" | |
8f6860a0 | 1237 | " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" |
a6099ef3 | 1238 | " \"address\" : \"receivingaddress\", (string) The receiving address\n" |
7b782f5b | 1239 | " \"account\" : \"accountname\", (string) DEPRECATED. The account of the receiving address. The default account is \"\".\n" |
a6099ef3 | 1240 | " \"amount\" : x.xxx, (numeric) The total amount in btc received by the address\n" |
1241 | " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n" | |
1242 | " }\n" | |
1243 | " ,...\n" | |
1244 | "]\n" | |
1245 | ||
1246 | "\nExamples:\n" | |
1247 | + HelpExampleCli("listreceivedbyaddress", "") | |
1248 | + HelpExampleCli("listreceivedbyaddress", "6 true") | |
0fa2f889 | 1249 | + HelpExampleRpc("listreceivedbyaddress", "6, true, true") |
a6099ef3 | 1250 | ); |
e3bc5698 | 1251 | |
4401b2d7 EL |
1252 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1253 | ||
e3bc5698 JG |
1254 | return ListReceived(params, false); |
1255 | } | |
1256 | ||
1257 | Value listreceivedbyaccount(const Array& params, bool fHelp) | |
1258 | { | |
b9fb692d JS |
1259 | if (!EnsureWalletIsAvailable(fHelp)) |
1260 | return Value::null; | |
1261 | ||
0fa2f889 | 1262 | if (fHelp || params.size() > 3) |
e3bc5698 | 1263 | throw runtime_error( |
0fa2f889 | 1264 | "listreceivedbyaccount ( minconf includeempty includeWatchonly)\n" |
7b782f5b | 1265 | "\nDEPRECATED. List balances by account.\n" |
a6099ef3 | 1266 | "\nArguments:\n" |
1267 | "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" | |
1268 | "2. includeempty (boolean, optional, default=false) Whether to include accounts that haven't received any payments.\n" | |
0fa2f889 | 1269 | "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" |
a6099ef3 | 1270 | |
1271 | "\nResult:\n" | |
1272 | "[\n" | |
1273 | " {\n" | |
8f6860a0 | 1274 | " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" |
a6099ef3 | 1275 | " \"account\" : \"accountname\", (string) The account name of the receiving account\n" |
1276 | " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this account\n" | |
1277 | " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n" | |
1278 | " }\n" | |
1279 | " ,...\n" | |
1280 | "]\n" | |
1281 | ||
1282 | "\nExamples:\n" | |
1283 | + HelpExampleCli("listreceivedbyaccount", "") | |
1284 | + HelpExampleCli("listreceivedbyaccount", "6 true") | |
0fa2f889 | 1285 | + HelpExampleRpc("listreceivedbyaccount", "6, true, true") |
a6099ef3 | 1286 | ); |
e3bc5698 | 1287 | |
4401b2d7 EL |
1288 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1289 | ||
e3bc5698 JG |
1290 | return ListReceived(params, true); |
1291 | } | |
1292 | ||
cc6cfab3 LD |
1293 | static void MaybePushAddress(Object & entry, const CTxDestination &dest) |
1294 | { | |
1295 | CBitcoinAddress addr; | |
1296 | if (addr.Set(dest)) | |
1297 | entry.push_back(Pair("address", addr.ToString())); | |
1298 | } | |
1299 | ||
80dda36a | 1300 | void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, Array& ret, const isminefilter& filter) |
e3bc5698 | 1301 | { |
a372168e | 1302 | CAmount nFee; |
e3bc5698 | 1303 | string strSentAccount; |
1b4568cb CL |
1304 | list<COutputEntry> listReceived; |
1305 | list<COutputEntry> listSent; | |
e3bc5698 | 1306 | |
d7d5d23b | 1307 | wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); |
e3bc5698 JG |
1308 | |
1309 | bool fAllAccounts = (strAccount == string("*")); | |
a3e192a3 | 1310 | bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY); |
e3bc5698 | 1311 | |
e3bc5698 JG |
1312 | // Sent |
1313 | if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) | |
1314 | { | |
1b4568cb | 1315 | BOOST_FOREACH(const COutputEntry& s, listSent) |
e3bc5698 JG |
1316 | { |
1317 | Object entry; | |
1b4568cb | 1318 | if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY)) |
952877e0 | 1319 | entry.push_back(Pair("involvesWatchonly", true)); |
e3bc5698 | 1320 | entry.push_back(Pair("account", strSentAccount)); |
1b4568cb | 1321 | MaybePushAddress(entry, s.destination); |
b96f6a77 | 1322 | entry.push_back(Pair("category", "send")); |
1b4568cb CL |
1323 | entry.push_back(Pair("amount", ValueFromAmount(-s.amount))); |
1324 | entry.push_back(Pair("vout", s.vout)); | |
e3bc5698 JG |
1325 | entry.push_back(Pair("fee", ValueFromAmount(-nFee))); |
1326 | if (fLong) | |
1327 | WalletTxToJSON(wtx, entry); | |
1328 | ret.push_back(entry); | |
1329 | } | |
1330 | } | |
1331 | ||
1332 | // Received | |
1333 | if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) | |
1334 | { | |
1b4568cb | 1335 | BOOST_FOREACH(const COutputEntry& r, listReceived) |
e3bc5698 JG |
1336 | { |
1337 | string account; | |
1b4568cb CL |
1338 | if (pwalletMain->mapAddressBook.count(r.destination)) |
1339 | account = pwalletMain->mapAddressBook[r.destination].name; | |
e3bc5698 JG |
1340 | if (fAllAccounts || (account == strAccount)) |
1341 | { | |
1342 | Object entry; | |
1b4568cb | 1343 | if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY)) |
952877e0 | 1344 | entry.push_back(Pair("involvesWatchonly", true)); |
e3bc5698 | 1345 | entry.push_back(Pair("account", account)); |
1b4568cb | 1346 | MaybePushAddress(entry, r.destination); |
e07c8e91 LD |
1347 | if (wtx.IsCoinBase()) |
1348 | { | |
1349 | if (wtx.GetDepthInMainChain() < 1) | |
1350 | entry.push_back(Pair("category", "orphan")); | |
1351 | else if (wtx.GetBlocksToMaturity() > 0) | |
1352 | entry.push_back(Pair("category", "immature")); | |
1353 | else | |
1354 | entry.push_back(Pair("category", "generate")); | |
1355 | } | |
1356 | else | |
2b72d46f | 1357 | { |
b96f6a77 | 1358 | entry.push_back(Pair("category", "receive")); |
2b72d46f | 1359 | } |
1b4568cb CL |
1360 | entry.push_back(Pair("amount", ValueFromAmount(r.amount))); |
1361 | entry.push_back(Pair("vout", r.vout)); | |
e3bc5698 JG |
1362 | if (fLong) |
1363 | WalletTxToJSON(wtx, entry); | |
1364 | ret.push_back(entry); | |
1365 | } | |
1366 | } | |
1367 | } | |
1368 | } | |
1369 | ||
1370 | void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, Array& ret) | |
1371 | { | |
1372 | bool fAllAccounts = (strAccount == string("*")); | |
1373 | ||
1374 | if (fAllAccounts || acentry.strAccount == strAccount) | |
1375 | { | |
1376 | Object entry; | |
1377 | entry.push_back(Pair("account", acentry.strAccount)); | |
1378 | entry.push_back(Pair("category", "move")); | |
d56e30ca | 1379 | entry.push_back(Pair("time", acentry.nTime)); |
e3bc5698 JG |
1380 | entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); |
1381 | entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); | |
1382 | entry.push_back(Pair("comment", acentry.strComment)); | |
1383 | ret.push_back(entry); | |
1384 | } | |
1385 | } | |
1386 | ||
1387 | Value listtransactions(const Array& params, bool fHelp) | |
1388 | { | |
b9fb692d JS |
1389 | if (!EnsureWalletIsAvailable(fHelp)) |
1390 | return Value::null; | |
1391 | ||
d7d5d23b | 1392 | if (fHelp || params.size() > 4) |
e3bc5698 | 1393 | throw runtime_error( |
d7d5d23b | 1394 | "listtransactions ( \"account\" count from includeWatchonly)\n" |
a6099ef3 | 1395 | "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" |
1396 | "\nArguments:\n" | |
7b782f5b | 1397 | "1. \"account\" (string, optional) DEPRECATED. The account name. Should be \"*\".\n" |
a6099ef3 | 1398 | "2. count (numeric, optional, default=10) The number of transactions to return\n" |
1399 | "3. from (numeric, optional, default=0) The number of transactions to skip\n" | |
d7d5d23b | 1400 | "4. includeWatchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n" |
a6099ef3 | 1401 | "\nResult:\n" |
1402 | "[\n" | |
1403 | " {\n" | |
7b782f5b | 1404 | " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. \n" |
a6099ef3 | 1405 | " It will be \"\" for the default account.\n" |
58c4c0bb | 1406 | " \"address\":\"zcashaddress\", (string) The zcash address of the transaction. Not present for \n" |
a6099ef3 | 1407 | " move transactions (category = move).\n" |
1408 | " \"category\":\"send|receive|move\", (string) The transaction category. 'move' is a local (off blockchain)\n" | |
1409 | " transaction between accounts, and not associated with an address,\n" | |
1410 | " transaction id or block. 'send' and 'receive' transactions are \n" | |
1411 | " associated with an address, transaction id and block details\n" | |
1412 | " \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the\n" | |
1413 | " 'move' category for moves outbound. It is positive for the 'receive' category,\n" | |
1414 | " and for the 'move' category for inbound funds.\n" | |
1b4568cb | 1415 | " \"vout\" : n, (numeric) the vout value\n" |
a6099ef3 | 1416 | " \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the \n" |
1417 | " 'send' category of transactions.\n" | |
1418 | " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" | |
1419 | " 'receive' category of transactions.\n" | |
1420 | " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n" | |
1421 | " category of transactions.\n" | |
1422 | " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive'\n" | |
1423 | " category of transactions.\n" | |
b5ef85c7 | 1424 | " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" |
a6099ef3 | 1425 | " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" |
1426 | " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" | |
1427 | " for 'send' and 'receive' category of transactions.\n" | |
1428 | " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" | |
1429 | " \"otheraccount\": \"accountname\", (string) For the 'move' category of transactions, the account the funds came \n" | |
1430 | " from (for receiving funds, positive amounts), or went to (for sending funds,\n" | |
1431 | " negative amounts).\n" | |
1432 | " }\n" | |
1433 | "]\n" | |
1434 | ||
1435 | "\nExamples:\n" | |
1436 | "\nList the most recent 10 transactions in the systems\n" | |
1437 | + HelpExampleCli("listtransactions", "") + | |
7b782f5b LD |
1438 | "\nList transactions 100 to 120\n" |
1439 | + HelpExampleCli("listtransactions", "\"*\" 20 100") + | |
a6099ef3 | 1440 | "\nAs a json rpc call\n" |
7b782f5b | 1441 | + HelpExampleRpc("listtransactions", "\"*\", 20, 100") |
a6099ef3 | 1442 | ); |
e3bc5698 | 1443 | |
4401b2d7 EL |
1444 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1445 | ||
e3bc5698 JG |
1446 | string strAccount = "*"; |
1447 | if (params.size() > 0) | |
1448 | strAccount = params[0].get_str(); | |
1449 | int nCount = 10; | |
1450 | if (params.size() > 1) | |
1451 | nCount = params[1].get_int(); | |
1452 | int nFrom = 0; | |
1453 | if (params.size() > 2) | |
1454 | nFrom = params[2].get_int(); | |
a3e192a3 | 1455 | isminefilter filter = ISMINE_SPENDABLE; |
a5c6c5d6 J |
1456 | if(params.size() > 3) |
1457 | if(params[3].get_bool()) | |
a3e192a3 | 1458 | filter = filter | ISMINE_WATCH_ONLY; |
e3bc5698 JG |
1459 | |
1460 | if (nCount < 0) | |
738835d7 | 1461 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count"); |
e3bc5698 | 1462 | if (nFrom < 0) |
738835d7 | 1463 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from"); |
e3bc5698 JG |
1464 | |
1465 | Array ret; | |
e3bc5698 | 1466 | |
ddb709e9 LD |
1467 | std::list<CAccountingEntry> acentries; |
1468 | CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount); | |
e3bc5698 JG |
1469 | |
1470 | // iterate backwards until we have nCount items to return: | |
c3f95ef1 | 1471 | for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) |
e3bc5698 JG |
1472 | { |
1473 | CWalletTx *const pwtx = (*it).second.first; | |
1474 | if (pwtx != 0) | |
d7d5d23b | 1475 | ListTransactions(*pwtx, strAccount, 0, true, ret, filter); |
e3bc5698 JG |
1476 | CAccountingEntry *const pacentry = (*it).second.second; |
1477 | if (pacentry != 0) | |
1478 | AcentryToJSON(*pacentry, strAccount, ret); | |
1479 | ||
1480 | if ((int)ret.size() >= (nCount+nFrom)) break; | |
1481 | } | |
1482 | // ret is newest to oldest | |
1483 | ||
1484 | if (nFrom > (int)ret.size()) | |
1485 | nFrom = ret.size(); | |
1486 | if ((nFrom + nCount) > (int)ret.size()) | |
1487 | nCount = ret.size() - nFrom; | |
1488 | Array::iterator first = ret.begin(); | |
1489 | std::advance(first, nFrom); | |
1490 | Array::iterator last = ret.begin(); | |
1491 | std::advance(last, nFrom+nCount); | |
1492 | ||
1493 | if (last != ret.end()) ret.erase(last, ret.end()); | |
1494 | if (first != ret.begin()) ret.erase(ret.begin(), first); | |
1495 | ||
1496 | std::reverse(ret.begin(), ret.end()); // Return oldest to newest | |
1497 | ||
1498 | return ret; | |
1499 | } | |
1500 | ||
1501 | Value listaccounts(const Array& params, bool fHelp) | |
1502 | { | |
b9fb692d JS |
1503 | if (!EnsureWalletIsAvailable(fHelp)) |
1504 | return Value::null; | |
1505 | ||
83f3543f | 1506 | if (fHelp || params.size() > 2) |
e3bc5698 | 1507 | throw runtime_error( |
83f3543f | 1508 | "listaccounts ( minconf includeWatchonly)\n" |
7b782f5b | 1509 | "\nDEPRECATED. Returns Object that has account names as keys, account balances as values.\n" |
a6099ef3 | 1510 | "\nArguments:\n" |
5617267c | 1511 | "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n" |
83f3543f | 1512 | "2. includeWatchonly (bool, optional, default=false) Include balances in watchonly addresses (see 'importaddress')\n" |
a6099ef3 | 1513 | "\nResult:\n" |
1514 | "{ (json object where keys are account names, and values are numeric balances\n" | |
1515 | " \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n" | |
1516 | " ...\n" | |
1517 | "}\n" | |
1518 | "\nExamples:\n" | |
1519 | "\nList account balances where there at least 1 confirmation\n" | |
1520 | + HelpExampleCli("listaccounts", "") + | |
1521 | "\nList account balances including zero confirmation transactions\n" | |
1522 | + HelpExampleCli("listaccounts", "0") + | |
1523 | "\nList account balances for 6 or more confirmations\n" | |
1524 | + HelpExampleCli("listaccounts", "6") + | |
1525 | "\nAs json rpc call\n" | |
1526 | + HelpExampleRpc("listaccounts", "6") | |
1527 | ); | |
e3bc5698 | 1528 | |
4401b2d7 EL |
1529 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1530 | ||
e3bc5698 JG |
1531 | int nMinDepth = 1; |
1532 | if (params.size() > 0) | |
1533 | nMinDepth = params[0].get_int(); | |
a3e192a3 | 1534 | isminefilter includeWatchonly = ISMINE_SPENDABLE; |
a5c6c5d6 J |
1535 | if(params.size() > 1) |
1536 | if(params[1].get_bool()) | |
a3e192a3 | 1537 | includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY; |
e3bc5698 | 1538 | |
a372168e | 1539 | map<string, CAmount> mapAccountBalances; |
61885513 | 1540 | BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) { |
83f3543f | 1541 | if (IsMine(*pwalletMain, entry.first) & includeWatchonly) // This address belongs to me |
61885513 | 1542 | mapAccountBalances[entry.second.name] = 0; |
e3bc5698 JG |
1543 | } |
1544 | ||
1545 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) | |
1546 | { | |
1547 | const CWalletTx& wtx = (*it).second; | |
a372168e | 1548 | CAmount nFee; |
e3bc5698 | 1549 | string strSentAccount; |
1b4568cb CL |
1550 | list<COutputEntry> listReceived; |
1551 | list<COutputEntry> listSent; | |
93a18a36 GA |
1552 | int nDepth = wtx.GetDepthInMainChain(); |
1553 | if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) | |
731b89b8 | 1554 | continue; |
83f3543f | 1555 | wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); |
e3bc5698 | 1556 | mapAccountBalances[strSentAccount] -= nFee; |
1b4568cb CL |
1557 | BOOST_FOREACH(const COutputEntry& s, listSent) |
1558 | mapAccountBalances[strSentAccount] -= s.amount; | |
93a18a36 | 1559 | if (nDepth >= nMinDepth) |
e3bc5698 | 1560 | { |
1b4568cb CL |
1561 | BOOST_FOREACH(const COutputEntry& r, listReceived) |
1562 | if (pwalletMain->mapAddressBook.count(r.destination)) | |
1563 | mapAccountBalances[pwalletMain->mapAddressBook[r.destination].name] += r.amount; | |
e3bc5698 | 1564 | else |
1b4568cb | 1565 | mapAccountBalances[""] += r.amount; |
e3bc5698 JG |
1566 | } |
1567 | } | |
1568 | ||
1569 | list<CAccountingEntry> acentries; | |
1570 | CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); | |
1571 | BOOST_FOREACH(const CAccountingEntry& entry, acentries) | |
1572 | mapAccountBalances[entry.strAccount] += entry.nCreditDebit; | |
1573 | ||
1574 | Object ret; | |
a372168e | 1575 | BOOST_FOREACH(const PAIRTYPE(string, CAmount)& accountBalance, mapAccountBalances) { |
e3bc5698 JG |
1576 | ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); |
1577 | } | |
1578 | return ret; | |
1579 | } | |
1580 | ||
1581 | Value listsinceblock(const Array& params, bool fHelp) | |
1582 | { | |
b9fb692d JS |
1583 | if (!EnsureWalletIsAvailable(fHelp)) |
1584 | return Value::null; | |
1585 | ||
e3bc5698 JG |
1586 | if (fHelp) |
1587 | throw runtime_error( | |
d7d5d23b | 1588 | "listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n" |
a6099ef3 | 1589 | "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n" |
1590 | "\nArguments:\n" | |
1591 | "1. \"blockhash\" (string, optional) The block hash to list transactions since\n" | |
1592 | "2. target-confirmations: (numeric, optional) The confirmations required, must be 1 or more\n" | |
d7d5d23b | 1593 | "3. includeWatchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')" |
a6099ef3 | 1594 | "\nResult:\n" |
1595 | "{\n" | |
1596 | " \"transactions\": [\n" | |
7b782f5b | 1597 | " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. Will be \"\" for the default account.\n" |
58c4c0bb | 1598 | " \"address\":\"zcashaddress\", (string) The zcash address of the transaction. Not present for move transactions (category = move).\n" |
a6099ef3 | 1599 | " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" |
1600 | " \"amount\": x.xxx, (numeric) The amount in btc. This is negative for the 'send' category, and for the 'move' category for moves \n" | |
1601 | " outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n" | |
1b4568cb | 1602 | " \"vout\" : n, (numeric) the vout value\n" |
a6099ef3 | 1603 | " \"fee\": x.xxx, (numeric) The amount of the fee in btc. This is negative and only available for the 'send' category of transactions.\n" |
1604 | " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n" | |
1605 | " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n" | |
1606 | " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive' category of transactions.\n" | |
1607 | " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" | |
b5ef85c7 | 1608 | " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" |
a6099ef3 | 1609 | " \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n" |
1610 | " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n" | |
1611 | " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" | |
1612 | " \"to\": \"...\", (string) If a comment to is associated with the transaction.\n" | |
1613 | " ],\n" | |
1614 | " \"lastblock\": \"lastblockhash\" (string) The hash of the last block\n" | |
1615 | "}\n" | |
1616 | "\nExamples:\n" | |
1617 | + HelpExampleCli("listsinceblock", "") | |
1618 | + HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6") | |
1619 | + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6") | |
1620 | ); | |
e3bc5698 | 1621 | |
4401b2d7 EL |
1622 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1623 | ||
e3bc5698 JG |
1624 | CBlockIndex *pindex = NULL; |
1625 | int target_confirms = 1; | |
a3e192a3 | 1626 | isminefilter filter = ISMINE_SPENDABLE; |
e3bc5698 JG |
1627 | |
1628 | if (params.size() > 0) | |
1629 | { | |
4f152496 | 1630 | uint256 blockId; |
e3bc5698 JG |
1631 | |
1632 | blockId.SetHex(params[0].get_str()); | |
145d5be8 | 1633 | BlockMap::iterator it = mapBlockIndex.find(blockId); |
e4daecda PW |
1634 | if (it != mapBlockIndex.end()) |
1635 | pindex = it->second; | |
e3bc5698 JG |
1636 | } |
1637 | ||
1638 | if (params.size() > 1) | |
1639 | { | |
1640 | target_confirms = params[1].get_int(); | |
1641 | ||
1642 | if (target_confirms < 1) | |
738835d7 | 1643 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); |
e3bc5698 JG |
1644 | } |
1645 | ||
a5c6c5d6 J |
1646 | if(params.size() > 2) |
1647 | if(params[2].get_bool()) | |
a3e192a3 | 1648 | filter = filter | ISMINE_WATCH_ONLY; |
a5c6c5d6 | 1649 | |
4c6d41b8 | 1650 | int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; |
e3bc5698 JG |
1651 | |
1652 | Array transactions; | |
1653 | ||
1654 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) | |
1655 | { | |
1656 | CWalletTx tx = (*it).second; | |
1657 | ||
1658 | if (depth == -1 || tx.GetDepthInMainChain() < depth) | |
d7d5d23b | 1659 | ListTransactions(tx, "*", 0, true, transactions, filter); |
e3bc5698 JG |
1660 | } |
1661 | ||
4c6d41b8 | 1662 | CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; |
4f152496 | 1663 | uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); |
e3bc5698 JG |
1664 | |
1665 | Object ret; | |
1666 | ret.push_back(Pair("transactions", transactions)); | |
1667 | ret.push_back(Pair("lastblock", lastblock.GetHex())); | |
1668 | ||
1669 | return ret; | |
1670 | } | |
1671 | ||
1672 | Value gettransaction(const Array& params, bool fHelp) | |
1673 | { | |
b9fb692d JS |
1674 | if (!EnsureWalletIsAvailable(fHelp)) |
1675 | return Value::null; | |
1676 | ||
f87ba3df | 1677 | if (fHelp || params.size() < 1 || params.size() > 2) |
e3bc5698 | 1678 | throw runtime_error( |
57e1716d | 1679 | "gettransaction \"txid\" ( includeWatchonly )\n" |
a6099ef3 | 1680 | "\nGet detailed information about in-wallet transaction <txid>\n" |
1681 | "\nArguments:\n" | |
1682 | "1. \"txid\" (string, required) The transaction id\n" | |
f87ba3df | 1683 | "2. \"includeWatchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n" |
a6099ef3 | 1684 | "\nResult:\n" |
1685 | "{\n" | |
1686 | " \"amount\" : x.xxx, (numeric) The transaction amount in btc\n" | |
1687 | " \"confirmations\" : n, (numeric) The number of confirmations\n" | |
1688 | " \"blockhash\" : \"hash\", (string) The block hash\n" | |
1689 | " \"blockindex\" : xx, (numeric) The block index\n" | |
1690 | " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n" | |
b5ef85c7 | 1691 | " \"txid\" : \"transactionid\", (string) The transaction id.\n" |
a6099ef3 | 1692 | " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" |
1693 | " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" | |
1694 | " \"details\" : [\n" | |
1695 | " {\n" | |
7b782f5b | 1696 | " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" |
58c4c0bb | 1697 | " \"address\" : \"zcashaddress\", (string) The zcash address involved in the transaction\n" |
a6099ef3 | 1698 | " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" |
1699 | " \"amount\" : x.xxx (numeric) The amount in btc\n" | |
1b4568cb | 1700 | " \"vout\" : n, (numeric) the vout value\n" |
a6099ef3 | 1701 | " }\n" |
1702 | " ,...\n" | |
3a1c20b7 | 1703 | " ],\n" |
f7cfb52d S |
1704 | " \"vjoinsplit\" : [\n" |
1705 | " {\n" | |
1706 | " \"anchor\" : \"treestateref\", (string) Merkle root of note commitment tree\n" | |
1707 | " \"nullifiers\" : [ string, ... ] (string) Nullifiers of input notes\n" | |
1708 | " \"commitments\" : [ string, ... ] (string) Note commitments for note outputs\n" | |
1709 | " \"macs\" : [ string, ... ] (string) Message authentication tags\n" | |
1710 | " \"vpub_old\" : x.xxx (numeric) The amount removed from the transparent value pool\n" | |
1711 | " \"vpub_new\" : x.xxx, (numeric) The amount added to the transparent value pool\n" | |
1712 | " }\n" | |
1713 | " ,...\n" | |
1714 | " ],\n" | |
3a1c20b7 | 1715 | " \"hex\" : \"data\" (string) Raw data for transaction\n" |
a6099ef3 | 1716 | "}\n" |
1717 | ||
ab45ddb5 | 1718 | "\nExamples:\n" |
a6099ef3 | 1719 | + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") |
57e1716d | 1720 | + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true") |
a6099ef3 | 1721 | + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") |
1722 | ); | |
e3bc5698 | 1723 | |
4401b2d7 EL |
1724 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1725 | ||
e3bc5698 JG |
1726 | uint256 hash; |
1727 | hash.SetHex(params[0].get_str()); | |
1728 | ||
a3e192a3 | 1729 | isminefilter filter = ISMINE_SPENDABLE; |
f87ba3df J |
1730 | if(params.size() > 1) |
1731 | if(params[1].get_bool()) | |
a3e192a3 | 1732 | filter = filter | ISMINE_WATCH_ONLY; |
f87ba3df | 1733 | |
e3bc5698 JG |
1734 | Object entry; |
1735 | if (!pwalletMain->mapWallet.count(hash)) | |
738835d7 | 1736 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); |
e3bc5698 JG |
1737 | const CWalletTx& wtx = pwalletMain->mapWallet[hash]; |
1738 | ||
ccca27a7 | 1739 | CAmount nCredit = wtx.GetCredit(filter); |
a372168e MF |
1740 | CAmount nDebit = wtx.GetDebit(filter); |
1741 | CAmount nNet = nCredit - nDebit; | |
1742 | CAmount nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0); | |
e3bc5698 JG |
1743 | |
1744 | entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); | |
80dda36a | 1745 | if (wtx.IsFromMe(filter)) |
e3bc5698 JG |
1746 | entry.push_back(Pair("fee", ValueFromAmount(nFee))); |
1747 | ||
1748 | WalletTxToJSON(wtx, entry); | |
1749 | ||
1750 | Array details; | |
f87ba3df | 1751 | ListTransactions(wtx, "*", 0, false, details, filter); |
e3bc5698 JG |
1752 | entry.push_back(Pair("details", details)); |
1753 | ||
ae775b5b | 1754 | string strHex = EncodeHexTx(static_cast<CTransaction>(wtx)); |
3a1c20b7 WL |
1755 | entry.push_back(Pair("hex", strHex)); |
1756 | ||
e3bc5698 JG |
1757 | return entry; |
1758 | } | |
1759 | ||
1760 | ||
1761 | Value backupwallet(const Array& params, bool fHelp) | |
1762 | { | |
b9fb692d JS |
1763 | if (!EnsureWalletIsAvailable(fHelp)) |
1764 | return Value::null; | |
1765 | ||
e3bc5698 JG |
1766 | if (fHelp || params.size() != 1) |
1767 | throw runtime_error( | |
a6099ef3 | 1768 | "backupwallet \"destination\"\n" |
1769 | "\nSafely copies wallet.dat to destination, which can be a directory or a path with filename.\n" | |
1770 | "\nArguments:\n" | |
1771 | "1. \"destination\" (string) The destination directory or file\n" | |
1772 | "\nExamples:\n" | |
1773 | + HelpExampleCli("backupwallet", "\"backup.dat\"") | |
1774 | + HelpExampleRpc("backupwallet", "\"backup.dat\"") | |
1775 | ); | |
e3bc5698 | 1776 | |
4401b2d7 EL |
1777 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1778 | ||
e3bc5698 | 1779 | string strDest = params[0].get_str(); |
ad525e9c PK |
1780 | if (!BackupWallet(*pwalletMain, strDest)) |
1781 | throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); | |
e3bc5698 JG |
1782 | |
1783 | return Value::null; | |
1784 | } | |
1785 | ||
1786 | ||
1787 | Value keypoolrefill(const Array& params, bool fHelp) | |
1788 | { | |
b9fb692d JS |
1789 | if (!EnsureWalletIsAvailable(fHelp)) |
1790 | return Value::null; | |
1791 | ||
36bd46f1 | 1792 | if (fHelp || params.size() > 1) |
e3bc5698 | 1793 | throw runtime_error( |
a6099ef3 | 1794 | "keypoolrefill ( newsize )\n" |
1795 | "\nFills the keypool." | |
1796 | + HelpRequiringPassphrase() + "\n" | |
1797 | "\nArguments\n" | |
1798 | "1. newsize (numeric, optional, default=100) The new keypool size\n" | |
1799 | "\nExamples:\n" | |
1800 | + HelpExampleCli("keypoolrefill", "") | |
1801 | + HelpExampleRpc("keypoolrefill", "") | |
1802 | ); | |
e3bc5698 | 1803 | |
4401b2d7 EL |
1804 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1805 | ||
f914c7a1 PK |
1806 | // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool |
1807 | unsigned int kpSize = 0; | |
36bd46f1 JG |
1808 | if (params.size() > 0) { |
1809 | if (params[0].get_int() < 0) | |
f914c7a1 PK |
1810 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size."); |
1811 | kpSize = (unsigned int)params[0].get_int(); | |
36bd46f1 JG |
1812 | } |
1813 | ||
e3bc5698 | 1814 | EnsureWalletIsUnlocked(); |
36bd46f1 | 1815 | pwalletMain->TopUpKeyPool(kpSize); |
e3bc5698 | 1816 | |
36bd46f1 | 1817 | if (pwalletMain->GetKeyPoolSize() < kpSize) |
738835d7 | 1818 | throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); |
e3bc5698 JG |
1819 | |
1820 | return Value::null; | |
1821 | } | |
1822 | ||
1823 | ||
92f2c1fe | 1824 | static void LockWallet(CWallet* pWallet) |
e3bc5698 | 1825 | { |
92f2c1fe GA |
1826 | LOCK(cs_nWalletUnlockTime); |
1827 | nWalletUnlockTime = 0; | |
1828 | pWallet->Lock(); | |
e3bc5698 JG |
1829 | } |
1830 | ||
1831 | Value walletpassphrase(const Array& params, bool fHelp) | |
1832 | { | |
b9fb692d JS |
1833 | if (!EnsureWalletIsAvailable(fHelp)) |
1834 | return Value::null; | |
1835 | ||
e3bc5698 JG |
1836 | if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) |
1837 | throw runtime_error( | |
a6099ef3 | 1838 | "walletpassphrase \"passphrase\" timeout\n" |
1839 | "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" | |
58c4c0bb | 1840 | "This is needed prior to performing transactions related to private keys such as sending zcash\n" |
a6099ef3 | 1841 | "\nArguments:\n" |
1842 | "1. \"passphrase\" (string, required) The wallet passphrase\n" | |
1843 | "2. timeout (numeric, required) The time to keep the decryption key in seconds.\n" | |
6c0db81c WL |
1844 | "\nNote:\n" |
1845 | "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n" | |
1846 | "time that overrides the old one.\n" | |
a6099ef3 | 1847 | "\nExamples:\n" |
1848 | "\nunlock the wallet for 60 seconds\n" | |
1849 | + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") + | |
1850 | "\nLock the wallet again (before 60 seconds)\n" | |
1851 | + HelpExampleCli("walletlock", "") + | |
1852 | "\nAs json rpc call\n" | |
1853 | + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60") | |
1854 | ); | |
1855 | ||
4401b2d7 EL |
1856 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1857 | ||
e3bc5698 JG |
1858 | if (fHelp) |
1859 | return true; | |
1860 | if (!pwalletMain->IsCrypted()) | |
738835d7 | 1861 | throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); |
e3bc5698 | 1862 | |
e3bc5698 JG |
1863 | // Note that the walletpassphrase is stored in params[0] which is not mlock()ed |
1864 | SecureString strWalletPass; | |
1865 | strWalletPass.reserve(100); | |
1866 | // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) | |
1867 | // Alternately, find a way to make params[0] mlock()'d to begin with. | |
1868 | strWalletPass = params[0].get_str().c_str(); | |
1869 | ||
1870 | if (strWalletPass.length() > 0) | |
1871 | { | |
1872 | if (!pwalletMain->Unlock(strWalletPass)) | |
738835d7 | 1873 | throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); |
e3bc5698 JG |
1874 | } |
1875 | else | |
1876 | throw runtime_error( | |
1877 | "walletpassphrase <passphrase> <timeout>\n" | |
1878 | "Stores the wallet decryption key in memory for <timeout> seconds."); | |
1879 | ||
6e263a5f | 1880 | // No need to check return values, because the wallet was unlocked above |
1a62587e | 1881 | pwalletMain->UpdateNullifierNoteMap(); |
92f2c1fe GA |
1882 | pwalletMain->TopUpKeyPool(); |
1883 | ||
51ed9ec9 | 1884 | int64_t nSleepTime = params[1].get_int64(); |
92f2c1fe GA |
1885 | LOCK(cs_nWalletUnlockTime); |
1886 | nWalletUnlockTime = GetTime() + nSleepTime; | |
1887 | RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime); | |
e3bc5698 JG |
1888 | |
1889 | return Value::null; | |
1890 | } | |
1891 | ||
1892 | ||
1893 | Value walletpassphrasechange(const Array& params, bool fHelp) | |
1894 | { | |
b9fb692d JS |
1895 | if (!EnsureWalletIsAvailable(fHelp)) |
1896 | return Value::null; | |
1897 | ||
e3bc5698 JG |
1898 | if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) |
1899 | throw runtime_error( | |
a6099ef3 | 1900 | "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n" |
1901 | "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n" | |
1902 | "\nArguments:\n" | |
1903 | "1. \"oldpassphrase\" (string) The current passphrase\n" | |
1904 | "2. \"newpassphrase\" (string) The new passphrase\n" | |
1905 | "\nExamples:\n" | |
1906 | + HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"") | |
1907 | + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"") | |
1908 | ); | |
1909 | ||
4401b2d7 EL |
1910 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1911 | ||
e3bc5698 JG |
1912 | if (fHelp) |
1913 | return true; | |
1914 | if (!pwalletMain->IsCrypted()) | |
738835d7 | 1915 | throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); |
e3bc5698 JG |
1916 | |
1917 | // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) | |
1918 | // Alternately, find a way to make params[0] mlock()'d to begin with. | |
1919 | SecureString strOldWalletPass; | |
1920 | strOldWalletPass.reserve(100); | |
1921 | strOldWalletPass = params[0].get_str().c_str(); | |
1922 | ||
1923 | SecureString strNewWalletPass; | |
1924 | strNewWalletPass.reserve(100); | |
1925 | strNewWalletPass = params[1].get_str().c_str(); | |
1926 | ||
1927 | if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) | |
1928 | throw runtime_error( | |
1929 | "walletpassphrasechange <oldpassphrase> <newpassphrase>\n" | |
1930 | "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>."); | |
1931 | ||
1932 | if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) | |
738835d7 | 1933 | throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); |
e3bc5698 JG |
1934 | |
1935 | return Value::null; | |
1936 | } | |
1937 | ||
1938 | ||
1939 | Value walletlock(const Array& params, bool fHelp) | |
1940 | { | |
b9fb692d JS |
1941 | if (!EnsureWalletIsAvailable(fHelp)) |
1942 | return Value::null; | |
1943 | ||
e3bc5698 JG |
1944 | if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0)) |
1945 | throw runtime_error( | |
1946 | "walletlock\n" | |
a6099ef3 | 1947 | "\nRemoves the wallet encryption key from memory, locking the wallet.\n" |
e3bc5698 | 1948 | "After calling this method, you will need to call walletpassphrase again\n" |
a6099ef3 | 1949 | "before being able to call any methods which require the wallet to be unlocked.\n" |
1950 | "\nExamples:\n" | |
1951 | "\nSet the passphrase for 2 minutes to perform a transaction\n" | |
1952 | + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") + | |
1953 | "\nPerform a send (requires passphrase set)\n" | |
1954 | + HelpExampleCli("sendtoaddress", "\"1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") + | |
1955 | "\nClear the passphrase since we are done before 2 minutes is up\n" | |
1956 | + HelpExampleCli("walletlock", "") + | |
1957 | "\nAs json rpc call\n" | |
1958 | + HelpExampleRpc("walletlock", "") | |
1959 | ); | |
1960 | ||
4401b2d7 EL |
1961 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1962 | ||
e3bc5698 JG |
1963 | if (fHelp) |
1964 | return true; | |
1965 | if (!pwalletMain->IsCrypted()) | |
738835d7 | 1966 | throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); |
e3bc5698 JG |
1967 | |
1968 | { | |
1969 | LOCK(cs_nWalletUnlockTime); | |
1970 | pwalletMain->Lock(); | |
1971 | nWalletUnlockTime = 0; | |
1972 | } | |
1973 | ||
1974 | return Value::null; | |
1975 | } | |
1976 | ||
1977 | ||
1978 | Value encryptwallet(const Array& params, bool fHelp) | |
1979 | { | |
b9fb692d JS |
1980 | if (!EnsureWalletIsAvailable(fHelp)) |
1981 | return Value::null; | |
62c0aa9e JG |
1982 | |
1983 | auto fEnableWalletEncryption = GetBoolArg("-developerencryptwallet", false); | |
1984 | ||
1985 | std::string strWalletEncryptionDisabledMsg = ""; | |
1986 | if (!fEnableWalletEncryption) { | |
1532cb75 | 1987 | strWalletEncryptionDisabledMsg = "\nWARNING: Wallet encryption is DISABLED. This call always fails.\n"; |
62c0aa9e JG |
1988 | } |
1989 | ||
e3bc5698 JG |
1990 | if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1)) |
1991 | throw runtime_error( | |
a6099ef3 | 1992 | "encryptwallet \"passphrase\"\n" |
62c0aa9e | 1993 | + strWalletEncryptionDisabledMsg + |
a6099ef3 | 1994 | "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n" |
1995 | "After this, any calls that interact with private keys such as sending or signing \n" | |
1996 | "will require the passphrase to be set prior the making these calls.\n" | |
1997 | "Use the walletpassphrase call for this, and then walletlock call.\n" | |
1998 | "If the wallet is already encrypted, use the walletpassphrasechange call.\n" | |
1999 | "Note that this will shutdown the server.\n" | |
2000 | "\nArguments:\n" | |
2001 | "1. \"passphrase\" (string) The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long.\n" | |
2002 | "\nExamples:\n" | |
2003 | "\nEncrypt you wallet\n" | |
2004 | + HelpExampleCli("encryptwallet", "\"my pass phrase\"") + | |
58c4c0bb | 2005 | "\nNow set the passphrase to use the wallet, such as for signing or sending zcash\n" |
a6099ef3 | 2006 | + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") + |
2007 | "\nNow we can so something like sign\n" | |
58c4c0bb | 2008 | + HelpExampleCli("signmessage", "\"zcashaddress\" \"test message\"") + |
a6099ef3 | 2009 | "\nNow lock the wallet again by removing the passphrase\n" |
2010 | + HelpExampleCli("walletlock", "") + | |
2011 | "\nAs a json rpc call\n" | |
2012 | + HelpExampleRpc("encryptwallet", "\"my pass phrase\"") | |
2013 | ); | |
2014 | ||
4401b2d7 EL |
2015 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2016 | ||
e3bc5698 JG |
2017 | if (fHelp) |
2018 | return true; | |
62c0aa9e | 2019 | if (!fEnableWalletEncryption) { |
1532cb75 | 2020 | throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet encryption is disabled."); |
62c0aa9e | 2021 | } |
e3bc5698 | 2022 | if (pwalletMain->IsCrypted()) |
738835d7 | 2023 | throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); |
e3bc5698 JG |
2024 | |
2025 | // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) | |
2026 | // Alternately, find a way to make params[0] mlock()'d to begin with. | |
2027 | SecureString strWalletPass; | |
2028 | strWalletPass.reserve(100); | |
2029 | strWalletPass = params[0].get_str().c_str(); | |
2030 | ||
2031 | if (strWalletPass.length() < 1) | |
2032 | throw runtime_error( | |
2033 | "encryptwallet <passphrase>\n" | |
2034 | "Encrypts the wallet with <passphrase>."); | |
2035 | ||
2036 | if (!pwalletMain->EncryptWallet(strWalletPass)) | |
738835d7 | 2037 | throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet."); |
e3bc5698 JG |
2038 | |
2039 | // BDB seems to have a bad habit of writing old data into | |
2040 | // slack space in .dat files; that is bad if the old data is | |
331544bc | 2041 | // unencrypted private keys. So: |
e3bc5698 | 2042 | StartShutdown(); |
58c4c0bb | 2043 | return "wallet encrypted; Zcash server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; |
e3bc5698 JG |
2044 | } |
2045 | ||
fdbb537d JG |
2046 | Value lockunspent(const Array& params, bool fHelp) |
2047 | { | |
b9fb692d JS |
2048 | if (!EnsureWalletIsAvailable(fHelp)) |
2049 | return Value::null; | |
2050 | ||
fdbb537d JG |
2051 | if (fHelp || params.size() < 1 || params.size() > 2) |
2052 | throw runtime_error( | |
a6099ef3 | 2053 | "lockunspent unlock [{\"txid\":\"txid\",\"vout\":n},...]\n" |
2054 | "\nUpdates list of temporarily unspendable outputs.\n" | |
90fd8737 | 2055 | "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n" |
58c4c0bb | 2056 | "A locked transaction output will not be chosen by automatic coin selection, when spending zcash.\n" |
a6099ef3 | 2057 | "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n" |
2058 | "is always cleared (by virtue of process exit) when a node stops or fails.\n" | |
2059 | "Also see the listunspent call\n" | |
2060 | "\nArguments:\n" | |
2061 | "1. unlock (boolean, required) Whether to unlock (true) or lock (false) the specified transactions\n" | |
2062 | "2. \"transactions\" (string, required) A json array of objects. Each object the txid (string) vout (numeric)\n" | |
2063 | " [ (json array of json objects)\n" | |
2064 | " {\n" | |
2065 | " \"txid\":\"id\", (string) The transaction id\n" | |
2066 | " \"vout\": n (numeric) The output number\n" | |
2067 | " }\n" | |
2068 | " ,...\n" | |
2069 | " ]\n" | |
2070 | ||
2071 | "\nResult:\n" | |
2072 | "true|false (boolean) Whether the command was successful or not\n" | |
2073 | ||
2074 | "\nExamples:\n" | |
2075 | "\nList the unspent transactions\n" | |
2076 | + HelpExampleCli("listunspent", "") + | |
2077 | "\nLock an unspent transaction\n" | |
2078 | + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + | |
2079 | "\nList the locked transactions\n" | |
2080 | + HelpExampleCli("listlockunspent", "") + | |
2081 | "\nUnlock the transaction again\n" | |
2082 | + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + | |
2083 | "\nAs a json rpc call\n" | |
2084 | + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") | |
2085 | ); | |
fdbb537d | 2086 | |
4401b2d7 EL |
2087 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2088 | ||
fdbb537d | 2089 | if (params.size() == 1) |
856e862f | 2090 | RPCTypeCheck(params, boost::assign::list_of(bool_type)); |
fdbb537d | 2091 | else |
856e862f | 2092 | RPCTypeCheck(params, boost::assign::list_of(bool_type)(array_type)); |
fdbb537d JG |
2093 | |
2094 | bool fUnlock = params[0].get_bool(); | |
2095 | ||
2096 | if (params.size() == 1) { | |
2097 | if (fUnlock) | |
2098 | pwalletMain->UnlockAllCoins(); | |
2099 | return true; | |
2100 | } | |
2101 | ||
2102 | Array outputs = params[1].get_array(); | |
2103 | BOOST_FOREACH(Value& output, outputs) | |
2104 | { | |
2105 | if (output.type() != obj_type) | |
15117692 | 2106 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); |
fdbb537d JG |
2107 | const Object& o = output.get_obj(); |
2108 | ||
856e862f | 2109 | RPCTypeCheck(o, boost::assign::map_list_of("txid", str_type)("vout", int_type)); |
fdbb537d JG |
2110 | |
2111 | string txid = find_value(o, "txid").get_str(); | |
2112 | if (!IsHex(txid)) | |
15117692 | 2113 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); |
fdbb537d JG |
2114 | |
2115 | int nOutput = find_value(o, "vout").get_int(); | |
2116 | if (nOutput < 0) | |
15117692 | 2117 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); |
fdbb537d | 2118 | |
34cdc411 | 2119 | COutPoint outpt(uint256S(txid), nOutput); |
fdbb537d JG |
2120 | |
2121 | if (fUnlock) | |
2122 | pwalletMain->UnlockCoin(outpt); | |
2123 | else | |
2124 | pwalletMain->LockCoin(outpt); | |
2125 | } | |
2126 | ||
2127 | return true; | |
2128 | } | |
2129 | ||
2130 | Value listlockunspent(const Array& params, bool fHelp) | |
2131 | { | |
b9fb692d JS |
2132 | if (!EnsureWalletIsAvailable(fHelp)) |
2133 | return Value::null; | |
2134 | ||
fdbb537d JG |
2135 | if (fHelp || params.size() > 0) |
2136 | throw runtime_error( | |
2137 | "listlockunspent\n" | |
a6099ef3 | 2138 | "\nReturns list of temporarily unspendable outputs.\n" |
2139 | "See the lockunspent call to lock and unlock transactions for spending.\n" | |
2140 | "\nResult:\n" | |
2141 | "[\n" | |
2142 | " {\n" | |
2143 | " \"txid\" : \"transactionid\", (string) The transaction id locked\n" | |
2144 | " \"vout\" : n (numeric) The vout value\n" | |
2145 | " }\n" | |
2146 | " ,...\n" | |
2147 | "]\n" | |
2148 | "\nExamples:\n" | |
2149 | "\nList the unspent transactions\n" | |
2150 | + HelpExampleCli("listunspent", "") + | |
2151 | "\nLock an unspent transaction\n" | |
2152 | + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + | |
2153 | "\nList the locked transactions\n" | |
2154 | + HelpExampleCli("listlockunspent", "") + | |
2155 | "\nUnlock the transaction again\n" | |
2156 | + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + | |
2157 | "\nAs a json rpc call\n" | |
2158 | + HelpExampleRpc("listlockunspent", "") | |
2159 | ); | |
fdbb537d | 2160 | |
4401b2d7 EL |
2161 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2162 | ||
fdbb537d JG |
2163 | vector<COutPoint> vOutpts; |
2164 | pwalletMain->ListLockedCoins(vOutpts); | |
2165 | ||
2166 | Array ret; | |
2167 | ||
2168 | BOOST_FOREACH(COutPoint &outpt, vOutpts) { | |
2169 | Object o; | |
2170 | ||
2171 | o.push_back(Pair("txid", outpt.hash.GetHex())); | |
2172 | o.push_back(Pair("vout", (int)outpt.n)); | |
2173 | ret.push_back(o); | |
2174 | } | |
2175 | ||
2176 | return ret; | |
2177 | } | |
2178 | ||
a943bde6 WL |
2179 | Value settxfee(const Array& params, bool fHelp) |
2180 | { | |
b9fb692d JS |
2181 | if (!EnsureWalletIsAvailable(fHelp)) |
2182 | return Value::null; | |
2183 | ||
a943bde6 WL |
2184 | if (fHelp || params.size() < 1 || params.size() > 1) |
2185 | throw runtime_error( | |
2186 | "settxfee amount\n" | |
6943cb9b | 2187 | "\nSet the transaction fee per kB.\n" |
a943bde6 | 2188 | "\nArguments:\n" |
6943cb9b | 2189 | "1. amount (numeric, required) The transaction fee in BTC/kB rounded to the nearest 0.00000001\n" |
a943bde6 WL |
2190 | "\nResult\n" |
2191 | "true|false (boolean) Returns true if successful\n" | |
2192 | "\nExamples:\n" | |
2193 | + HelpExampleCli("settxfee", "0.00001") | |
2194 | + HelpExampleRpc("settxfee", "0.00001") | |
2195 | ); | |
2196 | ||
4401b2d7 EL |
2197 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2198 | ||
a943bde6 | 2199 | // Amount |
a372168e | 2200 | CAmount nAmount = 0; |
a943bde6 WL |
2201 | if (params[0].get_real() != 0.0) |
2202 | nAmount = AmountFromValue(params[0]); // rejects 0.0 amounts | |
2203 | ||
c6cb21d1 | 2204 | payTxFee = CFeeRate(nAmount, 1000); |
a943bde6 WL |
2205 | return true; |
2206 | } | |
2207 | ||
a00ebb51 DN |
2208 | Value getwalletinfo(const Array& params, bool fHelp) |
2209 | { | |
b9fb692d JS |
2210 | if (!EnsureWalletIsAvailable(fHelp)) |
2211 | return Value::null; | |
2212 | ||
a00ebb51 DN |
2213 | if (fHelp || params.size() != 0) |
2214 | throw runtime_error( | |
2215 | "getwalletinfo\n" | |
2216 | "Returns an object containing various wallet state info.\n" | |
2217 | "\nResult:\n" | |
2218 | "{\n" | |
2219 | " \"walletversion\": xxxxx, (numeric) the wallet version\n" | |
58c4c0bb SB |
2220 | " \"balance\": xxxxxxx, (numeric) the total confirmed zcash balance of the wallet\n" |
2221 | " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed zcash balance of the wallet\n" | |
8024d67d | 2222 | " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet\n" |
a00ebb51 DN |
2223 | " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n" |
2224 | " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" | |
2225 | " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" | |
2226 | " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" | |
2227 | "}\n" | |
2228 | "\nExamples:\n" | |
2229 | + HelpExampleCli("getwalletinfo", "") | |
2230 | + HelpExampleRpc("getwalletinfo", "") | |
2231 | ); | |
2232 | ||
4401b2d7 EL |
2233 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2234 | ||
a00ebb51 DN |
2235 | Object obj; |
2236 | obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); | |
2237 | obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); | |
8024d67d GM |
2238 | obj.push_back(Pair("unconfirmed_balance", ValueFromAmount(pwalletMain->GetUnconfirmedBalance()))); |
2239 | obj.push_back(Pair("immature_balance", ValueFromAmount(pwalletMain->GetImmatureBalance()))); | |
a00ebb51 | 2240 | obj.push_back(Pair("txcount", (int)pwalletMain->mapWallet.size())); |
d56e30ca | 2241 | obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); |
a00ebb51 DN |
2242 | obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); |
2243 | if (pwalletMain->IsCrypted()) | |
d56e30ca | 2244 | obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); |
a00ebb51 DN |
2245 | return obj; |
2246 | } | |
0f5954c4 GA |
2247 | |
2248 | Value resendwallettransactions(const Array& params, bool fHelp) | |
2249 | { | |
b9fb692d JS |
2250 | if (!EnsureWalletIsAvailable(fHelp)) |
2251 | return Value::null; | |
2252 | ||
0f5954c4 GA |
2253 | if (fHelp || params.size() != 0) |
2254 | throw runtime_error( | |
2255 | "resendwallettransactions\n" | |
2256 | "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n" | |
2257 | "Intended only for testing; the wallet code periodically re-broadcasts\n" | |
2258 | "automatically.\n" | |
2259 | "Returns array of transaction ids that were re-broadcast.\n" | |
2260 | ); | |
2261 | ||
2262 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
2263 | ||
2264 | std::vector<uint256> txids = pwalletMain->ResendWalletTransactionsBefore(GetTime()); | |
2265 | Array result; | |
2266 | BOOST_FOREACH(const uint256& txid, txids) | |
2267 | { | |
2268 | result.push_back(txid.ToString()); | |
2269 | } | |
2270 | return result; | |
2271 | } | |
0b9dc9c8 JS |
2272 | |
2273 | Value listunspent(const Array& params, bool fHelp) | |
2274 | { | |
2275 | if (!EnsureWalletIsAvailable(fHelp)) | |
2276 | return Value::null; | |
2277 | ||
2278 | if (fHelp || params.size() > 3) | |
ea9e82df JS |
2279 | throw runtime_error( |
2280 | "listunspent ( minconf maxconf [\"address\",...] )\n" | |
2281 | "\nReturns array of unspent transaction outputs\n" | |
2282 | "with between minconf and maxconf (inclusive) confirmations.\n" | |
2283 | "Optionally filter to only include txouts paid to specified addresses.\n" | |
2284 | "Results are an array of Objects, each of which has:\n" | |
2285 | "{txid, vout, scriptPubKey, amount, confirmations}\n" | |
2286 | "\nArguments:\n" | |
2287 | "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" | |
2288 | "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" | |
58c4c0bb | 2289 | "3. \"addresses\" (string) A json array of zcash addresses to filter\n" |
ea9e82df | 2290 | " [\n" |
58c4c0bb | 2291 | " \"address\" (string) zcash address\n" |
ea9e82df JS |
2292 | " ,...\n" |
2293 | " ]\n" | |
2294 | "\nResult\n" | |
2295 | "[ (array of json object)\n" | |
2296 | " {\n" | |
2297 | " \"txid\" : \"txid\", (string) the transaction id \n" | |
2298 | " \"vout\" : n, (numeric) the vout value\n" | |
58c4c0bb | 2299 | " \"address\" : \"address\", (string) the zcash address\n" |
ea9e82df JS |
2300 | " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" |
2301 | " \"scriptPubKey\" : \"key\", (string) the script key\n" | |
2302 | " \"amount\" : x.xxx, (numeric) the transaction amount in btc\n" | |
2303 | " \"confirmations\" : n (numeric) The number of confirmations\n" | |
2304 | " }\n" | |
2305 | " ,...\n" | |
2306 | "]\n" | |
2307 | ||
2308 | "\nExamples\n" | |
2309 | + HelpExampleCli("listunspent", "") | |
2310 | + HelpExampleCli("listunspent", "6 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") | |
2311 | + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") | |
2312 | ); | |
2313 | ||
0b9dc9c8 | 2314 | RPCTypeCheck(params, boost::assign::list_of(int_type)(int_type)(array_type)); |
ea9e82df | 2315 | |
0b9dc9c8 JS |
2316 | int nMinDepth = 1; |
2317 | if (params.size() > 0) | |
ea9e82df JS |
2318 | nMinDepth = params[0].get_int(); |
2319 | ||
0b9dc9c8 JS |
2320 | int nMaxDepth = 9999999; |
2321 | if (params.size() > 1) | |
ea9e82df JS |
2322 | nMaxDepth = params[1].get_int(); |
2323 | ||
0b9dc9c8 JS |
2324 | set<CBitcoinAddress> setAddress; |
2325 | if (params.size() > 2) { | |
2326 | Array inputs = params[2].get_array(); | |
2327 | BOOST_FOREACH(Value& input, inputs) { | |
2328 | CBitcoinAddress address(input.get_str()); | |
2329 | if (!address.IsValid()) | |
58c4c0bb | 2330 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Zcash address: ")+input.get_str()); |
0b9dc9c8 | 2331 | if (setAddress.count(address)) |
ea9e82df JS |
2332 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str()); |
2333 | setAddress.insert(address); | |
0b9dc9c8 JS |
2334 | } |
2335 | } | |
ea9e82df | 2336 | |
0b9dc9c8 JS |
2337 | Array results; |
2338 | vector<COutput> vecOutputs; | |
2339 | assert(pwalletMain != NULL); | |
2340 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
219953ce | 2341 | pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); |
0b9dc9c8 JS |
2342 | BOOST_FOREACH(const COutput& out, vecOutputs) { |
2343 | if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) | |
ea9e82df JS |
2344 | continue; |
2345 | ||
0b9dc9c8 JS |
2346 | if (setAddress.size()) { |
2347 | CTxDestination address; | |
2348 | if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) | |
ea9e82df JS |
2349 | continue; |
2350 | ||
0b9dc9c8 | 2351 | if (!setAddress.count(address)) |
ea9e82df | 2352 | continue; |
0b9dc9c8 | 2353 | } |
ea9e82df | 2354 | |
0b9dc9c8 JS |
2355 | CAmount nValue = out.tx->vout[out.i].nValue; |
2356 | const CScript& pk = out.tx->vout[out.i].scriptPubKey; | |
2357 | Object entry; | |
805344dc | 2358 | entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); |
0b9dc9c8 JS |
2359 | entry.push_back(Pair("vout", out.i)); |
2360 | CTxDestination address; | |
2361 | if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { | |
2362 | entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); | |
2363 | if (pwalletMain->mapAddressBook.count(address)) | |
ea9e82df | 2364 | entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); |
0b9dc9c8 JS |
2365 | } |
2366 | entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); | |
2367 | if (pk.IsPayToScriptHash()) { | |
2368 | CTxDestination address; | |
2369 | if (ExtractDestination(pk, address)) { | |
8b08d953 | 2370 | const CScriptID& hash = boost::get<CScriptID>(address); |
0b9dc9c8 JS |
2371 | CScript redeemScript; |
2372 | if (pwalletMain->GetCScript(hash, redeemScript)) | |
ea9e82df | 2373 | entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); |
0b9dc9c8 JS |
2374 | } |
2375 | } | |
2376 | entry.push_back(Pair("amount",ValueFromAmount(nValue))); | |
2377 | entry.push_back(Pair("confirmations",out.nDepth)); | |
2378 | entry.push_back(Pair("spendable", out.fSpendable)); | |
2379 | results.push_back(entry); | |
2380 | } | |
ea9e82df | 2381 | |
0b9dc9c8 | 2382 | return results; |
c9fd9078 | 2383 | } |
730790f7 | 2384 | |
1737627c SB |
2385 | Value zc_sample_joinsplit(const json_spirit::Array& params, bool fHelp) |
2386 | { | |
2387 | if (fHelp) { | |
2388 | throw runtime_error( | |
2389 | "zcsamplejoinsplit\n" | |
2390 | "\n" | |
2391 | "Perform a joinsplit and return the JSDescription.\n" | |
2392 | ); | |
2393 | } | |
2394 | ||
2395 | LOCK(cs_main); | |
2396 | ||
2397 | uint256 pubKeyHash; | |
2398 | uint256 anchor = ZCIncrementalMerkleTree().root(); | |
2399 | JSDescription samplejoinsplit(*pzcashParams, | |
2400 | pubKeyHash, | |
2401 | anchor, | |
2402 | {JSInput(), JSInput()}, | |
2403 | {JSOutput(), JSOutput()}, | |
2404 | 0, | |
2405 | 0); | |
2406 | ||
2407 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
2408 | ss << samplejoinsplit; | |
2409 | ||
2410 | return HexStr(ss.begin(), ss.end()); | |
2411 | } | |
2412 | ||
6962bb3d TH |
2413 | Value zc_benchmark(const json_spirit::Array& params, bool fHelp) |
2414 | { | |
2415 | if (!EnsureWalletIsAvailable(fHelp)) { | |
2416 | return Value::null; | |
2417 | } | |
2418 | ||
2419 | if (fHelp || params.size() < 2) { | |
2420 | throw runtime_error( | |
2421 | "zcbenchmark benchmarktype samplecount\n" | |
2422 | "\n" | |
2423 | "Runs a benchmark of the selected type samplecount times,\n" | |
2424 | "returning the running times of each sample.\n" | |
2425 | "\n" | |
2426 | "Output: [\n" | |
2427 | " {\n" | |
2428 | " \"runningtime\": runningtime\n" | |
2429 | " },\n" | |
2430 | " {\n" | |
2431 | " \"runningtime\": runningtime\n" | |
2432 | " }\n" | |
2433 | " ...\n" | |
2434 | "]\n" | |
2435 | ); | |
2436 | } | |
2437 | ||
2438 | LOCK(cs_main); | |
2439 | ||
2440 | std::string benchmarktype = params[0].get_str(); | |
2441 | int samplecount = params[1].get_int(); | |
2442 | ||
2443 | if (samplecount <= 0) { | |
2444 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid samplecount"); | |
2445 | } | |
2446 | ||
2447 | std::vector<double> sample_times; | |
2448 | ||
2449 | if (benchmarktype == "createjoinsplit") { | |
2450 | /* Load the proving now key so that it doesn't happen as part of the | |
2451 | * first joinsplit. */ | |
2dc35992 | 2452 | pzcashParams->loadProvingKey(); |
6962bb3d TH |
2453 | } |
2454 | ||
1737627c | 2455 | JSDescription samplejoinsplit; |
2fbbde59 SB |
2456 | |
2457 | if (benchmarktype == "verifyjoinsplit") { | |
1737627c SB |
2458 | CDataStream ss(ParseHexV(params[2].get_str(), "js"), SER_NETWORK, PROTOCOL_VERSION); |
2459 | ss >> samplejoinsplit; | |
2fbbde59 SB |
2460 | } |
2461 | ||
6962bb3d TH |
2462 | for (int i = 0; i < samplecount; i++) { |
2463 | if (benchmarktype == "sleep") { | |
2464 | sample_times.push_back(benchmark_sleep()); | |
2465 | } else if (benchmarktype == "parameterloading") { | |
2466 | sample_times.push_back(benchmark_parameter_loading()); | |
2467 | } else if (benchmarktype == "createjoinsplit") { | |
2468 | sample_times.push_back(benchmark_create_joinsplit()); | |
2469 | } else if (benchmarktype == "verifyjoinsplit") { | |
1737627c | 2470 | sample_times.push_back(benchmark_verify_joinsplit(samplejoinsplit)); |
bf8def97 | 2471 | } else if (benchmarktype == "solveequihash") { |
f7478de6 | 2472 | if (params.size() < 3) { |
9e52ca32 | 2473 | sample_times.push_back(benchmark_solve_equihash()); |
f7478de6 JG |
2474 | } else { |
2475 | int nThreads = params[2].get_int(); | |
9e52ca32 JG |
2476 | std::vector<double> vals = benchmark_solve_equihash_threaded(nThreads); |
2477 | sample_times.insert(sample_times.end(), vals.begin(), vals.end()); | |
f7478de6 | 2478 | } |
bf8def97 | 2479 | } else if (benchmarktype == "verifyequihash") { |
a1cd1a27 | 2480 | sample_times.push_back(benchmark_verify_equihash()); |
f5edc37f | 2481 | } else if (benchmarktype == "validatelargetx") { |
9c45b501 | 2482 | sample_times.push_back(benchmark_large_tx()); |
6962bb3d TH |
2483 | } else { |
2484 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid benchmarktype"); | |
2485 | } | |
2486 | } | |
2487 | ||
2488 | Array results; | |
9e52ca32 | 2489 | for (auto time : sample_times) { |
6962bb3d | 2490 | Object result; |
9e52ca32 | 2491 | result.push_back(Pair("runningtime", time)); |
6962bb3d TH |
2492 | results.push_back(result); |
2493 | } | |
2494 | ||
2495 | return results; | |
2496 | } | |
2497 | ||
a8ac403d SB |
2498 | Value zc_raw_receive(const json_spirit::Array& params, bool fHelp) |
2499 | { | |
f15b9549 NW |
2500 | if (!EnsureWalletIsAvailable(fHelp)) { |
2501 | return Value::null; | |
2502 | } | |
2503 | ||
2504 | if (fHelp || params.size() != 2) { | |
2505 | throw runtime_error( | |
eae37941 | 2506 | "zcrawreceive zcsecretkey encryptednote\n" |
f15b9549 | 2507 | "\n" |
ca0ec80b | 2508 | "DEPRECATED. Decrypts encryptednote and checks if the coin commitments\n" |
f15b9549 NW |
2509 | "are in the blockchain as indicated by the \"exists\" result.\n" |
2510 | "\n" | |
2511 | "Output: {\n" | |
2512 | " \"amount\": value,\n" | |
4bc00dc1 | 2513 | " \"note\": noteplaintext,\n" |
f15b9549 NW |
2514 | " \"exists\": exists\n" |
2515 | "}\n" | |
2516 | ); | |
2517 | } | |
2518 | ||
a8ac403d | 2519 | RPCTypeCheck(params, boost::assign::list_of(str_type)(str_type)); |
f15b9549 | 2520 | |
a8ac403d SB |
2521 | LOCK(cs_main); |
2522 | ||
0d6864e4 SB |
2523 | CZCSpendingKey spendingkey(params[0].get_str()); |
2524 | SpendingKey k = spendingkey.Get(); | |
a8ac403d | 2525 | |
6c36a9fe SB |
2526 | uint256 epk; |
2527 | unsigned char nonce; | |
2528 | ZCNoteEncryption::Ciphertext ct; | |
2dc35992 | 2529 | uint256 h_sig; |
6c36a9fe SB |
2530 | |
2531 | { | |
4bc00dc1 | 2532 | CDataStream ssData(ParseHexV(params[1], "encrypted_note"), SER_NETWORK, PROTOCOL_VERSION); |
6c36a9fe SB |
2533 | try { |
2534 | ssData >> nonce; | |
2535 | ssData >> epk; | |
2536 | ssData >> ct; | |
2dc35992 | 2537 | ssData >> h_sig; |
6c36a9fe SB |
2538 | } catch(const std::exception &) { |
2539 | throw runtime_error( | |
4bc00dc1 | 2540 | "encrypted_note could not be decoded" |
6c36a9fe SB |
2541 | ); |
2542 | } | |
2543 | } | |
2544 | ||
2dc35992 | 2545 | ZCNoteDecryption decryptor(k.viewing_key()); |
a8ac403d | 2546 | |
2dc35992 SB |
2547 | NotePlaintext npt = NotePlaintext::decrypt( |
2548 | decryptor, | |
2549 | ct, | |
2550 | epk, | |
2551 | h_sig, | |
2552 | nonce | |
2553 | ); | |
2554 | PaymentAddress payment_addr = k.address(); | |
2555 | Note decrypted_note = npt.note(payment_addr); | |
a8ac403d SB |
2556 | |
2557 | assert(pwalletMain != NULL); | |
2dc35992 | 2558 | std::vector<boost::optional<ZCIncrementalWitness>> witnesses; |
a8ac403d | 2559 | uint256 anchor; |
2dc35992 | 2560 | uint256 commitment = decrypted_note.cm(); |
4bc00dc1 | 2561 | pwalletMain->WitnessNoteCommitment( |
2dc35992 SB |
2562 | {commitment}, |
2563 | witnesses, | |
2564 | anchor | |
2565 | ); | |
a8ac403d SB |
2566 | |
2567 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
2dc35992 | 2568 | ss << npt; |
a8ac403d SB |
2569 | |
2570 | Object result; | |
2dc35992 | 2571 | result.push_back(Pair("amount", ValueFromAmount(decrypted_note.value))); |
4bc00dc1 | 2572 | result.push_back(Pair("note", HexStr(ss.begin(), ss.end()))); |
2dc35992 | 2573 | result.push_back(Pair("exists", (bool) witnesses[0])); |
a8ac403d SB |
2574 | return result; |
2575 | } | |
2576 | ||
2dc35992 SB |
2577 | |
2578 | ||
b7e4abd6 | 2579 | Value zc_raw_joinsplit(const json_spirit::Array& params, bool fHelp) |
730790f7 | 2580 | { |
f15b9549 NW |
2581 | if (!EnsureWalletIsAvailable(fHelp)) { |
2582 | return Value::null; | |
2583 | } | |
2584 | ||
2585 | if (fHelp || params.size() != 5) { | |
2586 | throw runtime_error( | |
eae37941 | 2587 | "zcrawjoinsplit rawtx inputs outputs vpub_old vpub_new\n" |
4bc00dc1 | 2588 | " inputs: a JSON object mapping {note: zcsecretkey, ...}\n" |
f15b9549 NW |
2589 | " outputs: a JSON object mapping {zcaddr: value, ...}\n" |
2590 | "\n" | |
ca0ec80b | 2591 | "DEPRECATED. Splices a joinsplit into rawtx. Inputs are unilaterally confidential.\n" |
f15b9549 NW |
2592 | "Outputs are confidential between sender/receiver. The vpub_old and\n" |
2593 | "vpub_new values are globally public and move transparent value into\n" | |
2594 | "or out of the confidential value store, respectively.\n" | |
2595 | "\n" | |
2596 | "Note: The caller is responsible for delivering the output enc1 and\n" | |
2597 | "enc2 to the appropriate recipients, as well as signing rawtxout and\n" | |
2598 | "ensuring it is mined. (A future RPC call will deliver the confidential\n" | |
2599 | "payments in-band on the blockchain.)\n" | |
2600 | "\n" | |
2601 | "Output: {\n" | |
4bc00dc1 DH |
2602 | " \"encryptednote1\": enc1,\n" |
2603 | " \"encryptednote2\": enc2,\n" | |
f15b9549 NW |
2604 | " \"rawtxn\": rawtxout\n" |
2605 | "}\n" | |
2606 | ); | |
2607 | } | |
2608 | ||
a8ac403d | 2609 | LOCK(cs_main); |
730790f7 SB |
2610 | |
2611 | CTransaction tx; | |
2612 | if (!DecodeHexTx(tx, params[0].get_str())) | |
2613 | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); | |
2614 | ||
2615 | Object inputs = params[1].get_obj(); | |
2616 | Object outputs = params[2].get_obj(); | |
2617 | ||
2618 | CAmount vpub_old(0); | |
2619 | CAmount vpub_new(0); | |
2620 | ||
2621 | if (params[3].get_real() != 0.0) | |
2622 | vpub_old = AmountFromValue(params[3]); | |
2623 | ||
2624 | if (params[4].get_real() != 0.0) | |
2625 | vpub_new = AmountFromValue(params[4]); | |
2626 | ||
b7e4abd6 SB |
2627 | std::vector<JSInput> vjsin; |
2628 | std::vector<JSOutput> vjsout; | |
2dc35992 SB |
2629 | std::vector<Note> notes; |
2630 | std::vector<SpendingKey> keys; | |
2631 | std::vector<uint256> commitments; | |
a8ac403d | 2632 | |
730790f7 SB |
2633 | BOOST_FOREACH(const Pair& s, inputs) |
2634 | { | |
0d6864e4 SB |
2635 | CZCSpendingKey spendingkey(s.value_.get_str()); |
2636 | SpendingKey k = spendingkey.Get(); | |
a8ac403d | 2637 | |
2dc35992 | 2638 | keys.push_back(k); |
a8ac403d | 2639 | |
2dc35992 | 2640 | NotePlaintext npt; |
a8ac403d | 2641 | |
2dc35992 | 2642 | { |
4bc00dc1 | 2643 | CDataStream ssData(ParseHexV(s.name_, "note"), SER_NETWORK, PROTOCOL_VERSION); |
2dc35992 | 2644 | ssData >> npt; |
a8ac403d SB |
2645 | } |
2646 | ||
2dc35992 SB |
2647 | PaymentAddress addr = k.address(); |
2648 | Note note = npt.note(addr); | |
2649 | notes.push_back(note); | |
2650 | commitments.push_back(note.cm()); | |
2651 | } | |
2652 | ||
2653 | uint256 anchor; | |
2654 | std::vector<boost::optional<ZCIncrementalWitness>> witnesses; | |
4bc00dc1 | 2655 | pwalletMain->WitnessNoteCommitment(commitments, witnesses, anchor); |
2dc35992 SB |
2656 | |
2657 | assert(witnesses.size() == notes.size()); | |
2658 | assert(notes.size() == keys.size()); | |
2659 | ||
2660 | { | |
2661 | for (size_t i = 0; i < witnesses.size(); i++) { | |
2662 | if (!witnesses[i]) { | |
2663 | throw runtime_error( | |
b7e4abd6 | 2664 | "joinsplit input could not be found in tree" |
2dc35992 SB |
2665 | ); |
2666 | } | |
2667 | ||
b7e4abd6 | 2668 | vjsin.push_back(JSInput(*witnesses[i], notes[i], keys[i])); |
2dc35992 | 2669 | } |
730790f7 | 2670 | } |
730790f7 | 2671 | |
b7e4abd6 SB |
2672 | while (vjsin.size() < ZC_NUM_JS_INPUTS) { |
2673 | vjsin.push_back(JSInput()); | |
a8ac403d | 2674 | } |
730790f7 SB |
2675 | |
2676 | BOOST_FOREACH(const Pair& s, outputs) | |
2677 | { | |
e104fcdd SB |
2678 | CZCPaymentAddress pubaddr(s.name_); |
2679 | PaymentAddress addrTo = pubaddr.Get(); | |
730790f7 SB |
2680 | CAmount nAmount = AmountFromValue(s.value_); |
2681 | ||
b7e4abd6 | 2682 | vjsout.push_back(JSOutput(addrTo, nAmount)); |
730790f7 SB |
2683 | } |
2684 | ||
b7e4abd6 SB |
2685 | while (vjsout.size() < ZC_NUM_JS_OUTPUTS) { |
2686 | vjsout.push_back(JSOutput()); | |
730790f7 SB |
2687 | } |
2688 | ||
2689 | // TODO | |
b7e4abd6 SB |
2690 | if (vjsout.size() != ZC_NUM_JS_INPUTS || vjsin.size() != ZC_NUM_JS_OUTPUTS) { |
2691 | throw runtime_error("unsupported joinsplit input/output counts"); | |
730790f7 SB |
2692 | } |
2693 | ||
320f2cc7 SB |
2694 | uint256 joinSplitPubKey; |
2695 | unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; | |
2696 | crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); | |
6aae9d1a TH |
2697 | |
2698 | CMutableTransaction mtx(tx); | |
2699 | mtx.nVersion = 2; | |
2700 | mtx.joinSplitPubKey = joinSplitPubKey; | |
2701 | ||
22de1602 SB |
2702 | JSDescription jsdesc(*pzcashParams, |
2703 | joinSplitPubKey, | |
2704 | anchor, | |
2705 | {vjsin[0], vjsin[1]}, | |
2706 | {vjsout[0], vjsout[1]}, | |
2707 | vpub_old, | |
2708 | vpub_new); | |
320f2cc7 | 2709 | |
22de1602 | 2710 | assert(jsdesc.Verify(*pzcashParams, joinSplitPubKey)); |
730790f7 | 2711 | |
22de1602 | 2712 | mtx.vjoinsplit.push_back(jsdesc); |
730790f7 | 2713 | |
6aae9d1a TH |
2714 | // Empty output script. |
2715 | CScript scriptCode; | |
2716 | CTransaction signTx(mtx); | |
2717 | uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL); | |
6aae9d1a TH |
2718 | |
2719 | // Add the signature | |
320f2cc7 SB |
2720 | assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, |
2721 | dataToBeSigned.begin(), 32, | |
2722 | joinSplitPrivKey | |
2723 | ) == 0); | |
2724 | ||
2725 | // Sanity check | |
2726 | assert(crypto_sign_verify_detached(&mtx.joinSplitSig[0], | |
2727 | dataToBeSigned.begin(), 32, | |
2728 | mtx.joinSplitPubKey.begin() | |
2729 | ) == 0); | |
6aae9d1a | 2730 | |
730790f7 SB |
2731 | CTransaction rawTx(mtx); |
2732 | ||
2733 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
2734 | ss << rawTx; | |
2735 | ||
4bc00dc1 DH |
2736 | std::string encryptedNote1; |
2737 | std::string encryptedNote2; | |
6c36a9fe SB |
2738 | { |
2739 | CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); | |
2740 | ss2 << ((unsigned char) 0x00); | |
22de1602 SB |
2741 | ss2 << jsdesc.ephemeralKey; |
2742 | ss2 << jsdesc.ciphertexts[0]; | |
2743 | ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey); | |
6c36a9fe | 2744 | |
4bc00dc1 | 2745 | encryptedNote1 = HexStr(ss2.begin(), ss2.end()); |
6c36a9fe SB |
2746 | } |
2747 | { | |
2748 | CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); | |
2749 | ss2 << ((unsigned char) 0x01); | |
22de1602 SB |
2750 | ss2 << jsdesc.ephemeralKey; |
2751 | ss2 << jsdesc.ciphertexts[1]; | |
2752 | ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey); | |
6c36a9fe | 2753 | |
4bc00dc1 | 2754 | encryptedNote2 = HexStr(ss2.begin(), ss2.end()); |
6c36a9fe SB |
2755 | } |
2756 | ||
730790f7 | 2757 | Object result; |
4bc00dc1 DH |
2758 | result.push_back(Pair("encryptednote1", encryptedNote1)); |
2759 | result.push_back(Pair("encryptednote2", encryptedNote2)); | |
730790f7 SB |
2760 | result.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end()))); |
2761 | return result; | |
2762 | } | |
2763 | ||
2764 | Value zc_raw_keygen(const json_spirit::Array& params, bool fHelp) | |
2765 | { | |
f15b9549 NW |
2766 | if (!EnsureWalletIsAvailable(fHelp)) { |
2767 | return Value::null; | |
2768 | } | |
2769 | ||
2770 | if (fHelp || params.size() != 0) { | |
2771 | throw runtime_error( | |
eae37941 | 2772 | "zcrawkeygen\n" |
f15b9549 | 2773 | "\n" |
ca0ec80b | 2774 | "DEPRECATED. Generate a zcaddr which can send and receive confidential values.\n" |
f15b9549 NW |
2775 | "\n" |
2776 | "Output: {\n" | |
2777 | " \"zcaddress\": zcaddr,\n" | |
2778 | " \"zcsecretkey\": zcsecretkey,\n" | |
2779 | "}\n" | |
2780 | ); | |
2781 | } | |
2782 | ||
2dc35992 SB |
2783 | auto k = SpendingKey::random(); |
2784 | auto addr = k.address(); | |
2785 | auto viewing_key = k.viewing_key(); | |
730790f7 | 2786 | |
2dc35992 | 2787 | CDataStream viewing(SER_NETWORK, PROTOCOL_VERSION); |
730790f7 | 2788 | |
2dc35992 | 2789 | viewing << viewing_key; |
730790f7 | 2790 | |
e104fcdd | 2791 | CZCPaymentAddress pubaddr(addr); |
0d6864e4 | 2792 | CZCSpendingKey spendingkey(k); |
2dc35992 | 2793 | std::string viewing_hex = HexStr(viewing.begin(), viewing.end()); |
730790f7 SB |
2794 | |
2795 | Object result; | |
e104fcdd | 2796 | result.push_back(Pair("zcaddress", pubaddr.ToString())); |
0d6864e4 | 2797 | result.push_back(Pair("zcsecretkey", spendingkey.ToString())); |
2dc35992 | 2798 | result.push_back(Pair("zcviewingkey", viewing_hex)); |
730790f7 | 2799 | return result; |
f15b9549 | 2800 | } |
c1c45943 S |
2801 | |
2802 | ||
2803 | Value z_getnewaddress(const Array& params, bool fHelp) | |
2804 | { | |
2805 | if (!EnsureWalletIsAvailable(fHelp)) | |
2806 | return Value::null; | |
2807 | ||
49e591eb | 2808 | if (fHelp || params.size() > 0) |
c1c45943 S |
2809 | throw runtime_error( |
2810 | "z_getnewaddress\n" | |
2811 | "\nReturns a new zaddr for receiving payments.\n" | |
2812 | "\nArguments:\n" | |
2813 | "\nResult:\n" | |
2814 | "\"zcashaddress\" (string) The new zaddr\n" | |
2815 | "\nExamples:\n" | |
2816 | + HelpExampleCli("z_getnewaddress", "") | |
2817 | + HelpExampleRpc("z_getnewaddress", "") | |
2818 | ); | |
2819 | ||
2820 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
2821 | ||
73699cea S |
2822 | EnsureWalletIsUnlocked(); |
2823 | ||
c1c45943 S |
2824 | CZCPaymentAddress pubaddr = pwalletMain->GenerateNewZKey(); |
2825 | std::string result = pubaddr.ToString(); | |
2826 | return result; | |
2827 | } | |
2828 | ||
e709997f S |
2829 | |
2830 | Value z_listaddresses(const Array& params, bool fHelp) | |
2831 | { | |
2832 | if (!EnsureWalletIsAvailable(fHelp)) | |
2833 | return Value::null; | |
2834 | ||
2835 | if (fHelp || params.size() > 1) | |
2836 | throw runtime_error( | |
2837 | "z_listaddresses\n" | |
2838 | "\nReturns the list of zaddr belonging to the wallet.\n" | |
2839 | "\nArguments:\n" | |
2840 | "\nResult:\n" | |
2841 | "[ (json array of string)\n" | |
2842 | " \"zaddr\" (string) a zaddr belonging to the wallet\n" | |
2843 | " ,...\n" | |
2844 | "]\n" | |
2845 | "\nExamples:\n" | |
2846 | + HelpExampleCli("z_listaddresses", "") | |
2847 | + HelpExampleRpc("z_listaddresses", "") | |
2848 | ); | |
2849 | ||
2850 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
2851 | ||
2852 | Array ret; | |
2853 | std::set<libzcash::PaymentAddress> addresses; | |
2854 | pwalletMain->GetPaymentAddresses(addresses); | |
2855 | for (auto addr : addresses ) { | |
2856 | ret.push_back(CZCPaymentAddress(addr).ToString()); | |
2857 | } | |
2858 | return ret; | |
2859 | } | |
2860 | ||
dec49d1f | 2861 | CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1) { |
a0a3334c S |
2862 | set<CBitcoinAddress> setAddress; |
2863 | vector<COutput> vecOutputs; | |
2864 | CAmount balance = 0; | |
2865 | ||
2866 | if (transparentAddress.length() > 0) { | |
2867 | CBitcoinAddress taddr = CBitcoinAddress(transparentAddress); | |
2868 | if (!taddr.IsValid()) { | |
2869 | throw std::runtime_error("invalid transparent address"); | |
2870 | } | |
2871 | setAddress.insert(taddr); | |
2872 | } | |
2873 | ||
2874 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
2875 | ||
2876 | pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); | |
2877 | ||
2878 | BOOST_FOREACH(const COutput& out, vecOutputs) { | |
2879 | if (out.nDepth < minDepth) { | |
2880 | continue; | |
2881 | } | |
2882 | ||
2883 | if (setAddress.size()) { | |
2884 | CTxDestination address; | |
2885 | if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { | |
2886 | continue; | |
2887 | } | |
2888 | ||
2889 | if (!setAddress.count(address)) { | |
2890 | continue; | |
2891 | } | |
2892 | } | |
2893 | ||
2894 | CAmount nValue = out.tx->vout[out.i].nValue; | |
2895 | balance += nValue; | |
2896 | } | |
2897 | return balance; | |
2898 | } | |
2899 | ||
dec49d1f | 2900 | CAmount getBalanceZaddr(std::string address, int minDepth = 1) { |
a0a3334c | 2901 | CAmount balance = 0; |
a9743bc8 | 2902 | std::vector<CNotePlaintextEntry> entries; |
a0a3334c | 2903 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1b141933 | 2904 | pwalletMain->GetFilteredNotes(entries, address, minDepth); |
a9743bc8 S |
2905 | for (auto & entry : entries) { |
2906 | balance += CAmount(entry.plaintext.value); | |
a0a3334c | 2907 | } |
a0a3334c S |
2908 | return balance; |
2909 | } | |
2910 | ||
2911 | ||
6c41028f S |
2912 | Value z_listreceivedbyaddress(const Array& params, bool fHelp) |
2913 | { | |
2914 | if (!EnsureWalletIsAvailable(fHelp)) | |
2915 | return Value::null; | |
2916 | ||
2917 | if (fHelp || params.size()==0 || params.size() >2) | |
2918 | throw runtime_error( | |
2919 | "z_listreceivedbyaddress \"address\" ( minconf )\n" | |
2920 | "\nReturn a list of amounts received by a zaddr belonging to the node’s wallet.\n" | |
2921 | "\nArguments:\n" | |
2922 | "1. \"address\" (string) The private address.\n" | |
2923 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" | |
2924 | "\nResult:\n" | |
2925 | "{\n" | |
2926 | " \"txid\": xxxxx, (string) the transaction id\n" | |
2927 | " \"amount\": xxxxx, (numeric) the amount of value in the note\n" | |
2928 | " \"memo\": xxxxx, (string) hexademical string representation of memo field\n" | |
2929 | "}\n" | |
2930 | ); | |
2931 | ||
2932 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
2933 | ||
2934 | int nMinDepth = 1; | |
2935 | if (params.size() > 1) { | |
2936 | nMinDepth = params[1].get_int(); | |
2937 | } | |
2938 | if (nMinDepth < 0) { | |
2939 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); | |
2940 | } | |
2941 | ||
2942 | // Check that the from address is valid. | |
2943 | auto fromaddress = params[0].get_str(); | |
2944 | ||
2945 | libzcash::PaymentAddress zaddr; | |
2946 | CZCPaymentAddress address(fromaddress); | |
2947 | try { | |
2948 | zaddr = address.Get(); | |
2949 | } catch (std::runtime_error) { | |
2950 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr."); | |
2951 | } | |
2952 | ||
2953 | if (!pwalletMain->HaveSpendingKey(zaddr)) { | |
2954 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found."); | |
2955 | } | |
2956 | ||
2957 | ||
2958 | Array result; | |
2959 | std::vector<CNotePlaintextEntry> entries; | |
2960 | pwalletMain->GetFilteredNotes(entries, fromaddress, nMinDepth, false); | |
2961 | for (CNotePlaintextEntry & entry : entries) { | |
2962 | Object obj; | |
2963 | obj.push_back(Pair("txid",entry.jsop.hash.ToString())); | |
2964 | obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.plaintext.value)))); | |
2965 | std::string data(entry.plaintext.memo.begin(), entry.plaintext.memo.end()); | |
2966 | obj.push_back(Pair("memo", HexStr(data))); | |
2967 | result.push_back(obj); | |
2968 | } | |
2969 | return result; | |
2970 | } | |
2971 | ||
2972 | ||
a0a3334c S |
2973 | Value z_getbalance(const Array& params, bool fHelp) |
2974 | { | |
2975 | if (!EnsureWalletIsAvailable(fHelp)) | |
2976 | return Value::null; | |
2977 | ||
2978 | if (fHelp || params.size()==0 || params.size() >2) | |
2979 | throw runtime_error( | |
2980 | "z_getbalance \"address\" ( minconf )\n" | |
2981 | "\nReturns the balance of a taddr or zaddr belonging to the node’s wallet.\n" | |
2982 | "\nArguments:\n" | |
2983 | "1. \"address\" (string) The selected address. It may be a transparent or private address.\n" | |
2984 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" | |
2985 | "\nResult:\n" | |
2986 | "amount (numeric) The total amount in ZEC received for this address.\n" | |
2987 | "\nExamples:\n" | |
2988 | "\nThe total amount received by address \"myaddress\"\n" | |
2989 | + HelpExampleCli("z_getbalance", "\"myaddress\"") + | |
2990 | "\nThe total amount received by address \"myaddress\" at least 5 blocks confirmed\n" | |
2991 | + HelpExampleCli("z_getbalance", "\"myaddress\" 5") + | |
2992 | "\nAs a json rpc call\n" | |
2993 | + HelpExampleRpc("z_getbalance", "\"myaddress\", 5") | |
2994 | ); | |
2995 | ||
2996 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
2997 | ||
2998 | int nMinDepth = 1; | |
2999 | if (params.size() > 1) { | |
3000 | nMinDepth = params[1].get_int(); | |
3001 | } | |
12448b64 S |
3002 | if (nMinDepth < 0) { |
3003 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); | |
3004 | } | |
a0a3334c S |
3005 | |
3006 | // Check that the from address is valid. | |
3007 | auto fromaddress = params[0].get_str(); | |
3008 | bool fromTaddr = false; | |
3009 | CBitcoinAddress taddr(fromaddress); | |
3010 | fromTaddr = taddr.IsValid(); | |
3011 | libzcash::PaymentAddress zaddr; | |
3012 | if (!fromTaddr) { | |
3013 | CZCPaymentAddress address(fromaddress); | |
3014 | try { | |
3015 | zaddr = address.Get(); | |
3016 | } catch (std::runtime_error) { | |
3017 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); | |
3018 | } | |
12448b64 S |
3019 | if (!pwalletMain->HaveSpendingKey(zaddr)) { |
3020 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found."); | |
3021 | } | |
a0a3334c S |
3022 | } |
3023 | ||
3024 | CAmount nBalance = 0; | |
3025 | if (fromTaddr) { | |
3026 | nBalance = getBalanceTaddr(fromaddress, nMinDepth); | |
3027 | } else { | |
3028 | nBalance = getBalanceZaddr(fromaddress, nMinDepth); | |
3029 | } | |
3030 | ||
3031 | return ValueFromAmount(nBalance); | |
3032 | } | |
3033 | ||
3034 | ||
3035 | Value z_gettotalbalance(const Array& params, bool fHelp) | |
3036 | { | |
3037 | if (!EnsureWalletIsAvailable(fHelp)) | |
3038 | return Value::null; | |
3039 | ||
3040 | if (fHelp || params.size() > 1) | |
3041 | throw runtime_error( | |
3042 | "z_gettotalbalance ( minconf )\n" | |
3043 | "\nReturn the total value of funds stored in the node’s wallet.\n" | |
3044 | "\nArguments:\n" | |
3045 | "1. minconf (numeric, optional, default=1) Only include private and transparent transactions confirmed at least this many times.\n" | |
3046 | "\nResult:\n" | |
3047 | "{\n" | |
3048 | " \"transparent\": xxxxx, (numeric) the total balance of transparent funds\n" | |
3049 | " \"private\": xxxxx, (numeric) the total balance of private funds\n" | |
3050 | " \"total\": xxxxx, (numeric) the total balance of both transparent and private funds\n" | |
3051 | "}\n" | |
3052 | "\nExamples:\n" | |
3053 | "\nThe total amount in the wallet\n" | |
3054 | + HelpExampleCli("z_gettotalbalance", "") + | |
3055 | "\nThe total amount in the wallet at least 5 blocks confirmed\n" | |
3056 | + HelpExampleCli("z_gettotalbalance", "5") + | |
3057 | "\nAs a json rpc call\n" | |
3058 | + HelpExampleRpc("z_gettotalbalance", "5") | |
3059 | ); | |
3060 | ||
3061 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3062 | ||
3063 | int nMinDepth = 1; | |
3064 | if (params.size() == 1) { | |
3065 | nMinDepth = params[0].get_int(); | |
3066 | } | |
12448b64 S |
3067 | if (nMinDepth < 0) { |
3068 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); | |
3069 | } | |
a0a3334c S |
3070 | |
3071 | // getbalance and "getbalance * 1 true" should return the same number | |
3072 | // but they don't because wtx.GetAmounts() does not handle tx where there are no outputs | |
3073 | // pwalletMain->GetBalance() does not accept min depth parameter | |
3074 | // so we use our own method to get balance of utxos. | |
3075 | CAmount nBalance = getBalanceTaddr("", nMinDepth); | |
3076 | CAmount nPrivateBalance = getBalanceZaddr("", nMinDepth); | |
3077 | CAmount nTotalBalance = nBalance + nPrivateBalance; | |
3078 | Object result; | |
3079 | result.push_back(Pair("transparent", FormatMoney(nBalance, false))); | |
3080 | result.push_back(Pair("private", FormatMoney(nPrivateBalance, false))); | |
3081 | result.push_back(Pair("total", FormatMoney(nTotalBalance, false))); | |
3082 | return result; | |
3083 | } | |
3084 | ||
c1eae280 S |
3085 | Value z_getoperationresult(const Array& params, bool fHelp) |
3086 | { | |
3087 | if (!EnsureWalletIsAvailable(fHelp)) | |
3088 | return Value::null; | |
3089 | ||
3090 | if (fHelp || params.size() > 1) | |
3091 | throw runtime_error( | |
3092 | "z_getoperationresult ([\"operationid\", ... ]) \n" | |
3093 | "\nRetrieve the result and status of an operation which has finished, and then remove the operation from memory." | |
3094 | + HelpRequiringPassphrase() + "\n" | |
3095 | "\nArguments:\n" | |
3096 | "1. \"operationid\" (array, optional) A list of operation ids we are interested in. If not provided, examine all operations known to the node.\n" | |
3097 | "\nResult:\n" | |
3098 | "\" [object, ...]\" (array) A list of JSON objects\n" | |
3099 | ); | |
3100 | ||
3101 | // This call will remove finished operations | |
3102 | return z_getoperationstatus_IMPL(params, true); | |
3103 | } | |
fc72c078 S |
3104 | |
3105 | Value z_getoperationstatus(const Array& params, bool fHelp) | |
3106 | { | |
3107 | if (!EnsureWalletIsAvailable(fHelp)) | |
3108 | return Value::null; | |
3109 | ||
34f0001c | 3110 | if (fHelp || params.size() > 1) |
fc72c078 | 3111 | throw runtime_error( |
34f0001c | 3112 | "z_getoperationstatus ([\"operationid\", ... ]) \n" |
c1eae280 | 3113 | "\nGet operation status and any associated result or error data. The operation will remain in memory." |
fc72c078 S |
3114 | + HelpRequiringPassphrase() + "\n" |
3115 | "\nArguments:\n" | |
c1eae280 | 3116 | "1. \"operationid\" (array, optional) A list of operation ids we are interested in. If not provided, examine all operations known to the node.\n" |
fc72c078 | 3117 | "\nResult:\n" |
34f0001c | 3118 | "\" [object, ...]\" (array) A list of JSON objects\n" |
fc72c078 | 3119 | ); |
c1eae280 S |
3120 | |
3121 | // This call is idempotent so we don't want to remove finished operations | |
3122 | return z_getoperationstatus_IMPL(params, false); | |
3123 | } | |
fc72c078 | 3124 | |
c1eae280 S |
3125 | Value z_getoperationstatus_IMPL(const Array& params, bool fRemoveFinishedOperations=false) |
3126 | { | |
fc72c078 S |
3127 | LOCK2(cs_main, pwalletMain->cs_wallet); |
3128 | ||
34f0001c S |
3129 | std::set<AsyncRPCOperationId> filter; |
3130 | if (params.size()==1) { | |
3131 | Array ids = params[0].get_array(); | |
3132 | for (Value & v : ids) { | |
3133 | filter.insert(v.get_str()); | |
3134 | } | |
fc72c078 | 3135 | } |
34f0001c | 3136 | bool useFilter = (filter.size()>0); |
fc72c078 | 3137 | |
34f0001c S |
3138 | Array ret; |
3139 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); | |
3140 | std::vector<AsyncRPCOperationId> ids = q->getAllOperationIds(); | |
3141 | ||
3142 | for (auto id : ids) { | |
3143 | if (useFilter && !filter.count(id)) | |
3144 | continue; | |
3145 | ||
3146 | std::shared_ptr<AsyncRPCOperation> operation = q->getOperationForId(id); | |
3147 | if (!operation) { | |
3148 | continue; | |
3149 | // It's possible that the operation was removed from the internal queue and map during this loop | |
3150 | // throw JSONRPCError(RPC_INVALID_PARAMETER, "No operation exists for that id."); | |
3151 | } | |
3152 | ||
3153 | Value status = operation->getStatus(); | |
fc72c078 | 3154 | |
c1eae280 S |
3155 | if (fRemoveFinishedOperations) { |
3156 | // Caller is only interested in retrieving finished results | |
3157 | if (operation->isSuccess() || operation->isFailed() || operation->isCancelled()) { | |
3158 | ret.push_back(status); | |
3159 | q->popOperationForId(id); | |
3160 | } | |
3161 | } else { | |
3162 | ret.push_back(status); | |
34f0001c | 3163 | } |
fc72c078 S |
3164 | } |
3165 | ||
34f0001c | 3166 | return ret; |
fc72c078 S |
3167 | } |
3168 | ||
3920292b S |
3169 | |
3170 | // Here we define the maximum number of zaddr outputs that can be included in a transaction. | |
3171 | // If input notes are small, we might actually require more than one joinsplit per zaddr output. | |
3172 | // For now though, we assume we use one joinsplit per zaddr output (and the second output note is change). | |
3173 | // We reduce the result by 1 to ensure there is room for non-joinsplit CTransaction data. | |
3174 | #define Z_SENDMANY_MAX_ZADDR_OUTPUTS ((MAX_TX_SIZE / JSDescription().GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION)) - 1) | |
3175 | ||
3176 | // transaction.h comment: spending taddr output requires CTxIn >= 148 bytes and typical taddr txout is 34 bytes | |
3177 | #define CTXIN_SPEND_DUST_SIZE 148 | |
3178 | #define CTXOUT_REGULAR_SIZE 34 | |
3179 | ||
fc72c078 S |
3180 | Value z_sendmany(const Array& params, bool fHelp) |
3181 | { | |
3182 | if (!EnsureWalletIsAvailable(fHelp)) | |
3183 | return Value::null; | |
3184 | ||
3185 | if (fHelp || params.size() < 2 || params.size() > 3) | |
3186 | throw runtime_error( | |
3187 | "z_sendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf )\n" | |
fc72c078 | 3188 | "\nSend multiple times. Amounts are double-precision floating point numbers." |
b7d7b2ad | 3189 | "\nChange from a taddr flows to a new taddr address, while change from zaddr returns to itself." |
db026807 | 3190 | "\nWhen sending coinbase UTXOs to a zaddr, change is not alllowed. The entire value of the UTXO(s) must be consumed." |
3920292b | 3191 | + strprintf("\nCurrently, the maximum number of zaddr outputs is %d due to transaction size limits.\n", Z_SENDMANY_MAX_ZADDR_OUTPUTS) |
fc72c078 S |
3192 | + HelpRequiringPassphrase() + "\n" |
3193 | "\nArguments:\n" | |
3194 | "1. \"fromaddress\" (string, required) The taddr or zaddr to send the funds from.\n" | |
3195 | "2. \"amounts\" (array, required) An array of json objects representing the amounts to send.\n" | |
3196 | " [{\n" | |
3197 | " \"address\":address (string, required) The address is a taddr or zaddr\n" | |
3198 | " \"amount\":amount (numeric, required) The numeric amount in ZEC is the value\n" | |
3199 | " \"memo\":memo (string, optional) If the address is a zaddr, raw data represented in hexadecimal string format\n" | |
3200 | " }, ... ]\n" | |
3201 | "3. minconf (numeric, optional, default=1) Only use funds confirmed at least this many times.\n" | |
3202 | "\nResult:\n" | |
3203 | "\"operationid\" (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" | |
3204 | ); | |
3205 | ||
3206 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3207 | ||
fc72c078 S |
3208 | // Check that the from address is valid. |
3209 | auto fromaddress = params[0].get_str(); | |
3210 | bool fromTaddr = false; | |
3211 | CBitcoinAddress taddr(fromaddress); | |
3212 | fromTaddr = taddr.IsValid(); | |
3213 | libzcash::PaymentAddress zaddr; | |
3214 | if (!fromTaddr) { | |
3215 | CZCPaymentAddress address(fromaddress); | |
3216 | try { | |
3217 | zaddr = address.Get(); | |
3218 | } catch (std::runtime_error) { | |
3219 | // invalid | |
3220 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); | |
3221 | } | |
3222 | } | |
3223 | ||
dafb8161 | 3224 | // Check that we have the spending key |
fc72c078 S |
3225 | if (!fromTaddr) { |
3226 | if (!pwalletMain->HaveSpendingKey(zaddr)) { | |
3227 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found."); | |
3228 | } | |
3229 | } | |
3230 | ||
fc72c078 S |
3231 | Array outputs = params[1].get_array(); |
3232 | ||
3233 | if (outputs.size()==0) | |
3234 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amounts array is empty."); | |
3235 | ||
3236 | // Keep track of addresses to spot duplicates | |
3237 | set<std::string> setAddress; | |
3238 | ||
3239 | // Recipients | |
dafb8161 S |
3240 | std::vector<SendManyRecipient> taddrRecipients; |
3241 | std::vector<SendManyRecipient> zaddrRecipients; | |
fc72c078 S |
3242 | |
3243 | BOOST_FOREACH(Value& output, outputs) | |
3244 | { | |
3245 | if (output.type() != obj_type) | |
3246 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); | |
3247 | const Object& o = output.get_obj(); | |
3248 | ||
fc72c078 | 3249 | // sanity check, report error if unknown key-value pairs |
fc72c078 S |
3250 | for (const Pair& p : o) { |
3251 | std::string s = p.name_; | |
3252 | if (s != "address" && s != "amount" && s!="memo") | |
3253 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown key: ")+s); | |
3254 | } | |
3255 | ||
3256 | string address = find_value(o, "address").get_str(); | |
3257 | bool isZaddr = false; | |
3258 | CBitcoinAddress taddr(address); | |
3259 | if (!taddr.IsValid()) { | |
3260 | try { | |
3261 | CZCPaymentAddress zaddr(address); | |
3262 | zaddr.Get(); | |
3263 | isZaddr = true; | |
3264 | } catch (std::runtime_error) { | |
3265 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ")+address ); | |
3266 | } | |
3267 | } | |
3268 | ||
3269 | if (setAddress.count(address)) | |
3270 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+address); | |
3271 | setAddress.insert(address); | |
3272 | ||
3273 | Value memoValue = find_value(o, "memo"); | |
3274 | string memo; | |
3275 | if (!memoValue.is_null()) { | |
3276 | memo = memoValue.get_str(); | |
3277 | if (!isZaddr) { | |
3278 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo can not be used with a taddr. It can only be used with a zaddr."); | |
3279 | } else if (!IsHex(memo)) { | |
3280 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format."); | |
3281 | } | |
6114cfe7 | 3282 | if (memo.length() > ZC_MEMO_SIZE*2) { |
dafb8161 S |
3283 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE )); |
3284 | } | |
fc72c078 S |
3285 | } |
3286 | ||
fc72c078 S |
3287 | Value av = find_value(o, "amount"); |
3288 | CAmount nAmount = AmountFromValue( av ); | |
3289 | if (nAmount < 0) | |
3290 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amount must be positive"); | |
3291 | ||
dafb8161 S |
3292 | if (isZaddr) { |
3293 | zaddrRecipients.push_back( SendManyRecipient(address, nAmount, memo) ); | |
3294 | } else { | |
3295 | taddrRecipients.push_back( SendManyRecipient(address, nAmount, memo) ); | |
3296 | } | |
fc72c078 S |
3297 | } |
3298 | ||
3920292b S |
3299 | // Check the number of zaddr outputs does not exceed the limit. |
3300 | if (zaddrRecipients.size() > Z_SENDMANY_MAX_ZADDR_OUTPUTS) { | |
3301 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, too many zaddr outputs"); | |
3302 | } | |
3303 | ||
3304 | // As a sanity check, estimate and verify that the size of the transaction will be valid. | |
3305 | // Depending on the input notes, the actual tx size may turn out to be larger and perhaps invalid. | |
3306 | size_t txsize = 0; | |
3307 | CMutableTransaction mtx; | |
3308 | mtx.nVersion = 2; | |
3309 | for (int i = 0; i < zaddrRecipients.size(); i++) { | |
3310 | mtx.vjoinsplit.push_back(JSDescription()); | |
3311 | } | |
3312 | CTransaction tx(mtx); | |
3313 | txsize += tx.GetSerializeSize(SER_NETWORK, tx.nVersion); | |
3314 | if (fromTaddr) { | |
3315 | txsize += CTXIN_SPEND_DUST_SIZE; | |
3316 | txsize += CTXOUT_REGULAR_SIZE; // There will probably be taddr change | |
3317 | } | |
3318 | txsize += CTXOUT_REGULAR_SIZE * taddrRecipients.size(); | |
3319 | if (txsize > MAX_TX_SIZE) { | |
3320 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Too many outputs, size of raw transaction would be larger than limit of %d bytes", MAX_TX_SIZE )); | |
3321 | } | |
3322 | ||
fc72c078 S |
3323 | // Minimum confirmations |
3324 | int nMinDepth = 1; | |
12448b64 | 3325 | if (params.size() > 2) { |
fc72c078 | 3326 | nMinDepth = params[2].get_int(); |
12448b64 S |
3327 | } |
3328 | if (nMinDepth < 0) { | |
3329 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); | |
3330 | } | |
fc72c078 | 3331 | |
dafb8161 | 3332 | // Create operation and add to global queue |
fc72c078 | 3333 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); |
dafb8161 | 3334 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(fromaddress, taddrRecipients, zaddrRecipients, nMinDepth) ); |
fc72c078 S |
3335 | q->addOperation(operation); |
3336 | AsyncRPCOperationId operationId = operation->getId(); | |
3337 | return operationId; | |
fc72c078 | 3338 | } |
34f0001c S |
3339 | |
3340 | ||
3341 | Value z_listoperationids(const Array& params, bool fHelp) | |
3342 | { | |
3343 | if (!EnsureWalletIsAvailable(fHelp)) | |
3344 | return Value::null; | |
3345 | ||
3346 | if (fHelp || params.size() > 1) | |
3347 | throw runtime_error( | |
3348 | "z_listoperationids\n" | |
3349 | "\nReturns the list of operation ids currently known to the wallet.\n" | |
3350 | "\nArguments:\n" | |
3351 | "1. \"status\" (string, optional) Filter result by the operation's state state e.g. \"success\".\n" | |
3352 | "\nResult:\n" | |
3353 | "[ (json array of string)\n" | |
3354 | " \"operationid\" (string) an operation id belonging to the wallet\n" | |
3355 | " ,...\n" | |
3356 | "]\n" | |
3357 | "\nExamples:\n" | |
3358 | + HelpExampleCli("z_listoperationids", "") | |
3359 | + HelpExampleRpc("z_listoperationids", "") | |
3360 | ); | |
3361 | ||
3362 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3363 | ||
3364 | std::string filter; | |
3365 | bool useFilter = false; | |
3366 | if (params.size()==1) { | |
3367 | filter = params[0].get_str(); | |
3368 | useFilter = true; | |
3369 | } | |
3370 | ||
3371 | Array ret; | |
3372 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); | |
3373 | std::vector<AsyncRPCOperationId> ids = q->getAllOperationIds(); | |
3374 | for (auto id : ids) { | |
3375 | std::shared_ptr<AsyncRPCOperation> operation = q->getOperationForId(id); | |
3376 | if (!operation) { | |
3377 | continue; | |
3378 | } | |
3379 | std::string state = operation->getStateAsString(); | |
3380 | if (useFilter && filter.compare(state)!=0) | |
3381 | continue; | |
3382 | ret.push_back(id); | |
3383 | } | |
3384 | ||
3385 | return ret; | |
3386 | } | |
3387 |