]>
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" |
be126699 | 8 | #include "consensus/upgrades.h" |
ae775b5b | 9 | #include "core_io.h" |
e3bc5698 | 10 | #include "init.h" |
8a893c94 | 11 | #include "main.h" |
51ed9ec9 BD |
12 | #include "net.h" |
13 | #include "netbase.h" | |
8a893c94 | 14 | #include "rpcserver.h" |
14f888ca | 15 | #include "timedata.h" |
51ed9ec9 | 16 | #include "util.h" |
b93173de | 17 | #include "utilmoneystr.h" |
51ed9ec9 BD |
18 | #include "wallet.h" |
19 | #include "walletdb.h" | |
8cb25088 | 20 | #include "primitives/transaction.h" |
6962bb3d | 21 | #include "zcbenchmarks.h" |
6aae9d1a | 22 | #include "script/interpreter.h" |
51ed9ec9 | 23 | |
fc72c078 S |
24 | #include "utiltime.h" |
25 | #include "asyncrpcoperation.h" | |
ed21d5bd | 26 | #include "asyncrpcqueue.h" |
6e9c7629 | 27 | #include "wallet/asyncrpcoperation_mergetoaddress.h" |
fc72c078 | 28 | #include "wallet/asyncrpcoperation_sendmany.h" |
06c19063 | 29 | #include "wallet/asyncrpcoperation_shieldcoinbase.h" |
fc72c078 | 30 | |
320f2cc7 SB |
31 | #include "sodium.h" |
32 | ||
51ed9ec9 BD |
33 | #include <stdint.h> |
34 | ||
35 | #include <boost/assign/list_of.hpp> | |
25cf6f3d | 36 | |
a10a6e2a | 37 | #include <univalue.h> |
e3bc5698 | 38 | |
bcbde86a S |
39 | #include <numeric> |
40 | ||
e3bc5698 JG |
41 | using namespace std; |
42 | ||
2dc35992 SB |
43 | using namespace libzcash; |
44 | ||
0d37ae3a | 45 | extern UniValue TxJoinSplitToJSON(const CTransaction& tx); |
f7cfb52d | 46 | |
51ed9ec9 | 47 | int64_t nWalletUnlockTime; |
e3bc5698 JG |
48 | static CCriticalSection cs_nWalletUnlockTime; |
49 | ||
c1eae280 | 50 | // Private method: |
0d37ae3a | 51 | UniValue z_getoperationstatus_IMPL(const UniValue&, bool); |
c1eae280 | 52 | |
bdab0cf5 | 53 | std::string HelpRequiringPassphrase() |
e3bc5698 | 54 | { |
b0730874 | 55 | return pwalletMain && pwalletMain->IsCrypted() |
a6099ef3 | 56 | ? "\nRequires wallet passphrase to be set with walletpassphrase call." |
e3bc5698 JG |
57 | : ""; |
58 | } | |
59 | ||
b9fb692d JS |
60 | bool EnsureWalletIsAvailable(bool avoidException) |
61 | { | |
62 | if (!pwalletMain) | |
63 | { | |
64 | if (!avoidException) | |
65 | throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); | |
66 | else | |
67 | return false; | |
68 | } | |
69 | return true; | |
70 | } | |
71 | ||
bdab0cf5 | 72 | void EnsureWalletIsUnlocked() |
e3bc5698 JG |
73 | { |
74 | if (pwalletMain->IsLocked()) | |
738835d7 | 75 | throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); |
e3bc5698 JG |
76 | } |
77 | ||
d014114d | 78 | void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) |
e3bc5698 JG |
79 | { |
80 | int confirms = wtx.GetDepthInMainChain(); | |
81 | entry.push_back(Pair("confirmations", confirms)); | |
e07c8e91 LD |
82 | if (wtx.IsCoinBase()) |
83 | entry.push_back(Pair("generated", true)); | |
2b72d46f | 84 | if (confirms > 0) |
e3bc5698 JG |
85 | { |
86 | entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); | |
87 | entry.push_back(Pair("blockindex", wtx.nIndex)); | |
209377a7 | 88 | entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); |
9bb37bf0 | 89 | entry.push_back(Pair("expiryheight", (int64_t)wtx.nExpiryHeight)); |
e3bc5698 | 90 | } |
805344dc | 91 | uint256 hash = wtx.GetHash(); |
731b89b8 | 92 | entry.push_back(Pair("txid", hash.GetHex())); |
38fc4b70 | 93 | UniValue conflicts(UniValue::VARR); |
731b89b8 GA |
94 | BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts()) |
95 | conflicts.push_back(conflict.GetHex()); | |
96 | entry.push_back(Pair("walletconflicts", conflicts)); | |
d56e30ca | 97 | entry.push_back(Pair("time", wtx.GetTxTime())); |
4b61a6a4 | 98 | entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); |
e3bc5698 JG |
99 | BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) |
100 | entry.push_back(Pair(item.first, item.second)); | |
f7cfb52d S |
101 | |
102 | entry.push_back(Pair("vjoinsplit", TxJoinSplitToJSON(wtx))); | |
e3bc5698 JG |
103 | } |
104 | ||
d014114d | 105 | string AccountFromValue(const UniValue& value) |
e3bc5698 JG |
106 | { |
107 | string strAccount = value.get_str(); | |
3c31eb24 | 108 | if (strAccount != "") |
7b3351ff | 109 | throw JSONRPCError(RPC_WALLET_ACCOUNTS_UNSUPPORTED, "Accounts are unsupported"); |
e3bc5698 JG |
110 | return strAccount; |
111 | } | |
112 | ||
d014114d | 113 | UniValue getnewaddress(const UniValue& params, bool fHelp) |
e3bc5698 | 114 | { |
b9fb692d | 115 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 116 | return NullUniValue; |
70454796 | 117 | |
e3bc5698 JG |
118 | if (fHelp || params.size() > 1) |
119 | throw runtime_error( | |
a6099ef3 | 120 | "getnewaddress ( \"account\" )\n" |
58c4c0bb | 121 | "\nReturns a new Zcash address for receiving payments.\n" |
a6099ef3 | 122 | "\nArguments:\n" |
3c31eb24 | 123 | "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 | 124 | "\nResult:\n" |
d2c1e4a8 | 125 | "\"zcashaddress\" (string) The new Zcash address\n" |
a6099ef3 | 126 | "\nExamples:\n" |
127 | + HelpExampleCli("getnewaddress", "") | |
7b782f5b | 128 | + HelpExampleRpc("getnewaddress", "") |
a6099ef3 | 129 | ); |
e3bc5698 | 130 | |
4401b2d7 EL |
131 | LOCK2(cs_main, pwalletMain->cs_wallet); |
132 | ||
e3bc5698 JG |
133 | // Parse the account first so we don't generate a key if there's an error |
134 | string strAccount; | |
135 | if (params.size() > 0) | |
136 | strAccount = AccountFromValue(params[0]); | |
137 | ||
138 | if (!pwalletMain->IsLocked()) | |
139 | pwalletMain->TopUpKeyPool(); | |
140 | ||
141 | // Generate a new key that is added to wallet | |
142 | CPubKey newKey; | |
71ac5052 | 143 | if (!pwalletMain->GetKeyFromPool(newKey)) |
738835d7 | 144 | throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); |
e3bc5698 JG |
145 | CKeyID keyID = newKey.GetID(); |
146 | ||
a41d5fe0 | 147 | pwalletMain->SetAddressBook(keyID, strAccount, "receive"); |
e3bc5698 JG |
148 | |
149 | return CBitcoinAddress(keyID).ToString(); | |
150 | } | |
151 | ||
152 | ||
153 | CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) | |
154 | { | |
155 | CWalletDB walletdb(pwalletMain->strWalletFile); | |
156 | ||
157 | CAccount account; | |
158 | walletdb.ReadAccount(strAccount, account); | |
159 | ||
160 | bool bKeyUsed = false; | |
161 | ||
162 | // Check if the current key has been used | |
163 | if (account.vchPubKey.IsValid()) | |
164 | { | |
0be990ba | 165 | CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID()); |
e3bc5698 JG |
166 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); |
167 | it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid(); | |
168 | ++it) | |
169 | { | |
170 | const CWalletTx& wtx = (*it).second; | |
171 | BOOST_FOREACH(const CTxOut& txout, wtx.vout) | |
172 | if (txout.scriptPubKey == scriptPubKey) | |
173 | bKeyUsed = true; | |
174 | } | |
175 | } | |
176 | ||
177 | // Generate a new key | |
178 | if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed) | |
179 | { | |
71ac5052 | 180 | if (!pwalletMain->GetKeyFromPool(account.vchPubKey)) |
738835d7 | 181 | throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); |
e3bc5698 | 182 | |
a41d5fe0 | 183 | pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); |
e3bc5698 JG |
184 | walletdb.WriteAccount(strAccount, account); |
185 | } | |
186 | ||
187 | return CBitcoinAddress(account.vchPubKey.GetID()); | |
188 | } | |
189 | ||
d014114d | 190 | UniValue getaccountaddress(const UniValue& params, bool fHelp) |
e3bc5698 | 191 | { |
b9fb692d | 192 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 193 | return NullUniValue; |
70454796 | 194 | |
e3bc5698 JG |
195 | if (fHelp || params.size() != 1) |
196 | throw runtime_error( | |
a6099ef3 | 197 | "getaccountaddress \"account\"\n" |
58c4c0bb | 198 | "\nDEPRECATED. Returns the current Zcash address for receiving payments to this account.\n" |
a6099ef3 | 199 | "\nArguments:\n" |
3c31eb24 | 200 | "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 | 201 | "\nResult:\n" |
d2c1e4a8 | 202 | "\"zcashaddress\" (string) The account Zcash address\n" |
a6099ef3 | 203 | "\nExamples:\n" |
204 | + HelpExampleCli("getaccountaddress", "") | |
205 | + HelpExampleCli("getaccountaddress", "\"\"") | |
206 | + HelpExampleCli("getaccountaddress", "\"myaccount\"") | |
207 | + HelpExampleRpc("getaccountaddress", "\"myaccount\"") | |
208 | ); | |
e3bc5698 | 209 | |
4401b2d7 EL |
210 | LOCK2(cs_main, pwalletMain->cs_wallet); |
211 | ||
e3bc5698 JG |
212 | // Parse the account first so we don't generate a key if there's an error |
213 | string strAccount = AccountFromValue(params[0]); | |
214 | ||
d014114d | 215 | UniValue ret(UniValue::VSTR); |
e3bc5698 JG |
216 | |
217 | ret = GetAccountAddress(strAccount).ToString(); | |
e3bc5698 JG |
218 | return ret; |
219 | } | |
220 | ||
221 | ||
d014114d | 222 | UniValue getrawchangeaddress(const UniValue& params, bool fHelp) |
e5e9904c | 223 | { |
b9fb692d | 224 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 225 | return NullUniValue; |
70454796 | 226 | |
e5e9904c JG |
227 | if (fHelp || params.size() > 1) |
228 | throw runtime_error( | |
229 | "getrawchangeaddress\n" | |
58c4c0bb | 230 | "\nReturns a new Zcash address, for receiving change.\n" |
a6099ef3 | 231 | "This is for use with raw transactions, NOT normal use.\n" |
232 | "\nResult:\n" | |
233 | "\"address\" (string) The address\n" | |
234 | "\nExamples:\n" | |
235 | + HelpExampleCli("getrawchangeaddress", "") | |
236 | + HelpExampleRpc("getrawchangeaddress", "") | |
237 | ); | |
e5e9904c | 238 | |
4401b2d7 EL |
239 | LOCK2(cs_main, pwalletMain->cs_wallet); |
240 | ||
e5e9904c JG |
241 | if (!pwalletMain->IsLocked()) |
242 | pwalletMain->TopUpKeyPool(); | |
243 | ||
244 | CReserveKey reservekey(pwalletMain); | |
245 | CPubKey vchPubKey; | |
246 | if (!reservekey.GetReservedKey(vchPubKey)) | |
6c37f7fd | 247 | throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); |
e5e9904c JG |
248 | |
249 | reservekey.KeepKey(); | |
250 | ||
251 | CKeyID keyID = vchPubKey.GetID(); | |
252 | ||
253 | return CBitcoinAddress(keyID).ToString(); | |
254 | } | |
255 | ||
e3bc5698 | 256 | |
d014114d | 257 | UniValue setaccount(const UniValue& params, bool fHelp) |
e3bc5698 | 258 | { |
b9fb692d | 259 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 260 | return NullUniValue; |
70454796 | 261 | |
e3bc5698 JG |
262 | if (fHelp || params.size() < 1 || params.size() > 2) |
263 | throw runtime_error( | |
58c4c0bb | 264 | "setaccount \"zcashaddress\" \"account\"\n" |
7b782f5b | 265 | "\nDEPRECATED. Sets the account associated with the given address.\n" |
a6099ef3 | 266 | "\nArguments:\n" |
d2c1e4a8 | 267 | "1. \"zcashaddress\" (string, required) The Zcash address to be associated with an account.\n" |
3c31eb24 | 268 | "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 | 269 | "\nExamples:\n" |
70454796 JG |
270 | + HelpExampleCli("setaccount", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" \"tabby\"") |
271 | + HelpExampleRpc("setaccount", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", \"tabby\"") | |
a6099ef3 | 272 | ); |
e3bc5698 | 273 | |
4401b2d7 EL |
274 | LOCK2(cs_main, pwalletMain->cs_wallet); |
275 | ||
e3bc5698 JG |
276 | CBitcoinAddress address(params[0].get_str()); |
277 | if (!address.IsValid()) | |
58c4c0bb | 278 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); |
e3bc5698 | 279 | |
e3bc5698 JG |
280 | string strAccount; |
281 | if (params.size() > 1) | |
282 | strAccount = AccountFromValue(params[1]); | |
283 | ||
31d6390f ES |
284 | // Only add the account if the address is yours. |
285 | if (IsMine(*pwalletMain, address.Get())) | |
e3bc5698 | 286 | { |
31d6390f ES |
287 | // Detect when changing the account of an address that is the 'unused current key' of another account: |
288 | if (pwalletMain->mapAddressBook.count(address.Get())) | |
289 | { | |
290 | string strOldAccount = pwalletMain->mapAddressBook[address.Get()].name; | |
291 | if (address == GetAccountAddress(strOldAccount)) | |
292 | GetAccountAddress(strOldAccount, true); | |
293 | } | |
294 | pwalletMain->SetAddressBook(address.Get(), strAccount, "receive"); | |
e3bc5698 | 295 | } |
31d6390f ES |
296 | else |
297 | throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); | |
e3bc5698 | 298 | |
ed21d5bd | 299 | return NullUniValue; |
e3bc5698 JG |
300 | } |
301 | ||
302 | ||
d014114d | 303 | UniValue getaccount(const UniValue& params, bool fHelp) |
e3bc5698 | 304 | { |
b9fb692d | 305 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 306 | return NullUniValue; |
70454796 | 307 | |
e3bc5698 JG |
308 | if (fHelp || params.size() != 1) |
309 | throw runtime_error( | |
58c4c0bb | 310 | "getaccount \"zcashaddress\"\n" |
7b782f5b | 311 | "\nDEPRECATED. Returns the account associated with the given address.\n" |
a6099ef3 | 312 | "\nArguments:\n" |
d2c1e4a8 | 313 | "1. \"zcashaddress\" (string, required) The Zcash address for account lookup.\n" |
a6099ef3 | 314 | "\nResult:\n" |
315 | "\"accountname\" (string) the account address\n" | |
316 | "\nExamples:\n" | |
70454796 JG |
317 | + HelpExampleCli("getaccount", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\"") |
318 | + HelpExampleRpc("getaccount", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\"") | |
a6099ef3 | 319 | ); |
e3bc5698 | 320 | |
4401b2d7 EL |
321 | LOCK2(cs_main, pwalletMain->cs_wallet); |
322 | ||
e3bc5698 JG |
323 | CBitcoinAddress address(params[0].get_str()); |
324 | if (!address.IsValid()) | |
58c4c0bb | 325 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); |
e3bc5698 JG |
326 | |
327 | string strAccount; | |
61885513 GA |
328 | map<CTxDestination, CAddressBookData>::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); |
329 | if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.name.empty()) | |
330 | strAccount = (*mi).second.name; | |
e3bc5698 JG |
331 | return strAccount; |
332 | } | |
333 | ||
334 | ||
d014114d | 335 | UniValue getaddressesbyaccount(const UniValue& params, bool fHelp) |
e3bc5698 | 336 | { |
b9fb692d | 337 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 338 | return NullUniValue; |
70454796 | 339 | |
e3bc5698 JG |
340 | if (fHelp || params.size() != 1) |
341 | throw runtime_error( | |
a6099ef3 | 342 | "getaddressesbyaccount \"account\"\n" |
7b782f5b | 343 | "\nDEPRECATED. Returns the list of addresses for the given account.\n" |
a6099ef3 | 344 | "\nArguments:\n" |
3c31eb24 | 345 | "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 | 346 | "\nResult:\n" |
347 | "[ (json array of string)\n" | |
d2c1e4a8 | 348 | " \"zcashaddress\" (string) a Zcash address associated with the given account\n" |
a6099ef3 | 349 | " ,...\n" |
350 | "]\n" | |
351 | "\nExamples:\n" | |
352 | + HelpExampleCli("getaddressesbyaccount", "\"tabby\"") | |
353 | + HelpExampleRpc("getaddressesbyaccount", "\"tabby\"") | |
354 | ); | |
e3bc5698 | 355 | |
4401b2d7 EL |
356 | LOCK2(cs_main, pwalletMain->cs_wallet); |
357 | ||
e3bc5698 JG |
358 | string strAccount = AccountFromValue(params[0]); |
359 | ||
360 | // Find all addresses that have the given account | |
38fc4b70 | 361 | UniValue ret(UniValue::VARR); |
61885513 | 362 | BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) |
e3bc5698 JG |
363 | { |
364 | const CBitcoinAddress& address = item.first; | |
61885513 | 365 | const string& strName = item.second.name; |
e3bc5698 JG |
366 | if (strName == strAccount) |
367 | ret.push_back(address.ToString()); | |
368 | } | |
369 | return ret; | |
370 | } | |
371 | ||
292623ad | 372 | static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew) |
b93173de | 373 | { |
25cf6f3d PK |
374 | CAmount curBalance = pwalletMain->GetBalance(); |
375 | ||
b93173de PJ |
376 | // Check amount |
377 | if (nValue <= 0) | |
4be639ea | 378 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); |
b93173de | 379 | |
25cf6f3d | 380 | if (nValue > curBalance) |
b93173de PJ |
381 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); |
382 | ||
58c4c0bb | 383 | // Parse Zcash address |
b93173de PJ |
384 | CScript scriptPubKey = GetScriptForDestination(address); |
385 | ||
386 | // Create and send the transaction | |
387 | CReserveKey reservekey(pwalletMain); | |
388 | CAmount nFeeRequired; | |
25cf6f3d | 389 | std::string strError; |
292623ad CL |
390 | vector<CRecipient> vecSend; |
391 | int nChangePosRet = -1; | |
392 | CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; | |
393 | vecSend.push_back(recipient); | |
394 | if (!pwalletMain->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { | |
395 | if (!fSubtractFeeFromAmount && nValue + nFeeRequired > pwalletMain->GetBalance()) | |
396 | 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)); | |
397 | throw JSONRPCError(RPC_WALLET_ERROR, strError); | |
b93173de PJ |
398 | } |
399 | if (!pwalletMain->CommitTransaction(wtxNew, reservekey)) | |
400 | 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 |
401 | } |
402 | ||
d014114d | 403 | UniValue sendtoaddress(const UniValue& params, bool fHelp) |
e3bc5698 | 404 | { |
b9fb692d | 405 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 406 | return NullUniValue; |
70454796 | 407 | |
292623ad | 408 | if (fHelp || params.size() < 2 || params.size() > 5) |
e3bc5698 | 409 | throw runtime_error( |
58c4c0bb | 410 | "sendtoaddress \"zcashaddress\" amount ( \"comment\" \"comment-to\" subtractfeefromamount )\n" |
e743678d | 411 | "\nSend an amount to a given address. The amount is a real and is rounded to the nearest 0.00000001\n" |
a6099ef3 | 412 | + HelpRequiringPassphrase() + |
413 | "\nArguments:\n" | |
58c4c0bb | 414 | "1. \"zcashaddress\" (string, required) The zcash address to send to.\n" |
091b2116 | 415 | "2. \"amount\" (numeric, required) The amount in " + CURRENCY_UNIT + " to send. eg 0.1\n" |
a6099ef3 | 416 | "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" |
417 | " This is not part of the transaction, just kept in your wallet.\n" | |
418 | "4. \"comment-to\" (string, optional) A comment to store the name of the person or organization \n" | |
419 | " to which you're sending the transaction. This is not part of the \n" | |
420 | " transaction, just kept in your wallet.\n" | |
292623ad | 421 | "5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n" |
c1652849 | 422 | " The recipient will receive less Zcash than you enter in the amount field.\n" |
a6099ef3 | 423 | "\nResult:\n" |
b5ef85c7 | 424 | "\"transactionid\" (string) The transaction id.\n" |
a6099ef3 | 425 | "\nExamples:\n" |
70454796 JG |
426 | + HelpExampleCli("sendtoaddress", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1") |
427 | + HelpExampleCli("sendtoaddress", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"donation\" \"seans outpost\"") | |
428 | + HelpExampleCli("sendtoaddress", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.1 \"\" \"\" true") | |
429 | + HelpExampleRpc("sendtoaddress", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.1, \"donation\", \"seans outpost\"") | |
a6099ef3 | 430 | ); |
e3bc5698 | 431 | |
4401b2d7 EL |
432 | LOCK2(cs_main, pwalletMain->cs_wallet); |
433 | ||
e3bc5698 JG |
434 | CBitcoinAddress address(params[0].get_str()); |
435 | if (!address.IsValid()) | |
58c4c0bb | 436 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); |
e3bc5698 JG |
437 | |
438 | // Amount | |
a372168e | 439 | CAmount nAmount = AmountFromValue(params[1]); |
e76a3849 WL |
440 | if (nAmount <= 0) |
441 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); | |
e3bc5698 JG |
442 | |
443 | // Wallet comments | |
444 | CWalletTx wtx; | |
ed21d5bd | 445 | if (params.size() > 2 && !params[2].isNull() && !params[2].get_str().empty()) |
e3bc5698 | 446 | wtx.mapValue["comment"] = params[2].get_str(); |
ed21d5bd | 447 | if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) |
e3bc5698 JG |
448 | wtx.mapValue["to"] = params[3].get_str(); |
449 | ||
292623ad CL |
450 | bool fSubtractFeeFromAmount = false; |
451 | if (params.size() > 4) | |
452 | fSubtractFeeFromAmount = params[4].get_bool(); | |
453 | ||
f914c7a1 | 454 | EnsureWalletIsUnlocked(); |
e3bc5698 | 455 | |
292623ad | 456 | SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx); |
e3bc5698 | 457 | |
805344dc | 458 | return wtx.GetHash().GetHex(); |
e3bc5698 JG |
459 | } |
460 | ||
d014114d | 461 | UniValue listaddressgroupings(const UniValue& params, bool fHelp) |
22dfd735 | 462 | { |
b9fb692d | 463 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 464 | return NullUniValue; |
70454796 | 465 | |
22dfd735 | 466 | if (fHelp) |
b1093efa GM |
467 | throw runtime_error( |
468 | "listaddressgroupings\n" | |
a6099ef3 | 469 | "\nLists groups of addresses which have had their common ownership\n" |
b1093efa | 470 | "made public by common use as inputs or as the resulting change\n" |
a6099ef3 | 471 | "in past transactions\n" |
472 | "\nResult:\n" | |
473 | "[\n" | |
474 | " [\n" | |
475 | " [\n" | |
58c4c0bb | 476 | " \"zcashaddress\", (string) The zcash address\n" |
091b2116 | 477 | " amount, (numeric) The amount in " + CURRENCY_UNIT + "\n" |
7b782f5b | 478 | " \"account\" (string, optional) The account (DEPRECATED)\n" |
a6099ef3 | 479 | " ]\n" |
480 | " ,...\n" | |
481 | " ]\n" | |
482 | " ,...\n" | |
483 | "]\n" | |
484 | "\nExamples:\n" | |
485 | + HelpExampleCli("listaddressgroupings", "") | |
486 | + HelpExampleRpc("listaddressgroupings", "") | |
487 | ); | |
22dfd735 | 488 | |
4401b2d7 EL |
489 | LOCK2(cs_main, pwalletMain->cs_wallet); |
490 | ||
38fc4b70 | 491 | UniValue jsonGroupings(UniValue::VARR); |
a372168e | 492 | map<CTxDestination, CAmount> balances = pwalletMain->GetAddressBalances(); |
b1093efa | 493 | BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings()) |
22dfd735 | 494 | { |
38fc4b70 | 495 | UniValue jsonGrouping(UniValue::VARR); |
b1093efa | 496 | BOOST_FOREACH(CTxDestination address, grouping) |
22dfd735 | 497 | { |
38fc4b70 | 498 | UniValue addressInfo(UniValue::VARR); |
b1093efa | 499 | addressInfo.push_back(CBitcoinAddress(address).ToString()); |
22dfd735 | 500 | addressInfo.push_back(ValueFromAmount(balances[address])); |
501 | { | |
22dfd735 | 502 | if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end()) |
61885513 | 503 | addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name); |
22dfd735 | 504 | } |
505 | jsonGrouping.push_back(addressInfo); | |
506 | } | |
507 | jsonGroupings.push_back(jsonGrouping); | |
508 | } | |
509 | return jsonGroupings; | |
510 | } | |
511 | ||
d014114d | 512 | UniValue signmessage(const UniValue& params, bool fHelp) |
e3bc5698 | 513 | { |
b9fb692d | 514 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 515 | return NullUniValue; |
70454796 | 516 | |
e3bc5698 JG |
517 | if (fHelp || params.size() != 2) |
518 | throw runtime_error( | |
58c4c0bb | 519 | "signmessage \"zcashaddress\" \"message\"\n" |
a6099ef3 | 520 | "\nSign a message with the private key of an address" |
521 | + HelpRequiringPassphrase() + "\n" | |
522 | "\nArguments:\n" | |
d2c1e4a8 | 523 | "1. \"zcashaddress\" (string, required) The Zcash address to use for the private key.\n" |
a6099ef3 | 524 | "2. \"message\" (string, required) The message to create a signature of.\n" |
525 | "\nResult:\n" | |
526 | "\"signature\" (string) The signature of the message encoded in base 64\n" | |
527 | "\nExamples:\n" | |
528 | "\nUnlock the wallet for 30 seconds\n" | |
529 | + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + | |
530 | "\nCreate the signature\n" | |
70454796 | 531 | + HelpExampleCli("signmessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" \"my message\"") + |
a6099ef3 | 532 | "\nVerify the signature\n" |
70454796 | 533 | + HelpExampleCli("verifymessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" \"signature\" \"my message\"") + |
a6099ef3 | 534 | "\nAs json rpc\n" |
70454796 | 535 | + HelpExampleRpc("signmessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", \"my message\"") |
a6099ef3 | 536 | ); |
e3bc5698 | 537 | |
4401b2d7 EL |
538 | LOCK2(cs_main, pwalletMain->cs_wallet); |
539 | ||
e3bc5698 JG |
540 | EnsureWalletIsUnlocked(); |
541 | ||
542 | string strAddress = params[0].get_str(); | |
543 | string strMessage = params[1].get_str(); | |
544 | ||
545 | CBitcoinAddress addr(strAddress); | |
546 | if (!addr.IsValid()) | |
738835d7 | 547 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); |
e3bc5698 JG |
548 | |
549 | CKeyID keyID; | |
550 | if (!addr.GetKeyID(keyID)) | |
738835d7 | 551 | throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); |
e3bc5698 JG |
552 | |
553 | CKey key; | |
554 | if (!pwalletMain->GetKey(keyID, key)) | |
738835d7 | 555 | throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); |
e3bc5698 | 556 | |
8980a509 | 557 | CHashWriter ss(SER_GETHASH, 0); |
e3bc5698 JG |
558 | ss << strMessageMagic; |
559 | ss << strMessage; | |
560 | ||
561 | vector<unsigned char> vchSig; | |
8980a509 | 562 | if (!key.SignCompact(ss.GetHash(), vchSig)) |
738835d7 | 563 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); |
e3bc5698 JG |
564 | |
565 | return EncodeBase64(&vchSig[0], vchSig.size()); | |
566 | } | |
567 | ||
d014114d | 568 | UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) |
e3bc5698 | 569 | { |
b9fb692d | 570 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 571 | return NullUniValue; |
70454796 | 572 | |
e3bc5698 JG |
573 | if (fHelp || params.size() < 1 || params.size() > 2) |
574 | throw runtime_error( | |
58c4c0bb | 575 | "getreceivedbyaddress \"zcashaddress\" ( minconf )\n" |
c1652849 | 576 | "\nReturns the total amount received by the given Zcash address in transactions with at least minconf confirmations.\n" |
a6099ef3 | 577 | "\nArguments:\n" |
d2c1e4a8 | 578 | "1. \"zcashaddress\" (string, required) The Zcash address for transactions.\n" |
a6099ef3 | 579 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" |
580 | "\nResult:\n" | |
091b2116 | 581 | "amount (numeric) The total amount in " + CURRENCY_UNIT + " received at this address.\n" |
a6099ef3 | 582 | "\nExamples:\n" |
583 | "\nThe amount from transactions with at least 1 confirmation\n" | |
70454796 | 584 | + HelpExampleCli("getreceivedbyaddress", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\"") + |
a6099ef3 | 585 | "\nThe amount including unconfirmed transactions, zero confirmations\n" |
70454796 | 586 | + HelpExampleCli("getreceivedbyaddress", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" 0") + |
a6099ef3 | 587 | "\nThe amount with at least 6 confirmation, very safe\n" |
70454796 | 588 | + HelpExampleCli("getreceivedbyaddress", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" 6") + |
a6099ef3 | 589 | "\nAs a json rpc call\n" |
70454796 | 590 | + HelpExampleRpc("getreceivedbyaddress", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", 6") |
a6099ef3 | 591 | ); |
e3bc5698 | 592 | |
4401b2d7 EL |
593 | LOCK2(cs_main, pwalletMain->cs_wallet); |
594 | ||
e3bc5698 JG |
595 | // Bitcoin address |
596 | CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); | |
e3bc5698 | 597 | if (!address.IsValid()) |
58c4c0bb | 598 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); |
0be990ba | 599 | CScript scriptPubKey = GetScriptForDestination(address.Get()); |
e3bc5698 JG |
600 | if (!IsMine(*pwalletMain,scriptPubKey)) |
601 | return (double)0.0; | |
602 | ||
603 | // Minimum confirmations | |
604 | int nMinDepth = 1; | |
605 | if (params.size() > 1) | |
606 | nMinDepth = params[1].get_int(); | |
607 | ||
608 | // Tally | |
a372168e | 609 | CAmount nAmount = 0; |
e3bc5698 JG |
610 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) |
611 | { | |
612 | const CWalletTx& wtx = (*it).second; | |
75a4d512 | 613 | if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) |
e3bc5698 JG |
614 | continue; |
615 | ||
616 | BOOST_FOREACH(const CTxOut& txout, wtx.vout) | |
617 | if (txout.scriptPubKey == scriptPubKey) | |
618 | if (wtx.GetDepthInMainChain() >= nMinDepth) | |
619 | nAmount += txout.nValue; | |
620 | } | |
621 | ||
622 | return ValueFromAmount(nAmount); | |
623 | } | |
624 | ||
625 | ||
d014114d | 626 | UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) |
e3bc5698 | 627 | { |
b9fb692d | 628 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 629 | return NullUniValue; |
70454796 | 630 | |
e3bc5698 JG |
631 | if (fHelp || params.size() < 1 || params.size() > 2) |
632 | throw runtime_error( | |
a6099ef3 | 633 | "getreceivedbyaccount \"account\" ( minconf )\n" |
7b782f5b | 634 | "\nDEPRECATED. Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.\n" |
a6099ef3 | 635 | "\nArguments:\n" |
3c31eb24 | 636 | "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 | 637 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" |
638 | "\nResult:\n" | |
091b2116 | 639 | "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" |
a6099ef3 | 640 | "\nExamples:\n" |
641 | "\nAmount received by the default account with at least 1 confirmation\n" | |
642 | + HelpExampleCli("getreceivedbyaccount", "\"\"") + | |
643 | "\nAmount received at the tabby account including unconfirmed amounts with zero confirmations\n" | |
644 | + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 0") + | |
645 | "\nThe amount with at least 6 confirmation, very safe\n" | |
646 | + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 6") + | |
647 | "\nAs a json rpc call\n" | |
648 | + HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6") | |
649 | ); | |
e3bc5698 | 650 | |
4401b2d7 EL |
651 | LOCK2(cs_main, pwalletMain->cs_wallet); |
652 | ||
e3bc5698 JG |
653 | // Minimum confirmations |
654 | int nMinDepth = 1; | |
655 | if (params.size() > 1) | |
656 | nMinDepth = params[1].get_int(); | |
657 | ||
658 | // Get the set of pub keys assigned to account | |
659 | string strAccount = AccountFromValue(params[0]); | |
3624356e | 660 | set<CTxDestination> setAddress = pwalletMain->GetAccountAddresses(strAccount); |
e3bc5698 JG |
661 | |
662 | // Tally | |
a372168e | 663 | CAmount nAmount = 0; |
e3bc5698 JG |
664 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) |
665 | { | |
666 | const CWalletTx& wtx = (*it).second; | |
75a4d512 | 667 | if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) |
e3bc5698 JG |
668 | continue; |
669 | ||
670 | BOOST_FOREACH(const CTxOut& txout, wtx.vout) | |
671 | { | |
672 | CTxDestination address; | |
673 | if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) | |
674 | if (wtx.GetDepthInMainChain() >= nMinDepth) | |
675 | nAmount += txout.nValue; | |
676 | } | |
677 | } | |
678 | ||
679 | return (double)nAmount / (double)COIN; | |
680 | } | |
681 | ||
682 | ||
a372168e | 683 | CAmount GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter) |
e3bc5698 | 684 | { |
a372168e | 685 | CAmount nBalance = 0; |
e3bc5698 JG |
686 | |
687 | // Tally wallet transactions | |
688 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) | |
689 | { | |
690 | const CWalletTx& wtx = (*it).second; | |
75a4d512 | 691 | if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) |
e3bc5698 JG |
692 | continue; |
693 | ||
a372168e | 694 | CAmount nReceived, nSent, nFee; |
d4640d7d | 695 | wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); |
e3bc5698 JG |
696 | |
697 | if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) | |
698 | nBalance += nReceived; | |
e07c8e91 | 699 | nBalance -= nSent + nFee; |
e3bc5698 JG |
700 | } |
701 | ||
702 | // Tally internal accounting entries | |
703 | nBalance += walletdb.GetAccountCreditDebit(strAccount); | |
704 | ||
705 | return nBalance; | |
706 | } | |
707 | ||
a372168e | 708 | CAmount GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter) |
e3bc5698 JG |
709 | { |
710 | CWalletDB walletdb(pwalletMain->strWalletFile); | |
d4640d7d | 711 | return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); |
e3bc5698 JG |
712 | } |
713 | ||
714 | ||
d014114d | 715 | UniValue getbalance(const UniValue& params, bool fHelp) |
e3bc5698 | 716 | { |
b9fb692d | 717 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 718 | return NullUniValue; |
70454796 | 719 | |
d4640d7d | 720 | if (fHelp || params.size() > 3) |
e3bc5698 | 721 | throw runtime_error( |
d4640d7d | 722 | "getbalance ( \"account\" minconf includeWatchonly )\n" |
3c31eb24 | 723 | "\nReturns the server's total available balance.\n" |
a6099ef3 | 724 | "\nArguments:\n" |
715e5bbe | 725 | "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 | 726 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" |
d4640d7d | 727 | "3. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n" |
a6099ef3 | 728 | "\nResult:\n" |
091b2116 | 729 | "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this account.\n" |
a6099ef3 | 730 | "\nExamples:\n" |
7b782f5b | 731 | "\nThe total amount in the wallet\n" |
a6099ef3 | 732 | + HelpExampleCli("getbalance", "") + |
7b782f5b | 733 | "\nThe total amount in the wallet at least 5 blocks confirmed\n" |
3cf1f436 | 734 | + HelpExampleCli("getbalance", "\"*\" 6") + |
a6099ef3 | 735 | "\nAs a json rpc call\n" |
7b782f5b | 736 | + HelpExampleRpc("getbalance", "\"*\", 6") |
a6099ef3 | 737 | ); |
e3bc5698 | 738 | |
4401b2d7 EL |
739 | LOCK2(cs_main, pwalletMain->cs_wallet); |
740 | ||
e3bc5698 JG |
741 | if (params.size() == 0) |
742 | return ValueFromAmount(pwalletMain->GetBalance()); | |
743 | ||
744 | int nMinDepth = 1; | |
745 | if (params.size() > 1) | |
746 | nMinDepth = params[1].get_int(); | |
a3e192a3 | 747 | isminefilter filter = ISMINE_SPENDABLE; |
a5c6c5d6 J |
748 | if(params.size() > 2) |
749 | if(params[2].get_bool()) | |
a3e192a3 | 750 | filter = filter | ISMINE_WATCH_ONLY; |
e3bc5698 JG |
751 | |
752 | if (params[0].get_str() == "*") { | |
753 | // Calculate total balance a different way from GetBalance() | |
754 | // (GetBalance() sums up all unspent TxOuts) | |
c9fd9078 | 755 | // getbalance and "getbalance * 1 true" should return the same number |
a372168e | 756 | CAmount nBalance = 0; |
e3bc5698 JG |
757 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) |
758 | { | |
759 | const CWalletTx& wtx = (*it).second; | |
c9fd9078 | 760 | if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) |
e3bc5698 JG |
761 | continue; |
762 | ||
a372168e | 763 | CAmount allFee; |
e3bc5698 | 764 | string strSentAccount; |
1b4568cb CL |
765 | list<COutputEntry> listReceived; |
766 | list<COutputEntry> listSent; | |
d4640d7d | 767 | wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); |
e3bc5698 JG |
768 | if (wtx.GetDepthInMainChain() >= nMinDepth) |
769 | { | |
1b4568cb CL |
770 | BOOST_FOREACH(const COutputEntry& r, listReceived) |
771 | nBalance += r.amount; | |
e3bc5698 | 772 | } |
1b4568cb CL |
773 | BOOST_FOREACH(const COutputEntry& s, listSent) |
774 | nBalance -= s.amount; | |
e3bc5698 | 775 | nBalance -= allFee; |
e3bc5698 JG |
776 | } |
777 | return ValueFromAmount(nBalance); | |
778 | } | |
779 | ||
780 | string strAccount = AccountFromValue(params[0]); | |
781 | ||
a372168e | 782 | CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, filter); |
e3bc5698 JG |
783 | |
784 | return ValueFromAmount(nBalance); | |
785 | } | |
786 | ||
d014114d | 787 | UniValue getunconfirmedbalance(const UniValue ¶ms, bool fHelp) |
6027b460 | 788 | { |
b9fb692d | 789 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 790 | return NullUniValue; |
70454796 | 791 | |
6027b460 MB |
792 | if (fHelp || params.size() > 0) |
793 | throw runtime_error( | |
794 | "getunconfirmedbalance\n" | |
795 | "Returns the server's total unconfirmed balance\n"); | |
4401b2d7 EL |
796 | |
797 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
798 | ||
6027b460 MB |
799 | return ValueFromAmount(pwalletMain->GetUnconfirmedBalance()); |
800 | } | |
801 | ||
e3bc5698 | 802 | |
d014114d | 803 | UniValue movecmd(const UniValue& params, bool fHelp) |
e3bc5698 | 804 | { |
b9fb692d | 805 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 806 | return NullUniValue; |
70454796 | 807 | |
e3bc5698 JG |
808 | if (fHelp || params.size() < 3 || params.size() > 5) |
809 | throw runtime_error( | |
a6099ef3 | 810 | "move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n" |
7b782f5b | 811 | "\nDEPRECATED. Move a specified amount from one account in your wallet to another.\n" |
a6099ef3 | 812 | "\nArguments:\n" |
3c31eb24 JG |
813 | "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" |
814 | "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" | |
091b2116 RN |
815 | "3. amount (numeric) Quantity of " + CURRENCY_UNIT + " to move between accounts.\n" |
816 | "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" | |
817 | "5. \"comment\" (string, optional) An optional comment, stored in the wallet only.\n" | |
a6099ef3 | 818 | "\nResult:\n" |
45bfa137 | 819 | "true|false (boolean) true if successful.\n" |
a6099ef3 | 820 | "\nExamples:\n" |
091b2116 | 821 | "\nMove 0.01 " + CURRENCY_UNIT + " from the default account to the account named tabby\n" |
a6099ef3 | 822 | + HelpExampleCli("move", "\"\" \"tabby\" 0.01") + |
091b2116 | 823 | "\nMove 0.01 " + CURRENCY_UNIT + " timotei to akiko with a comment and funds have 6 confirmations\n" |
a6099ef3 | 824 | + HelpExampleCli("move", "\"timotei\" \"akiko\" 0.01 6 \"happy birthday!\"") + |
825 | "\nAs a json rpc call\n" | |
826 | + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"") | |
827 | ); | |
e3bc5698 | 828 | |
4401b2d7 EL |
829 | LOCK2(cs_main, pwalletMain->cs_wallet); |
830 | ||
e3bc5698 JG |
831 | string strFrom = AccountFromValue(params[0]); |
832 | string strTo = AccountFromValue(params[1]); | |
a372168e | 833 | CAmount nAmount = AmountFromValue(params[2]); |
e76a3849 WL |
834 | if (nAmount <= 0) |
835 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); | |
e3bc5698 JG |
836 | if (params.size() > 3) |
837 | // unused parameter, used to be nMinDepth, keep type-checking it though | |
838 | (void)params[3].get_int(); | |
839 | string strComment; | |
840 | if (params.size() > 4) | |
841 | strComment = params[4].get_str(); | |
842 | ||
843 | CWalletDB walletdb(pwalletMain->strWalletFile); | |
844 | if (!walletdb.TxnBegin()) | |
738835d7 | 845 | throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); |
e3bc5698 | 846 | |
51ed9ec9 | 847 | int64_t nNow = GetAdjustedTime(); |
e3bc5698 JG |
848 | |
849 | // Debit | |
850 | CAccountingEntry debit; | |
4291e8fe | 851 | debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); |
e3bc5698 JG |
852 | debit.strAccount = strFrom; |
853 | debit.nCreditDebit = -nAmount; | |
854 | debit.nTime = nNow; | |
855 | debit.strOtherAccount = strTo; | |
856 | debit.strComment = strComment; | |
857 | walletdb.WriteAccountingEntry(debit); | |
858 | ||
859 | // Credit | |
860 | CAccountingEntry credit; | |
4291e8fe | 861 | credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); |
e3bc5698 JG |
862 | credit.strAccount = strTo; |
863 | credit.nCreditDebit = nAmount; | |
864 | credit.nTime = nNow; | |
865 | credit.strOtherAccount = strFrom; | |
866 | credit.strComment = strComment; | |
867 | walletdb.WriteAccountingEntry(credit); | |
868 | ||
869 | if (!walletdb.TxnCommit()) | |
738835d7 | 870 | throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); |
e3bc5698 JG |
871 | |
872 | return true; | |
873 | } | |
874 | ||
875 | ||
d014114d | 876 | UniValue sendfrom(const UniValue& params, bool fHelp) |
e3bc5698 | 877 | { |
b9fb692d | 878 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 879 | return NullUniValue; |
70454796 | 880 | |
e3bc5698 JG |
881 | if (fHelp || params.size() < 3 || params.size() > 6) |
882 | throw runtime_error( | |
58c4c0bb | 883 | "sendfrom \"fromaccount\" \"tozcashaddress\" amount ( minconf \"comment\" \"comment-to\" )\n" |
d2c1e4a8 | 884 | "\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a Zcash address.\n" |
a6099ef3 | 885 | "The amount is a real and is rounded to the nearest 0.00000001." |
886 | + HelpRequiringPassphrase() + "\n" | |
887 | "\nArguments:\n" | |
3c31eb24 | 888 | "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 | 889 | "2. \"tozcashaddress\" (string, required) The zcash address to send funds to.\n" |
091b2116 | 890 | "3. amount (numeric, required) The amount in " + CURRENCY_UNIT + " (transaction fee is added on top).\n" |
a6099ef3 | 891 | "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" |
892 | "5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" | |
893 | " This is not part of the transaction, just kept in your wallet.\n" | |
894 | "6. \"comment-to\" (string, optional) An optional comment to store the name of the person or organization \n" | |
895 | " to which you're sending the transaction. This is not part of the transaction, \n" | |
896 | " it is just kept in your wallet.\n" | |
897 | "\nResult:\n" | |
b5ef85c7 | 898 | "\"transactionid\" (string) The transaction id.\n" |
a6099ef3 | 899 | "\nExamples:\n" |
091b2116 | 900 | "\nSend 0.01 " + CURRENCY_UNIT + " from the default account to the address, must have at least 1 confirmation\n" |
70454796 | 901 | + HelpExampleCli("sendfrom", "\"\" \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01") + |
a6099ef3 | 902 | "\nSend 0.01 from the tabby account to the given address, funds must have at least 6 confirmations\n" |
70454796 | 903 | + HelpExampleCli("sendfrom", "\"tabby\" \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 0.01 6 \"donation\" \"seans outpost\"") + |
a6099ef3 | 904 | "\nAs a json rpc call\n" |
70454796 | 905 | + HelpExampleRpc("sendfrom", "\"tabby\", \"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", 0.01, 6, \"donation\", \"seans outpost\"") |
a6099ef3 | 906 | ); |
e3bc5698 | 907 | |
4401b2d7 EL |
908 | LOCK2(cs_main, pwalletMain->cs_wallet); |
909 | ||
e3bc5698 JG |
910 | string strAccount = AccountFromValue(params[0]); |
911 | CBitcoinAddress address(params[1].get_str()); | |
912 | if (!address.IsValid()) | |
58c4c0bb | 913 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Zcash address"); |
a372168e | 914 | CAmount nAmount = AmountFromValue(params[2]); |
e76a3849 WL |
915 | if (nAmount <= 0) |
916 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); | |
e3bc5698 JG |
917 | int nMinDepth = 1; |
918 | if (params.size() > 3) | |
919 | nMinDepth = params[3].get_int(); | |
920 | ||
921 | CWalletTx wtx; | |
922 | wtx.strFromAccount = strAccount; | |
ed21d5bd | 923 | if (params.size() > 4 && !params[4].isNull() && !params[4].get_str().empty()) |
e3bc5698 | 924 | wtx.mapValue["comment"] = params[4].get_str(); |
ed21d5bd | 925 | if (params.size() > 5 && !params[5].isNull() && !params[5].get_str().empty()) |
e3bc5698 JG |
926 | wtx.mapValue["to"] = params[5].get_str(); |
927 | ||
928 | EnsureWalletIsUnlocked(); | |
929 | ||
930 | // Check funds | |
a372168e | 931 | CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); |
e3bc5698 | 932 | if (nAmount > nBalance) |
738835d7 | 933 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); |
e3bc5698 | 934 | |
292623ad | 935 | SendMoney(address.Get(), nAmount, false, wtx); |
e3bc5698 | 936 | |
805344dc | 937 | return wtx.GetHash().GetHex(); |
e3bc5698 JG |
938 | } |
939 | ||
940 | ||
d014114d | 941 | UniValue sendmany(const UniValue& params, bool fHelp) |
e3bc5698 | 942 | { |
b9fb692d | 943 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 944 | return NullUniValue; |
70454796 | 945 | |
292623ad | 946 | if (fHelp || params.size() < 2 || params.size() > 5) |
e3bc5698 | 947 | throw runtime_error( |
40a75733 | 948 | "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n" |
a6099ef3 | 949 | "\nSend multiple times. Amounts are double-precision floating point numbers." |
950 | + HelpRequiringPassphrase() + "\n" | |
951 | "\nArguments:\n" | |
3c31eb24 | 952 | "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 | 953 | "2. \"amounts\" (string, required) A json object with addresses and amounts\n" |
954 | " {\n" | |
091b2116 | 955 | " \"address\":amount (numeric) The zcash address is the key, the numeric amount in " + CURRENCY_UNIT + " is the value\n" |
a6099ef3 | 956 | " ,...\n" |
957 | " }\n" | |
958 | "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n" | |
959 | "4. \"comment\" (string, optional) A comment\n" | |
40a75733 | 960 | "5. subtractfeefromamount (string, optional) A json array with addresses.\n" |
292623ad | 961 | " The fee will be equally deducted from the amount of each selected address.\n" |
c1652849 | 962 | " Those recipients will receive less Zcash than you enter in their corresponding amount field.\n" |
40a75733 LD |
963 | " If no addresses are specified here, the sender pays the fee.\n" |
964 | " [\n" | |
965 | " \"address\" (string) Subtract fee from this address\n" | |
292623ad | 966 | " ,...\n" |
40a75733 | 967 | " ]\n" |
a6099ef3 | 968 | "\nResult:\n" |
969 | "\"transactionid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" | |
b5ef85c7 | 970 | " the number of addresses.\n" |
a6099ef3 | 971 | "\nExamples:\n" |
972 | "\nSend two amounts to two different addresses:\n" | |
70454796 | 973 | + HelpExampleCli("sendmany", "\"\" \"{\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\":0.01,\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\"") + |
a6099ef3 | 974 | "\nSend two amounts to two different addresses setting the confirmation and comment:\n" |
70454796 | 975 | + HelpExampleCli("sendmany", "\"\" \"{\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\":0.01,\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 6 \"testing\"") + |
292623ad | 976 | "\nSend two amounts to two different addresses, subtract fee from amount:\n" |
70454796 | 977 | + HelpExampleCli("sendmany", "\"\" \"{\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\":0.01,\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\" 1 \"\" \"[\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\",\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\"]\"") + |
a6099ef3 | 978 | "\nAs a json rpc call\n" |
70454796 | 979 | + HelpExampleRpc("sendmany", "\"\", \"{\\\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\\\":0.01,\\\"t1353tsE8YMTA4EuV7dgUXGjNFf9KpVvKHz\\\":0.02}\", 6, \"testing\"") |
a6099ef3 | 980 | ); |
e3bc5698 | 981 | |
4401b2d7 EL |
982 | LOCK2(cs_main, pwalletMain->cs_wallet); |
983 | ||
e3bc5698 | 984 | string strAccount = AccountFromValue(params[0]); |
851f58f9 | 985 | UniValue sendTo = params[1].get_obj(); |
e3bc5698 JG |
986 | int nMinDepth = 1; |
987 | if (params.size() > 2) | |
988 | nMinDepth = params[2].get_int(); | |
989 | ||
990 | CWalletTx wtx; | |
991 | wtx.strFromAccount = strAccount; | |
ed21d5bd | 992 | if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) |
e3bc5698 JG |
993 | wtx.mapValue["comment"] = params[3].get_str(); |
994 | ||
38fc4b70 | 995 | UniValue subtractFeeFromAmount(UniValue::VARR); |
292623ad | 996 | if (params.size() > 4) |
40a75733 | 997 | subtractFeeFromAmount = params[4].get_array(); |
292623ad | 998 | |
e3bc5698 | 999 | set<CBitcoinAddress> setAddress; |
292623ad | 1000 | vector<CRecipient> vecSend; |
e3bc5698 | 1001 | |
a372168e | 1002 | CAmount totalAmount = 0; |
ed21d5bd JG |
1003 | vector<string> keys = sendTo.getKeys(); |
1004 | BOOST_FOREACH(const string& name_, keys) | |
e3bc5698 | 1005 | { |
ed21d5bd | 1006 | CBitcoinAddress address(name_); |
e3bc5698 | 1007 | if (!address.IsValid()) |
ed21d5bd | 1008 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Zcash address: ")+name_); |
e3bc5698 JG |
1009 | |
1010 | if (setAddress.count(address)) | |
ed21d5bd | 1011 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); |
e3bc5698 JG |
1012 | setAddress.insert(address); |
1013 | ||
0be990ba | 1014 | CScript scriptPubKey = GetScriptForDestination(address.Get()); |
ed21d5bd | 1015 | CAmount nAmount = AmountFromValue(sendTo[name_]); |
e76a3849 WL |
1016 | if (nAmount <= 0) |
1017 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); | |
e3bc5698 JG |
1018 | totalAmount += nAmount; |
1019 | ||
292623ad | 1020 | bool fSubtractFeeFromAmount = false; |
cc71666a | 1021 | for (size_t idx = 0; idx < subtractFeeFromAmount.size(); idx++) { |
d014114d | 1022 | const UniValue& addr = subtractFeeFromAmount[idx]; |
9756b7bd | 1023 | if (addr.get_str() == name_) |
292623ad | 1024 | fSubtractFeeFromAmount = true; |
9756b7bd | 1025 | } |
292623ad CL |
1026 | |
1027 | CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount}; | |
1028 | vecSend.push_back(recipient); | |
e3bc5698 JG |
1029 | } |
1030 | ||
1031 | EnsureWalletIsUnlocked(); | |
1032 | ||
1033 | // Check funds | |
a372168e | 1034 | CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); |
e3bc5698 | 1035 | if (totalAmount > nBalance) |
738835d7 | 1036 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); |
e3bc5698 JG |
1037 | |
1038 | // Send | |
1039 | CReserveKey keyChange(pwalletMain); | |
a372168e | 1040 | CAmount nFeeRequired = 0; |
292623ad | 1041 | int nChangePosRet = -1; |
1f00f4e9 | 1042 | string strFailReason; |
292623ad | 1043 | bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason); |
e3bc5698 | 1044 | if (!fCreated) |
1f00f4e9 | 1045 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); |
e3bc5698 | 1046 | if (!pwalletMain->CommitTransaction(wtx, keyChange)) |
738835d7 | 1047 | throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed"); |
e3bc5698 | 1048 | |
805344dc | 1049 | return wtx.GetHash().GetHex(); |
e3bc5698 JG |
1050 | } |
1051 | ||
723a03d2 | 1052 | // Defined in rpcmisc.cpp |
d014114d | 1053 | extern CScript _createmultisig_redeemScript(const UniValue& params); |
34226be7 | 1054 | |
d014114d | 1055 | UniValue addmultisigaddress(const UniValue& params, bool fHelp) |
34226be7 | 1056 | { |
b9fb692d | 1057 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1058 | return NullUniValue; |
70454796 | 1059 | |
34226be7 GA |
1060 | if (fHelp || params.size() < 2 || params.size() > 3) |
1061 | { | |
a6099ef3 | 1062 | string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n" |
1063 | "\nAdd a nrequired-to-sign multisignature address to the wallet.\n" | |
58c4c0bb | 1064 | "Each key is a Zcash address or hex-encoded public key.\n" |
7b782f5b | 1065 | "If 'account' is specified (DEPRECATED), assign address to that account.\n" |
a6099ef3 | 1066 | |
1067 | "\nArguments:\n" | |
1068 | "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" | |
d2c1e4a8 | 1069 | "2. \"keysobject\" (string, required) A json array of Zcash addresses or hex-encoded public keys\n" |
a6099ef3 | 1070 | " [\n" |
d2c1e4a8 | 1071 | " \"address\" (string) Zcash address or hex-encoded public key\n" |
a6099ef3 | 1072 | " ...,\n" |
1073 | " ]\n" | |
3c31eb24 | 1074 | "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 | 1075 | |
1076 | "\nResult:\n" | |
d2c1e4a8 | 1077 | "\"zcashaddress\" (string) A Zcash address associated with the keys.\n" |
a6099ef3 | 1078 | |
1079 | "\nExamples:\n" | |
1080 | "\nAdd a multisig address from 2 addresses\n" | |
70454796 | 1081 | + HelpExampleCli("addmultisigaddress", "2 \"[\\\"t16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"t171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + |
a6099ef3 | 1082 | "\nAs json rpc call\n" |
70454796 | 1083 | + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"t16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"t171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") |
a6099ef3 | 1084 | ; |
34226be7 GA |
1085 | throw runtime_error(msg); |
1086 | } | |
1087 | ||
4401b2d7 EL |
1088 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1089 | ||
34226be7 GA |
1090 | string strAccount; |
1091 | if (params.size() > 2) | |
1092 | strAccount = AccountFromValue(params[2]); | |
e3bc5698 JG |
1093 | |
1094 | // Construct using pay-to-script-hash: | |
787ee0c9 | 1095 | CScript inner = _createmultisig_redeemScript(params); |
066e2a14 | 1096 | CScriptID innerID(inner); |
e3bc5698 JG |
1097 | pwalletMain->AddCScript(inner); |
1098 | ||
a41d5fe0 | 1099 | pwalletMain->SetAddressBook(innerID, strAccount, "send"); |
e3bc5698 JG |
1100 | return CBitcoinAddress(innerID).ToString(); |
1101 | } | |
1102 | ||
1103 | ||
1104 | struct tallyitem | |
1105 | { | |
a372168e | 1106 | CAmount nAmount; |
e3bc5698 | 1107 | int nConf; |
62c9b115 | 1108 | vector<uint256> txids; |
0fa2f889 | 1109 | bool fIsWatchonly; |
e3bc5698 JG |
1110 | tallyitem() |
1111 | { | |
1112 | nAmount = 0; | |
1113 | nConf = std::numeric_limits<int>::max(); | |
0fa2f889 | 1114 | fIsWatchonly = false; |
e3bc5698 JG |
1115 | } |
1116 | }; | |
1117 | ||
d014114d | 1118 | UniValue ListReceived(const UniValue& params, bool fByAccounts) |
e3bc5698 JG |
1119 | { |
1120 | // Minimum confirmations | |
1121 | int nMinDepth = 1; | |
1122 | if (params.size() > 0) | |
1123 | nMinDepth = params[0].get_int(); | |
1124 | ||
1125 | // Whether to include empty accounts | |
1126 | bool fIncludeEmpty = false; | |
1127 | if (params.size() > 1) | |
1128 | fIncludeEmpty = params[1].get_bool(); | |
1129 | ||
a3e192a3 | 1130 | isminefilter filter = ISMINE_SPENDABLE; |
0fa2f889 J |
1131 | if(params.size() > 2) |
1132 | if(params[2].get_bool()) | |
a3e192a3 | 1133 | filter = filter | ISMINE_WATCH_ONLY; |
0fa2f889 | 1134 | |
e3bc5698 JG |
1135 | // Tally |
1136 | map<CBitcoinAddress, tallyitem> mapTally; | |
1137 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) | |
1138 | { | |
1139 | const CWalletTx& wtx = (*it).second; | |
1140 | ||
75a4d512 | 1141 | if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) |
e3bc5698 JG |
1142 | continue; |
1143 | ||
1144 | int nDepth = wtx.GetDepthInMainChain(); | |
1145 | if (nDepth < nMinDepth) | |
1146 | continue; | |
1147 | ||
1148 | BOOST_FOREACH(const CTxOut& txout, wtx.vout) | |
1149 | { | |
1150 | CTxDestination address; | |
0fa2f889 J |
1151 | if (!ExtractDestination(txout.scriptPubKey, address)) |
1152 | continue; | |
1153 | ||
1154 | isminefilter mine = IsMine(*pwalletMain, address); | |
f28707a8 | 1155 | if(!(mine & filter)) |
e3bc5698 JG |
1156 | continue; |
1157 | ||
1158 | tallyitem& item = mapTally[address]; | |
1159 | item.nAmount += txout.nValue; | |
1160 | item.nConf = min(item.nConf, nDepth); | |
805344dc | 1161 | item.txids.push_back(wtx.GetHash()); |
a3e192a3 | 1162 | if (mine & ISMINE_WATCH_ONLY) |
0fa2f889 | 1163 | item.fIsWatchonly = true; |
e3bc5698 JG |
1164 | } |
1165 | } | |
1166 | ||
1167 | // Reply | |
38fc4b70 | 1168 | UniValue ret(UniValue::VARR); |
e3bc5698 | 1169 | map<string, tallyitem> mapAccountTally; |
61885513 | 1170 | BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) |
e3bc5698 JG |
1171 | { |
1172 | const CBitcoinAddress& address = item.first; | |
61885513 | 1173 | const string& strAccount = item.second.name; |
e3bc5698 JG |
1174 | map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address); |
1175 | if (it == mapTally.end() && !fIncludeEmpty) | |
1176 | continue; | |
1177 | ||
a372168e | 1178 | CAmount nAmount = 0; |
e3bc5698 | 1179 | int nConf = std::numeric_limits<int>::max(); |
0fa2f889 | 1180 | bool fIsWatchonly = false; |
e3bc5698 JG |
1181 | if (it != mapTally.end()) |
1182 | { | |
1183 | nAmount = (*it).second.nAmount; | |
1184 | nConf = (*it).second.nConf; | |
0fa2f889 | 1185 | fIsWatchonly = (*it).second.fIsWatchonly; |
e3bc5698 JG |
1186 | } |
1187 | ||
1188 | if (fByAccounts) | |
1189 | { | |
1190 | tallyitem& item = mapAccountTally[strAccount]; | |
1191 | item.nAmount += nAmount; | |
1192 | item.nConf = min(item.nConf, nConf); | |
0fa2f889 | 1193 | item.fIsWatchonly = fIsWatchonly; |
e3bc5698 JG |
1194 | } |
1195 | else | |
1196 | { | |
38fc4b70 | 1197 | UniValue obj(UniValue::VOBJ); |
0fa2f889 J |
1198 | if(fIsWatchonly) |
1199 | obj.push_back(Pair("involvesWatchonly", true)); | |
e3bc5698 JG |
1200 | obj.push_back(Pair("address", address.ToString())); |
1201 | obj.push_back(Pair("account", strAccount)); | |
1202 | obj.push_back(Pair("amount", ValueFromAmount(nAmount))); | |
1203 | obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); | |
38fc4b70 | 1204 | UniValue transactions(UniValue::VARR); |
1a204694 | 1205 | if (it != mapTally.end()) |
62c9b115 | 1206 | { |
1a204694 A |
1207 | BOOST_FOREACH(const uint256& item, (*it).second.txids) |
1208 | { | |
1209 | transactions.push_back(item.GetHex()); | |
1210 | } | |
62c9b115 A |
1211 | } |
1212 | obj.push_back(Pair("txids", transactions)); | |
e3bc5698 JG |
1213 | ret.push_back(obj); |
1214 | } | |
1215 | } | |
1216 | ||
1217 | if (fByAccounts) | |
1218 | { | |
1219 | for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) | |
1220 | { | |
a372168e | 1221 | CAmount nAmount = (*it).second.nAmount; |
e3bc5698 | 1222 | int nConf = (*it).second.nConf; |
38fc4b70 | 1223 | UniValue obj(UniValue::VOBJ); |
0fa2f889 J |
1224 | if((*it).second.fIsWatchonly) |
1225 | obj.push_back(Pair("involvesWatchonly", true)); | |
e3bc5698 JG |
1226 | obj.push_back(Pair("account", (*it).first)); |
1227 | obj.push_back(Pair("amount", ValueFromAmount(nAmount))); | |
1228 | obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); | |
1229 | ret.push_back(obj); | |
1230 | } | |
1231 | } | |
1232 | ||
1233 | return ret; | |
1234 | } | |
1235 | ||
d014114d | 1236 | UniValue listreceivedbyaddress(const UniValue& params, bool fHelp) |
e3bc5698 | 1237 | { |
b9fb692d | 1238 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1239 | return NullUniValue; |
70454796 | 1240 | |
0fa2f889 | 1241 | if (fHelp || params.size() > 3) |
e3bc5698 | 1242 | throw runtime_error( |
0fa2f889 | 1243 | "listreceivedbyaddress ( minconf includeempty includeWatchonly)\n" |
a6099ef3 | 1244 | "\nList balances by receiving address.\n" |
1245 | "\nArguments:\n" | |
1246 | "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" | |
5617267c | 1247 | "2. includeempty (numeric, optional, default=false) Whether to include addresses that haven't received any payments.\n" |
0fa2f889 | 1248 | "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" |
a6099ef3 | 1249 | |
1250 | "\nResult:\n" | |
1251 | "[\n" | |
1252 | " {\n" | |
8f6860a0 | 1253 | " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" |
a6099ef3 | 1254 | " \"address\" : \"receivingaddress\", (string) The receiving address\n" |
7b782f5b | 1255 | " \"account\" : \"accountname\", (string) DEPRECATED. The account of the receiving address. The default account is \"\".\n" |
091b2116 | 1256 | " \"amount\" : x.xxx, (numeric) The total amount in " + CURRENCY_UNIT + " received by the address\n" |
a6099ef3 | 1257 | " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n" |
1258 | " }\n" | |
1259 | " ,...\n" | |
1260 | "]\n" | |
1261 | ||
1262 | "\nExamples:\n" | |
1263 | + HelpExampleCli("listreceivedbyaddress", "") | |
1264 | + HelpExampleCli("listreceivedbyaddress", "6 true") | |
0fa2f889 | 1265 | + HelpExampleRpc("listreceivedbyaddress", "6, true, true") |
a6099ef3 | 1266 | ); |
e3bc5698 | 1267 | |
4401b2d7 EL |
1268 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1269 | ||
e3bc5698 JG |
1270 | return ListReceived(params, false); |
1271 | } | |
1272 | ||
d014114d | 1273 | UniValue listreceivedbyaccount(const UniValue& params, bool fHelp) |
e3bc5698 | 1274 | { |
b9fb692d | 1275 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1276 | return NullUniValue; |
70454796 | 1277 | |
0fa2f889 | 1278 | if (fHelp || params.size() > 3) |
e3bc5698 | 1279 | throw runtime_error( |
0fa2f889 | 1280 | "listreceivedbyaccount ( minconf includeempty includeWatchonly)\n" |
7b782f5b | 1281 | "\nDEPRECATED. List balances by account.\n" |
a6099ef3 | 1282 | "\nArguments:\n" |
1283 | "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" | |
1284 | "2. includeempty (boolean, optional, default=false) Whether to include accounts that haven't received any payments.\n" | |
0fa2f889 | 1285 | "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" |
a6099ef3 | 1286 | |
1287 | "\nResult:\n" | |
1288 | "[\n" | |
1289 | " {\n" | |
8f6860a0 | 1290 | " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" |
a6099ef3 | 1291 | " \"account\" : \"accountname\", (string) The account name of the receiving account\n" |
1292 | " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this account\n" | |
1293 | " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n" | |
1294 | " }\n" | |
1295 | " ,...\n" | |
1296 | "]\n" | |
1297 | ||
1298 | "\nExamples:\n" | |
1299 | + HelpExampleCli("listreceivedbyaccount", "") | |
1300 | + HelpExampleCli("listreceivedbyaccount", "6 true") | |
0fa2f889 | 1301 | + HelpExampleRpc("listreceivedbyaccount", "6, true, true") |
a6099ef3 | 1302 | ); |
e3bc5698 | 1303 | |
4401b2d7 EL |
1304 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1305 | ||
e3bc5698 JG |
1306 | return ListReceived(params, true); |
1307 | } | |
1308 | ||
851f58f9 | 1309 | static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) |
cc6cfab3 LD |
1310 | { |
1311 | CBitcoinAddress addr; | |
1312 | if (addr.Set(dest)) | |
1313 | entry.push_back(Pair("address", addr.ToString())); | |
1314 | } | |
1315 | ||
d014114d | 1316 | void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) |
e3bc5698 | 1317 | { |
a372168e | 1318 | CAmount nFee; |
e3bc5698 | 1319 | string strSentAccount; |
1b4568cb CL |
1320 | list<COutputEntry> listReceived; |
1321 | list<COutputEntry> listSent; | |
e3bc5698 | 1322 | |
d7d5d23b | 1323 | wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); |
e3bc5698 JG |
1324 | |
1325 | bool fAllAccounts = (strAccount == string("*")); | |
a3e192a3 | 1326 | bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY); |
e3bc5698 | 1327 | |
e3bc5698 JG |
1328 | // Sent |
1329 | if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) | |
1330 | { | |
1b4568cb | 1331 | BOOST_FOREACH(const COutputEntry& s, listSent) |
e3bc5698 | 1332 | { |
38fc4b70 | 1333 | UniValue entry(UniValue::VOBJ); |
1b4568cb | 1334 | if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY)) |
952877e0 | 1335 | entry.push_back(Pair("involvesWatchonly", true)); |
e3bc5698 | 1336 | entry.push_back(Pair("account", strSentAccount)); |
1b4568cb | 1337 | MaybePushAddress(entry, s.destination); |
b96f6a77 | 1338 | entry.push_back(Pair("category", "send")); |
1b4568cb CL |
1339 | entry.push_back(Pair("amount", ValueFromAmount(-s.amount))); |
1340 | entry.push_back(Pair("vout", s.vout)); | |
e3bc5698 JG |
1341 | entry.push_back(Pair("fee", ValueFromAmount(-nFee))); |
1342 | if (fLong) | |
1343 | WalletTxToJSON(wtx, entry); | |
f32cade8 | 1344 | entry.push_back(Pair("size", static_cast<CTransaction>(wtx).GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION))); |
e3bc5698 JG |
1345 | ret.push_back(entry); |
1346 | } | |
1347 | } | |
1348 | ||
1349 | // Received | |
1350 | if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) | |
1351 | { | |
1b4568cb | 1352 | BOOST_FOREACH(const COutputEntry& r, listReceived) |
e3bc5698 JG |
1353 | { |
1354 | string account; | |
1b4568cb CL |
1355 | if (pwalletMain->mapAddressBook.count(r.destination)) |
1356 | account = pwalletMain->mapAddressBook[r.destination].name; | |
e3bc5698 JG |
1357 | if (fAllAccounts || (account == strAccount)) |
1358 | { | |
38fc4b70 | 1359 | UniValue entry(UniValue::VOBJ); |
1b4568cb | 1360 | if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY)) |
952877e0 | 1361 | entry.push_back(Pair("involvesWatchonly", true)); |
e3bc5698 | 1362 | entry.push_back(Pair("account", account)); |
1b4568cb | 1363 | MaybePushAddress(entry, r.destination); |
e07c8e91 LD |
1364 | if (wtx.IsCoinBase()) |
1365 | { | |
1366 | if (wtx.GetDepthInMainChain() < 1) | |
1367 | entry.push_back(Pair("category", "orphan")); | |
1368 | else if (wtx.GetBlocksToMaturity() > 0) | |
1369 | entry.push_back(Pair("category", "immature")); | |
1370 | else | |
1371 | entry.push_back(Pair("category", "generate")); | |
1372 | } | |
1373 | else | |
2b72d46f | 1374 | { |
b96f6a77 | 1375 | entry.push_back(Pair("category", "receive")); |
2b72d46f | 1376 | } |
1b4568cb CL |
1377 | entry.push_back(Pair("amount", ValueFromAmount(r.amount))); |
1378 | entry.push_back(Pair("vout", r.vout)); | |
e3bc5698 JG |
1379 | if (fLong) |
1380 | WalletTxToJSON(wtx, entry); | |
f32cade8 | 1381 | entry.push_back(Pair("size", static_cast<CTransaction>(wtx).GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION))); |
e3bc5698 JG |
1382 | ret.push_back(entry); |
1383 | } | |
1384 | } | |
1385 | } | |
1386 | } | |
1387 | ||
d014114d | 1388 | void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, UniValue& ret) |
e3bc5698 JG |
1389 | { |
1390 | bool fAllAccounts = (strAccount == string("*")); | |
1391 | ||
1392 | if (fAllAccounts || acentry.strAccount == strAccount) | |
1393 | { | |
38fc4b70 | 1394 | UniValue entry(UniValue::VOBJ); |
e3bc5698 JG |
1395 | entry.push_back(Pair("account", acentry.strAccount)); |
1396 | entry.push_back(Pair("category", "move")); | |
d56e30ca | 1397 | entry.push_back(Pair("time", acentry.nTime)); |
e3bc5698 JG |
1398 | entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); |
1399 | entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); | |
1400 | entry.push_back(Pair("comment", acentry.strComment)); | |
1401 | ret.push_back(entry); | |
1402 | } | |
1403 | } | |
1404 | ||
d014114d | 1405 | UniValue listtransactions(const UniValue& params, bool fHelp) |
e3bc5698 | 1406 | { |
b9fb692d | 1407 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1408 | return NullUniValue; |
70454796 | 1409 | |
d7d5d23b | 1410 | if (fHelp || params.size() > 4) |
e3bc5698 | 1411 | throw runtime_error( |
d7d5d23b | 1412 | "listtransactions ( \"account\" count from includeWatchonly)\n" |
a6099ef3 | 1413 | "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" |
1414 | "\nArguments:\n" | |
7b782f5b | 1415 | "1. \"account\" (string, optional) DEPRECATED. The account name. Should be \"*\".\n" |
a6099ef3 | 1416 | "2. count (numeric, optional, default=10) The number of transactions to return\n" |
1417 | "3. from (numeric, optional, default=0) The number of transactions to skip\n" | |
d7d5d23b | 1418 | "4. includeWatchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n" |
a6099ef3 | 1419 | "\nResult:\n" |
1420 | "[\n" | |
1421 | " {\n" | |
7b782f5b | 1422 | " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. \n" |
a6099ef3 | 1423 | " It will be \"\" for the default account.\n" |
d2c1e4a8 | 1424 | " \"address\":\"zcashaddress\", (string) The Zcash address of the transaction. Not present for \n" |
a6099ef3 | 1425 | " move transactions (category = move).\n" |
1426 | " \"category\":\"send|receive|move\", (string) The transaction category. 'move' is a local (off blockchain)\n" | |
1427 | " transaction between accounts, and not associated with an address,\n" | |
1428 | " transaction id or block. 'send' and 'receive' transactions are \n" | |
1429 | " associated with an address, transaction id and block details\n" | |
091b2116 | 1430 | " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the\n" |
a6099ef3 | 1431 | " 'move' category for moves outbound. It is positive for the 'receive' category,\n" |
1432 | " and for the 'move' category for inbound funds.\n" | |
1b4568cb | 1433 | " \"vout\" : n, (numeric) the vout value\n" |
091b2116 | 1434 | " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the \n" |
a6099ef3 | 1435 | " 'send' category of transactions.\n" |
1436 | " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" | |
1437 | " 'receive' category of transactions.\n" | |
1438 | " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n" | |
1439 | " category of transactions.\n" | |
1440 | " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive'\n" | |
1441 | " category of transactions.\n" | |
b5ef85c7 | 1442 | " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" |
a6099ef3 | 1443 | " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" |
1444 | " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" | |
1445 | " for 'send' and 'receive' category of transactions.\n" | |
1446 | " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" | |
1447 | " \"otheraccount\": \"accountname\", (string) For the 'move' category of transactions, the account the funds came \n" | |
1448 | " from (for receiving funds, positive amounts), or went to (for sending funds,\n" | |
1449 | " negative amounts).\n" | |
f32cade8 | 1450 | " \"size\": n, (numeric) Transaction size in bytes\n" |
a6099ef3 | 1451 | " }\n" |
1452 | "]\n" | |
1453 | ||
1454 | "\nExamples:\n" | |
1455 | "\nList the most recent 10 transactions in the systems\n" | |
1456 | + HelpExampleCli("listtransactions", "") + | |
7b782f5b LD |
1457 | "\nList transactions 100 to 120\n" |
1458 | + HelpExampleCli("listtransactions", "\"*\" 20 100") + | |
a6099ef3 | 1459 | "\nAs a json rpc call\n" |
7b782f5b | 1460 | + HelpExampleRpc("listtransactions", "\"*\", 20, 100") |
a6099ef3 | 1461 | ); |
e3bc5698 | 1462 | |
4401b2d7 EL |
1463 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1464 | ||
e3bc5698 JG |
1465 | string strAccount = "*"; |
1466 | if (params.size() > 0) | |
1467 | strAccount = params[0].get_str(); | |
1468 | int nCount = 10; | |
1469 | if (params.size() > 1) | |
1470 | nCount = params[1].get_int(); | |
1471 | int nFrom = 0; | |
1472 | if (params.size() > 2) | |
1473 | nFrom = params[2].get_int(); | |
a3e192a3 | 1474 | isminefilter filter = ISMINE_SPENDABLE; |
a5c6c5d6 J |
1475 | if(params.size() > 3) |
1476 | if(params[3].get_bool()) | |
a3e192a3 | 1477 | filter = filter | ISMINE_WATCH_ONLY; |
e3bc5698 JG |
1478 | |
1479 | if (nCount < 0) | |
738835d7 | 1480 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count"); |
e3bc5698 | 1481 | if (nFrom < 0) |
738835d7 | 1482 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from"); |
e3bc5698 | 1483 | |
38fc4b70 | 1484 | UniValue ret(UniValue::VARR); |
e3bc5698 | 1485 | |
ddb709e9 LD |
1486 | std::list<CAccountingEntry> acentries; |
1487 | CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount); | |
e3bc5698 JG |
1488 | |
1489 | // iterate backwards until we have nCount items to return: | |
c3f95ef1 | 1490 | for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) |
e3bc5698 JG |
1491 | { |
1492 | CWalletTx *const pwtx = (*it).second.first; | |
1493 | if (pwtx != 0) | |
d7d5d23b | 1494 | ListTransactions(*pwtx, strAccount, 0, true, ret, filter); |
e3bc5698 JG |
1495 | CAccountingEntry *const pacentry = (*it).second.second; |
1496 | if (pacentry != 0) | |
1497 | AcentryToJSON(*pacentry, strAccount, ret); | |
1498 | ||
1499 | if ((int)ret.size() >= (nCount+nFrom)) break; | |
1500 | } | |
1501 | // ret is newest to oldest | |
1502 | ||
1503 | if (nFrom > (int)ret.size()) | |
1504 | nFrom = ret.size(); | |
1505 | if ((nFrom + nCount) > (int)ret.size()) | |
1506 | nCount = ret.size() - nFrom; | |
ed21d5bd JG |
1507 | |
1508 | vector<UniValue> arrTmp = ret.getValues(); | |
1509 | ||
1510 | vector<UniValue>::iterator first = arrTmp.begin(); | |
e3bc5698 | 1511 | std::advance(first, nFrom); |
ed21d5bd | 1512 | vector<UniValue>::iterator last = arrTmp.begin(); |
e3bc5698 JG |
1513 | std::advance(last, nFrom+nCount); |
1514 | ||
ed21d5bd JG |
1515 | if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end()); |
1516 | if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first); | |
1517 | ||
1518 | std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest | |
e3bc5698 | 1519 | |
ed21d5bd | 1520 | ret.clear(); |
38fc4b70 | 1521 | ret.setArray(); |
ed21d5bd | 1522 | ret.push_backV(arrTmp); |
e3bc5698 JG |
1523 | |
1524 | return ret; | |
1525 | } | |
1526 | ||
d014114d | 1527 | UniValue listaccounts(const UniValue& params, bool fHelp) |
e3bc5698 | 1528 | { |
b9fb692d | 1529 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1530 | return NullUniValue; |
70454796 | 1531 | |
83f3543f | 1532 | if (fHelp || params.size() > 2) |
e3bc5698 | 1533 | throw runtime_error( |
83f3543f | 1534 | "listaccounts ( minconf includeWatchonly)\n" |
7b782f5b | 1535 | "\nDEPRECATED. Returns Object that has account names as keys, account balances as values.\n" |
a6099ef3 | 1536 | "\nArguments:\n" |
5617267c | 1537 | "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n" |
83f3543f | 1538 | "2. includeWatchonly (bool, optional, default=false) Include balances in watchonly addresses (see 'importaddress')\n" |
a6099ef3 | 1539 | "\nResult:\n" |
1540 | "{ (json object where keys are account names, and values are numeric balances\n" | |
1541 | " \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n" | |
1542 | " ...\n" | |
1543 | "}\n" | |
1544 | "\nExamples:\n" | |
1545 | "\nList account balances where there at least 1 confirmation\n" | |
1546 | + HelpExampleCli("listaccounts", "") + | |
1547 | "\nList account balances including zero confirmation transactions\n" | |
1548 | + HelpExampleCli("listaccounts", "0") + | |
1549 | "\nList account balances for 6 or more confirmations\n" | |
1550 | + HelpExampleCli("listaccounts", "6") + | |
1551 | "\nAs json rpc call\n" | |
1552 | + HelpExampleRpc("listaccounts", "6") | |
1553 | ); | |
e3bc5698 | 1554 | |
4401b2d7 EL |
1555 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1556 | ||
e3bc5698 JG |
1557 | int nMinDepth = 1; |
1558 | if (params.size() > 0) | |
1559 | nMinDepth = params[0].get_int(); | |
a3e192a3 | 1560 | isminefilter includeWatchonly = ISMINE_SPENDABLE; |
a5c6c5d6 J |
1561 | if(params.size() > 1) |
1562 | if(params[1].get_bool()) | |
a3e192a3 | 1563 | includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY; |
e3bc5698 | 1564 | |
a372168e | 1565 | map<string, CAmount> mapAccountBalances; |
61885513 | 1566 | BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) { |
83f3543f | 1567 | if (IsMine(*pwalletMain, entry.first) & includeWatchonly) // This address belongs to me |
61885513 | 1568 | mapAccountBalances[entry.second.name] = 0; |
e3bc5698 JG |
1569 | } |
1570 | ||
1571 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) | |
1572 | { | |
1573 | const CWalletTx& wtx = (*it).second; | |
a372168e | 1574 | CAmount nFee; |
e3bc5698 | 1575 | string strSentAccount; |
1b4568cb CL |
1576 | list<COutputEntry> listReceived; |
1577 | list<COutputEntry> listSent; | |
93a18a36 GA |
1578 | int nDepth = wtx.GetDepthInMainChain(); |
1579 | if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) | |
731b89b8 | 1580 | continue; |
83f3543f | 1581 | wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); |
e3bc5698 | 1582 | mapAccountBalances[strSentAccount] -= nFee; |
1b4568cb CL |
1583 | BOOST_FOREACH(const COutputEntry& s, listSent) |
1584 | mapAccountBalances[strSentAccount] -= s.amount; | |
93a18a36 | 1585 | if (nDepth >= nMinDepth) |
e3bc5698 | 1586 | { |
1b4568cb CL |
1587 | BOOST_FOREACH(const COutputEntry& r, listReceived) |
1588 | if (pwalletMain->mapAddressBook.count(r.destination)) | |
1589 | mapAccountBalances[pwalletMain->mapAddressBook[r.destination].name] += r.amount; | |
e3bc5698 | 1590 | else |
1b4568cb | 1591 | mapAccountBalances[""] += r.amount; |
e3bc5698 JG |
1592 | } |
1593 | } | |
1594 | ||
1595 | list<CAccountingEntry> acentries; | |
1596 | CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); | |
1597 | BOOST_FOREACH(const CAccountingEntry& entry, acentries) | |
1598 | mapAccountBalances[entry.strAccount] += entry.nCreditDebit; | |
1599 | ||
38fc4b70 | 1600 | UniValue ret(UniValue::VOBJ); |
a372168e | 1601 | BOOST_FOREACH(const PAIRTYPE(string, CAmount)& accountBalance, mapAccountBalances) { |
e3bc5698 JG |
1602 | ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); |
1603 | } | |
1604 | return ret; | |
1605 | } | |
1606 | ||
d014114d | 1607 | UniValue listsinceblock(const UniValue& params, bool fHelp) |
e3bc5698 | 1608 | { |
b9fb692d | 1609 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1610 | return NullUniValue; |
70454796 | 1611 | |
e3bc5698 JG |
1612 | if (fHelp) |
1613 | throw runtime_error( | |
d7d5d23b | 1614 | "listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n" |
a6099ef3 | 1615 | "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n" |
1616 | "\nArguments:\n" | |
1617 | "1. \"blockhash\" (string, optional) The block hash to list transactions since\n" | |
1618 | "2. target-confirmations: (numeric, optional) The confirmations required, must be 1 or more\n" | |
d7d5d23b | 1619 | "3. includeWatchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')" |
a6099ef3 | 1620 | "\nResult:\n" |
1621 | "{\n" | |
1622 | " \"transactions\": [\n" | |
7b782f5b | 1623 | " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. Will be \"\" for the default account.\n" |
d2c1e4a8 | 1624 | " \"address\":\"zcashaddress\", (string) The Zcash address of the transaction. Not present for move transactions (category = move).\n" |
a6099ef3 | 1625 | " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" |
091b2116 | 1626 | " \"amount\": x.xxx, (numeric) The amount in " + CURRENCY_UNIT + ". This is negative for the 'send' category, and for the 'move' category for moves \n" |
a6099ef3 | 1627 | " outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n" |
1b4568cb | 1628 | " \"vout\" : n, (numeric) the vout value\n" |
091b2116 | 1629 | " \"fee\": x.xxx, (numeric) The amount of the fee in " + CURRENCY_UNIT + ". This is negative and only available for the 'send' category of transactions.\n" |
a6099ef3 | 1630 | " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n" |
1631 | " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n" | |
1632 | " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive' category of transactions.\n" | |
1633 | " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" | |
b5ef85c7 | 1634 | " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" |
a6099ef3 | 1635 | " \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n" |
1636 | " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n" | |
1637 | " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" | |
1638 | " \"to\": \"...\", (string) If a comment to is associated with the transaction.\n" | |
1639 | " ],\n" | |
1640 | " \"lastblock\": \"lastblockhash\" (string) The hash of the last block\n" | |
1641 | "}\n" | |
1642 | "\nExamples:\n" | |
1643 | + HelpExampleCli("listsinceblock", "") | |
1644 | + HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6") | |
1645 | + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6") | |
1646 | ); | |
e3bc5698 | 1647 | |
4401b2d7 EL |
1648 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1649 | ||
e3bc5698 JG |
1650 | CBlockIndex *pindex = NULL; |
1651 | int target_confirms = 1; | |
a3e192a3 | 1652 | isminefilter filter = ISMINE_SPENDABLE; |
e3bc5698 JG |
1653 | |
1654 | if (params.size() > 0) | |
1655 | { | |
4f152496 | 1656 | uint256 blockId; |
e3bc5698 JG |
1657 | |
1658 | blockId.SetHex(params[0].get_str()); | |
145d5be8 | 1659 | BlockMap::iterator it = mapBlockIndex.find(blockId); |
e4daecda PW |
1660 | if (it != mapBlockIndex.end()) |
1661 | pindex = it->second; | |
e3bc5698 JG |
1662 | } |
1663 | ||
1664 | if (params.size() > 1) | |
1665 | { | |
1666 | target_confirms = params[1].get_int(); | |
1667 | ||
1668 | if (target_confirms < 1) | |
738835d7 | 1669 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); |
e3bc5698 JG |
1670 | } |
1671 | ||
a5c6c5d6 J |
1672 | if(params.size() > 2) |
1673 | if(params[2].get_bool()) | |
a3e192a3 | 1674 | filter = filter | ISMINE_WATCH_ONLY; |
a5c6c5d6 | 1675 | |
4c6d41b8 | 1676 | int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; |
e3bc5698 | 1677 | |
38fc4b70 | 1678 | UniValue transactions(UniValue::VARR); |
e3bc5698 JG |
1679 | |
1680 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) | |
1681 | { | |
1682 | CWalletTx tx = (*it).second; | |
1683 | ||
1684 | if (depth == -1 || tx.GetDepthInMainChain() < depth) | |
d7d5d23b | 1685 | ListTransactions(tx, "*", 0, true, transactions, filter); |
e3bc5698 JG |
1686 | } |
1687 | ||
4c6d41b8 | 1688 | CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; |
4f152496 | 1689 | uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); |
e3bc5698 | 1690 | |
38fc4b70 | 1691 | UniValue ret(UniValue::VOBJ); |
e3bc5698 JG |
1692 | ret.push_back(Pair("transactions", transactions)); |
1693 | ret.push_back(Pair("lastblock", lastblock.GetHex())); | |
1694 | ||
1695 | return ret; | |
1696 | } | |
1697 | ||
d014114d | 1698 | UniValue gettransaction(const UniValue& params, bool fHelp) |
e3bc5698 | 1699 | { |
b9fb692d | 1700 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1701 | return NullUniValue; |
70454796 | 1702 | |
f87ba3df | 1703 | if (fHelp || params.size() < 1 || params.size() > 2) |
e3bc5698 | 1704 | throw runtime_error( |
57e1716d | 1705 | "gettransaction \"txid\" ( includeWatchonly )\n" |
a6099ef3 | 1706 | "\nGet detailed information about in-wallet transaction <txid>\n" |
1707 | "\nArguments:\n" | |
1708 | "1. \"txid\" (string, required) The transaction id\n" | |
f87ba3df | 1709 | "2. \"includeWatchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n" |
a6099ef3 | 1710 | "\nResult:\n" |
1711 | "{\n" | |
091b2116 | 1712 | " \"amount\" : x.xxx, (numeric) The transaction amount in " + CURRENCY_UNIT + "\n" |
a6099ef3 | 1713 | " \"confirmations\" : n, (numeric) The number of confirmations\n" |
1714 | " \"blockhash\" : \"hash\", (string) The block hash\n" | |
1715 | " \"blockindex\" : xx, (numeric) The block index\n" | |
1716 | " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n" | |
b5ef85c7 | 1717 | " \"txid\" : \"transactionid\", (string) The transaction id.\n" |
a6099ef3 | 1718 | " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" |
1719 | " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" | |
1720 | " \"details\" : [\n" | |
1721 | " {\n" | |
7b782f5b | 1722 | " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" |
d2c1e4a8 | 1723 | " \"address\" : \"zcashaddress\", (string) The Zcash address involved in the transaction\n" |
a6099ef3 | 1724 | " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" |
091b2116 | 1725 | " \"amount\" : x.xxx (numeric) The amount in " + CURRENCY_UNIT + "\n" |
1b4568cb | 1726 | " \"vout\" : n, (numeric) the vout value\n" |
a6099ef3 | 1727 | " }\n" |
1728 | " ,...\n" | |
3a1c20b7 | 1729 | " ],\n" |
f7cfb52d S |
1730 | " \"vjoinsplit\" : [\n" |
1731 | " {\n" | |
1732 | " \"anchor\" : \"treestateref\", (string) Merkle root of note commitment tree\n" | |
1733 | " \"nullifiers\" : [ string, ... ] (string) Nullifiers of input notes\n" | |
1734 | " \"commitments\" : [ string, ... ] (string) Note commitments for note outputs\n" | |
1735 | " \"macs\" : [ string, ... ] (string) Message authentication tags\n" | |
1736 | " \"vpub_old\" : x.xxx (numeric) The amount removed from the transparent value pool\n" | |
1737 | " \"vpub_new\" : x.xxx, (numeric) The amount added to the transparent value pool\n" | |
1738 | " }\n" | |
1739 | " ,...\n" | |
1740 | " ],\n" | |
3a1c20b7 | 1741 | " \"hex\" : \"data\" (string) Raw data for transaction\n" |
a6099ef3 | 1742 | "}\n" |
1743 | ||
ab45ddb5 | 1744 | "\nExamples:\n" |
a6099ef3 | 1745 | + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") |
57e1716d | 1746 | + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true") |
a6099ef3 | 1747 | + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") |
1748 | ); | |
e3bc5698 | 1749 | |
4401b2d7 EL |
1750 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1751 | ||
e3bc5698 JG |
1752 | uint256 hash; |
1753 | hash.SetHex(params[0].get_str()); | |
1754 | ||
a3e192a3 | 1755 | isminefilter filter = ISMINE_SPENDABLE; |
f87ba3df J |
1756 | if(params.size() > 1) |
1757 | if(params[1].get_bool()) | |
a3e192a3 | 1758 | filter = filter | ISMINE_WATCH_ONLY; |
f87ba3df | 1759 | |
38fc4b70 | 1760 | UniValue entry(UniValue::VOBJ); |
e3bc5698 | 1761 | if (!pwalletMain->mapWallet.count(hash)) |
738835d7 | 1762 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); |
e3bc5698 JG |
1763 | const CWalletTx& wtx = pwalletMain->mapWallet[hash]; |
1764 | ||
ccca27a7 | 1765 | CAmount nCredit = wtx.GetCredit(filter); |
a372168e MF |
1766 | CAmount nDebit = wtx.GetDebit(filter); |
1767 | CAmount nNet = nCredit - nDebit; | |
1768 | CAmount nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0); | |
e3bc5698 JG |
1769 | |
1770 | entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); | |
80dda36a | 1771 | if (wtx.IsFromMe(filter)) |
e3bc5698 JG |
1772 | entry.push_back(Pair("fee", ValueFromAmount(nFee))); |
1773 | ||
1774 | WalletTxToJSON(wtx, entry); | |
1775 | ||
38fc4b70 | 1776 | UniValue details(UniValue::VARR); |
f87ba3df | 1777 | ListTransactions(wtx, "*", 0, false, details, filter); |
e3bc5698 JG |
1778 | entry.push_back(Pair("details", details)); |
1779 | ||
ae775b5b | 1780 | string strHex = EncodeHexTx(static_cast<CTransaction>(wtx)); |
3a1c20b7 WL |
1781 | entry.push_back(Pair("hex", strHex)); |
1782 | ||
e3bc5698 JG |
1783 | return entry; |
1784 | } | |
1785 | ||
1786 | ||
d014114d | 1787 | UniValue backupwallet(const UniValue& params, bool fHelp) |
e3bc5698 | 1788 | { |
b9fb692d | 1789 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1790 | return NullUniValue; |
70454796 | 1791 | |
e3bc5698 JG |
1792 | if (fHelp || params.size() != 1) |
1793 | throw runtime_error( | |
a6099ef3 | 1794 | "backupwallet \"destination\"\n" |
9064d73b | 1795 | "\nSafely copies wallet.dat to destination filename\n" |
a6099ef3 | 1796 | "\nArguments:\n" |
9064d73b S |
1797 | "1. \"destination\" (string, required) The destination filename, saved in the directory set by -exportdir option.\n" |
1798 | "\nResult:\n" | |
1799 | "\"path\" (string) The full path of the destination file\n" | |
a6099ef3 | 1800 | "\nExamples:\n" |
9064d73b S |
1801 | + HelpExampleCli("backupwallet", "\"backupdata\"") |
1802 | + HelpExampleRpc("backupwallet", "\"backupdata\"") | |
a6099ef3 | 1803 | ); |
e3bc5698 | 1804 | |
4401b2d7 EL |
1805 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1806 | ||
9064d73b S |
1807 | boost::filesystem::path exportdir; |
1808 | try { | |
1809 | exportdir = GetExportDir(); | |
1810 | } catch (const std::runtime_error& e) { | |
1811 | throw JSONRPCError(RPC_INTERNAL_ERROR, e.what()); | |
1812 | } | |
1813 | if (exportdir.empty()) { | |
1814 | throw JSONRPCError(RPC_WALLET_ERROR, "Cannot backup wallet until the -exportdir option has been set"); | |
1815 | } | |
1816 | std::string unclean = params[0].get_str(); | |
1817 | std::string clean = SanitizeFilename(unclean); | |
1818 | if (clean.compare(unclean) != 0) { | |
1819 | throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Filename is invalid as only alphanumeric characters are allowed. Try '%s' instead.", clean)); | |
1820 | } | |
1821 | boost::filesystem::path exportfilepath = exportdir / clean; | |
1822 | ||
1823 | if (!BackupWallet(*pwalletMain, exportfilepath.string())) | |
ad525e9c | 1824 | throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); |
e3bc5698 | 1825 | |
9064d73b | 1826 | return exportfilepath.string(); |
e3bc5698 JG |
1827 | } |
1828 | ||
1829 | ||
d014114d | 1830 | UniValue keypoolrefill(const UniValue& params, bool fHelp) |
e3bc5698 | 1831 | { |
b9fb692d | 1832 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1833 | return NullUniValue; |
70454796 | 1834 | |
36bd46f1 | 1835 | if (fHelp || params.size() > 1) |
e3bc5698 | 1836 | throw runtime_error( |
a6099ef3 | 1837 | "keypoolrefill ( newsize )\n" |
1838 | "\nFills the keypool." | |
1839 | + HelpRequiringPassphrase() + "\n" | |
1840 | "\nArguments\n" | |
1841 | "1. newsize (numeric, optional, default=100) The new keypool size\n" | |
1842 | "\nExamples:\n" | |
1843 | + HelpExampleCli("keypoolrefill", "") | |
1844 | + HelpExampleRpc("keypoolrefill", "") | |
1845 | ); | |
e3bc5698 | 1846 | |
4401b2d7 EL |
1847 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1848 | ||
f914c7a1 PK |
1849 | // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool |
1850 | unsigned int kpSize = 0; | |
36bd46f1 JG |
1851 | if (params.size() > 0) { |
1852 | if (params[0].get_int() < 0) | |
f914c7a1 PK |
1853 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size."); |
1854 | kpSize = (unsigned int)params[0].get_int(); | |
36bd46f1 JG |
1855 | } |
1856 | ||
e3bc5698 | 1857 | EnsureWalletIsUnlocked(); |
36bd46f1 | 1858 | pwalletMain->TopUpKeyPool(kpSize); |
e3bc5698 | 1859 | |
36bd46f1 | 1860 | if (pwalletMain->GetKeyPoolSize() < kpSize) |
738835d7 | 1861 | throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); |
e3bc5698 | 1862 | |
ed21d5bd | 1863 | return NullUniValue; |
e3bc5698 JG |
1864 | } |
1865 | ||
1866 | ||
92f2c1fe | 1867 | static void LockWallet(CWallet* pWallet) |
e3bc5698 | 1868 | { |
92f2c1fe GA |
1869 | LOCK(cs_nWalletUnlockTime); |
1870 | nWalletUnlockTime = 0; | |
1871 | pWallet->Lock(); | |
e3bc5698 JG |
1872 | } |
1873 | ||
d014114d | 1874 | UniValue walletpassphrase(const UniValue& params, bool fHelp) |
e3bc5698 | 1875 | { |
b9fb692d | 1876 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1877 | return NullUniValue; |
70454796 | 1878 | |
e3bc5698 JG |
1879 | if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) |
1880 | throw runtime_error( | |
a6099ef3 | 1881 | "walletpassphrase \"passphrase\" timeout\n" |
1882 | "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" | |
c1652849 | 1883 | "This is needed prior to performing transactions related to private keys such as sending Zcash\n" |
a6099ef3 | 1884 | "\nArguments:\n" |
1885 | "1. \"passphrase\" (string, required) The wallet passphrase\n" | |
1886 | "2. timeout (numeric, required) The time to keep the decryption key in seconds.\n" | |
6c0db81c WL |
1887 | "\nNote:\n" |
1888 | "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n" | |
1889 | "time that overrides the old one.\n" | |
a6099ef3 | 1890 | "\nExamples:\n" |
1891 | "\nunlock the wallet for 60 seconds\n" | |
1892 | + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") + | |
1893 | "\nLock the wallet again (before 60 seconds)\n" | |
1894 | + HelpExampleCli("walletlock", "") + | |
1895 | "\nAs json rpc call\n" | |
1896 | + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60") | |
1897 | ); | |
1898 | ||
4401b2d7 EL |
1899 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1900 | ||
e3bc5698 JG |
1901 | if (fHelp) |
1902 | return true; | |
1903 | if (!pwalletMain->IsCrypted()) | |
738835d7 | 1904 | throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); |
e3bc5698 | 1905 | |
e3bc5698 JG |
1906 | // Note that the walletpassphrase is stored in params[0] which is not mlock()ed |
1907 | SecureString strWalletPass; | |
1908 | strWalletPass.reserve(100); | |
1909 | // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) | |
1910 | // Alternately, find a way to make params[0] mlock()'d to begin with. | |
1911 | strWalletPass = params[0].get_str().c_str(); | |
1912 | ||
1913 | if (strWalletPass.length() > 0) | |
1914 | { | |
1915 | if (!pwalletMain->Unlock(strWalletPass)) | |
738835d7 | 1916 | throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); |
e3bc5698 JG |
1917 | } |
1918 | else | |
1919 | throw runtime_error( | |
1920 | "walletpassphrase <passphrase> <timeout>\n" | |
1921 | "Stores the wallet decryption key in memory for <timeout> seconds."); | |
1922 | ||
6e263a5f | 1923 | // No need to check return values, because the wallet was unlocked above |
1a62587e | 1924 | pwalletMain->UpdateNullifierNoteMap(); |
92f2c1fe GA |
1925 | pwalletMain->TopUpKeyPool(); |
1926 | ||
51ed9ec9 | 1927 | int64_t nSleepTime = params[1].get_int64(); |
92f2c1fe GA |
1928 | LOCK(cs_nWalletUnlockTime); |
1929 | nWalletUnlockTime = GetTime() + nSleepTime; | |
1930 | RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime); | |
e3bc5698 | 1931 | |
ed21d5bd | 1932 | return NullUniValue; |
e3bc5698 JG |
1933 | } |
1934 | ||
1935 | ||
d014114d | 1936 | UniValue walletpassphrasechange(const UniValue& params, bool fHelp) |
e3bc5698 | 1937 | { |
b9fb692d | 1938 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1939 | return NullUniValue; |
70454796 | 1940 | |
e3bc5698 JG |
1941 | if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) |
1942 | throw runtime_error( | |
a6099ef3 | 1943 | "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n" |
1944 | "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n" | |
1945 | "\nArguments:\n" | |
1946 | "1. \"oldpassphrase\" (string) The current passphrase\n" | |
1947 | "2. \"newpassphrase\" (string) The new passphrase\n" | |
1948 | "\nExamples:\n" | |
1949 | + HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"") | |
1950 | + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"") | |
1951 | ); | |
1952 | ||
4401b2d7 EL |
1953 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1954 | ||
e3bc5698 JG |
1955 | if (fHelp) |
1956 | return true; | |
1957 | if (!pwalletMain->IsCrypted()) | |
738835d7 | 1958 | throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); |
e3bc5698 JG |
1959 | |
1960 | // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) | |
1961 | // Alternately, find a way to make params[0] mlock()'d to begin with. | |
1962 | SecureString strOldWalletPass; | |
1963 | strOldWalletPass.reserve(100); | |
1964 | strOldWalletPass = params[0].get_str().c_str(); | |
1965 | ||
1966 | SecureString strNewWalletPass; | |
1967 | strNewWalletPass.reserve(100); | |
1968 | strNewWalletPass = params[1].get_str().c_str(); | |
1969 | ||
1970 | if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) | |
1971 | throw runtime_error( | |
1972 | "walletpassphrasechange <oldpassphrase> <newpassphrase>\n" | |
1973 | "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>."); | |
1974 | ||
1975 | if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) | |
738835d7 | 1976 | throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); |
e3bc5698 | 1977 | |
ed21d5bd | 1978 | return NullUniValue; |
e3bc5698 JG |
1979 | } |
1980 | ||
1981 | ||
d014114d | 1982 | UniValue walletlock(const UniValue& params, bool fHelp) |
e3bc5698 | 1983 | { |
b9fb692d | 1984 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1985 | return NullUniValue; |
70454796 | 1986 | |
e3bc5698 JG |
1987 | if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0)) |
1988 | throw runtime_error( | |
1989 | "walletlock\n" | |
a6099ef3 | 1990 | "\nRemoves the wallet encryption key from memory, locking the wallet.\n" |
e3bc5698 | 1991 | "After calling this method, you will need to call walletpassphrase again\n" |
a6099ef3 | 1992 | "before being able to call any methods which require the wallet to be unlocked.\n" |
1993 | "\nExamples:\n" | |
1994 | "\nSet the passphrase for 2 minutes to perform a transaction\n" | |
1995 | + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") + | |
1996 | "\nPerform a send (requires passphrase set)\n" | |
70454796 | 1997 | + HelpExampleCli("sendtoaddress", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" 1.0") + |
a6099ef3 | 1998 | "\nClear the passphrase since we are done before 2 minutes is up\n" |
1999 | + HelpExampleCli("walletlock", "") + | |
2000 | "\nAs json rpc call\n" | |
2001 | + HelpExampleRpc("walletlock", "") | |
2002 | ); | |
2003 | ||
4401b2d7 EL |
2004 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2005 | ||
e3bc5698 JG |
2006 | if (fHelp) |
2007 | return true; | |
2008 | if (!pwalletMain->IsCrypted()) | |
738835d7 | 2009 | throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); |
e3bc5698 JG |
2010 | |
2011 | { | |
2012 | LOCK(cs_nWalletUnlockTime); | |
2013 | pwalletMain->Lock(); | |
2014 | nWalletUnlockTime = 0; | |
2015 | } | |
2016 | ||
ed21d5bd | 2017 | return NullUniValue; |
e3bc5698 JG |
2018 | } |
2019 | ||
2020 | ||
d014114d | 2021 | UniValue encryptwallet(const UniValue& params, bool fHelp) |
e3bc5698 | 2022 | { |
b9fb692d | 2023 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2024 | return NullUniValue; |
62c0aa9e | 2025 | |
b8eb3775 | 2026 | auto fEnableWalletEncryption = fExperimentalMode && GetBoolArg("-developerencryptwallet", false); |
62c0aa9e JG |
2027 | |
2028 | std::string strWalletEncryptionDisabledMsg = ""; | |
2029 | if (!fEnableWalletEncryption) { | |
1532cb75 | 2030 | strWalletEncryptionDisabledMsg = "\nWARNING: Wallet encryption is DISABLED. This call always fails.\n"; |
62c0aa9e JG |
2031 | } |
2032 | ||
e3bc5698 JG |
2033 | if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1)) |
2034 | throw runtime_error( | |
a6099ef3 | 2035 | "encryptwallet \"passphrase\"\n" |
62c0aa9e | 2036 | + strWalletEncryptionDisabledMsg + |
a6099ef3 | 2037 | "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n" |
2038 | "After this, any calls that interact with private keys such as sending or signing \n" | |
2039 | "will require the passphrase to be set prior the making these calls.\n" | |
2040 | "Use the walletpassphrase call for this, and then walletlock call.\n" | |
2041 | "If the wallet is already encrypted, use the walletpassphrasechange call.\n" | |
2042 | "Note that this will shutdown the server.\n" | |
2043 | "\nArguments:\n" | |
2044 | "1. \"passphrase\" (string) The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long.\n" | |
2045 | "\nExamples:\n" | |
2046 | "\nEncrypt you wallet\n" | |
2047 | + HelpExampleCli("encryptwallet", "\"my pass phrase\"") + | |
c1652849 | 2048 | "\nNow set the passphrase to use the wallet, such as for signing or sending Zcash\n" |
a6099ef3 | 2049 | + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") + |
2050 | "\nNow we can so something like sign\n" | |
58c4c0bb | 2051 | + HelpExampleCli("signmessage", "\"zcashaddress\" \"test message\"") + |
a6099ef3 | 2052 | "\nNow lock the wallet again by removing the passphrase\n" |
2053 | + HelpExampleCli("walletlock", "") + | |
2054 | "\nAs a json rpc call\n" | |
2055 | + HelpExampleRpc("encryptwallet", "\"my pass phrase\"") | |
2056 | ); | |
2057 | ||
4401b2d7 EL |
2058 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2059 | ||
e3bc5698 JG |
2060 | if (fHelp) |
2061 | return true; | |
62c0aa9e | 2062 | if (!fEnableWalletEncryption) { |
1532cb75 | 2063 | throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet encryption is disabled."); |
62c0aa9e | 2064 | } |
e3bc5698 | 2065 | if (pwalletMain->IsCrypted()) |
738835d7 | 2066 | throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); |
e3bc5698 JG |
2067 | |
2068 | // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) | |
2069 | // Alternately, find a way to make params[0] mlock()'d to begin with. | |
2070 | SecureString strWalletPass; | |
2071 | strWalletPass.reserve(100); | |
2072 | strWalletPass = params[0].get_str().c_str(); | |
2073 | ||
2074 | if (strWalletPass.length() < 1) | |
2075 | throw runtime_error( | |
2076 | "encryptwallet <passphrase>\n" | |
2077 | "Encrypts the wallet with <passphrase>."); | |
2078 | ||
2079 | if (!pwalletMain->EncryptWallet(strWalletPass)) | |
738835d7 | 2080 | throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet."); |
e3bc5698 JG |
2081 | |
2082 | // BDB seems to have a bad habit of writing old data into | |
2083 | // slack space in .dat files; that is bad if the old data is | |
331544bc | 2084 | // unencrypted private keys. So: |
e3bc5698 | 2085 | StartShutdown(); |
58c4c0bb | 2086 | 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 |
2087 | } |
2088 | ||
d014114d | 2089 | UniValue lockunspent(const UniValue& params, bool fHelp) |
fdbb537d | 2090 | { |
b9fb692d | 2091 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2092 | return NullUniValue; |
70454796 | 2093 | |
fdbb537d JG |
2094 | if (fHelp || params.size() < 1 || params.size() > 2) |
2095 | throw runtime_error( | |
a6099ef3 | 2096 | "lockunspent unlock [{\"txid\":\"txid\",\"vout\":n},...]\n" |
2097 | "\nUpdates list of temporarily unspendable outputs.\n" | |
90fd8737 | 2098 | "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n" |
c1652849 | 2099 | "A locked transaction output will not be chosen by automatic coin selection, when spending Zcash.\n" |
a6099ef3 | 2100 | "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n" |
2101 | "is always cleared (by virtue of process exit) when a node stops or fails.\n" | |
2102 | "Also see the listunspent call\n" | |
2103 | "\nArguments:\n" | |
2104 | "1. unlock (boolean, required) Whether to unlock (true) or lock (false) the specified transactions\n" | |
2105 | "2. \"transactions\" (string, required) A json array of objects. Each object the txid (string) vout (numeric)\n" | |
2106 | " [ (json array of json objects)\n" | |
2107 | " {\n" | |
2108 | " \"txid\":\"id\", (string) The transaction id\n" | |
2109 | " \"vout\": n (numeric) The output number\n" | |
2110 | " }\n" | |
2111 | " ,...\n" | |
2112 | " ]\n" | |
2113 | ||
2114 | "\nResult:\n" | |
2115 | "true|false (boolean) Whether the command was successful or not\n" | |
2116 | ||
2117 | "\nExamples:\n" | |
2118 | "\nList the unspent transactions\n" | |
2119 | + HelpExampleCli("listunspent", "") + | |
2120 | "\nLock an unspent transaction\n" | |
2121 | + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + | |
2122 | "\nList the locked transactions\n" | |
2123 | + HelpExampleCli("listlockunspent", "") + | |
2124 | "\nUnlock the transaction again\n" | |
2125 | + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + | |
2126 | "\nAs a json rpc call\n" | |
2127 | + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") | |
2128 | ); | |
fdbb537d | 2129 | |
4401b2d7 EL |
2130 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2131 | ||
fdbb537d | 2132 | if (params.size() == 1) |
ed21d5bd | 2133 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)); |
fdbb537d | 2134 | else |
ed21d5bd | 2135 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)(UniValue::VARR)); |
fdbb537d JG |
2136 | |
2137 | bool fUnlock = params[0].get_bool(); | |
2138 | ||
2139 | if (params.size() == 1) { | |
2140 | if (fUnlock) | |
2141 | pwalletMain->UnlockAllCoins(); | |
2142 | return true; | |
2143 | } | |
2144 | ||
851f58f9 | 2145 | UniValue outputs = params[1].get_array(); |
cc71666a | 2146 | for (size_t idx = 0; idx < outputs.size(); idx++) { |
ed21d5bd JG |
2147 | const UniValue& output = outputs[idx]; |
2148 | if (!output.isObject()) | |
15117692 | 2149 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); |
d014114d | 2150 | const UniValue& o = output.get_obj(); |
fdbb537d | 2151 | |
ed21d5bd | 2152 | RPCTypeCheckObj(o, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)); |
fdbb537d JG |
2153 | |
2154 | string txid = find_value(o, "txid").get_str(); | |
2155 | if (!IsHex(txid)) | |
15117692 | 2156 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); |
fdbb537d JG |
2157 | |
2158 | int nOutput = find_value(o, "vout").get_int(); | |
2159 | if (nOutput < 0) | |
15117692 | 2160 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); |
fdbb537d | 2161 | |
34cdc411 | 2162 | COutPoint outpt(uint256S(txid), nOutput); |
fdbb537d JG |
2163 | |
2164 | if (fUnlock) | |
2165 | pwalletMain->UnlockCoin(outpt); | |
2166 | else | |
2167 | pwalletMain->LockCoin(outpt); | |
2168 | } | |
2169 | ||
2170 | return true; | |
2171 | } | |
2172 | ||
d014114d | 2173 | UniValue listlockunspent(const UniValue& params, bool fHelp) |
fdbb537d | 2174 | { |
b9fb692d | 2175 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2176 | return NullUniValue; |
70454796 | 2177 | |
fdbb537d JG |
2178 | if (fHelp || params.size() > 0) |
2179 | throw runtime_error( | |
2180 | "listlockunspent\n" | |
a6099ef3 | 2181 | "\nReturns list of temporarily unspendable outputs.\n" |
2182 | "See the lockunspent call to lock and unlock transactions for spending.\n" | |
2183 | "\nResult:\n" | |
2184 | "[\n" | |
2185 | " {\n" | |
2186 | " \"txid\" : \"transactionid\", (string) The transaction id locked\n" | |
2187 | " \"vout\" : n (numeric) The vout value\n" | |
2188 | " }\n" | |
2189 | " ,...\n" | |
2190 | "]\n" | |
2191 | "\nExamples:\n" | |
2192 | "\nList the unspent transactions\n" | |
2193 | + HelpExampleCli("listunspent", "") + | |
2194 | "\nLock an unspent transaction\n" | |
2195 | + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + | |
2196 | "\nList the locked transactions\n" | |
2197 | + HelpExampleCli("listlockunspent", "") + | |
2198 | "\nUnlock the transaction again\n" | |
2199 | + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + | |
2200 | "\nAs a json rpc call\n" | |
2201 | + HelpExampleRpc("listlockunspent", "") | |
2202 | ); | |
fdbb537d | 2203 | |
4401b2d7 EL |
2204 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2205 | ||
fdbb537d JG |
2206 | vector<COutPoint> vOutpts; |
2207 | pwalletMain->ListLockedCoins(vOutpts); | |
2208 | ||
38fc4b70 | 2209 | UniValue ret(UniValue::VARR); |
fdbb537d JG |
2210 | |
2211 | BOOST_FOREACH(COutPoint &outpt, vOutpts) { | |
38fc4b70 | 2212 | UniValue o(UniValue::VOBJ); |
fdbb537d JG |
2213 | |
2214 | o.push_back(Pair("txid", outpt.hash.GetHex())); | |
2215 | o.push_back(Pair("vout", (int)outpt.n)); | |
2216 | ret.push_back(o); | |
2217 | } | |
2218 | ||
2219 | return ret; | |
2220 | } | |
2221 | ||
d014114d | 2222 | UniValue settxfee(const UniValue& params, bool fHelp) |
a943bde6 | 2223 | { |
b9fb692d | 2224 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2225 | return NullUniValue; |
70454796 | 2226 | |
a943bde6 WL |
2227 | if (fHelp || params.size() < 1 || params.size() > 1) |
2228 | throw runtime_error( | |
2229 | "settxfee amount\n" | |
6943cb9b | 2230 | "\nSet the transaction fee per kB.\n" |
a943bde6 | 2231 | "\nArguments:\n" |
091b2116 | 2232 | "1. amount (numeric, required) The transaction fee in " + CURRENCY_UNIT + "/kB rounded to the nearest 0.00000001\n" |
a943bde6 WL |
2233 | "\nResult\n" |
2234 | "true|false (boolean) Returns true if successful\n" | |
2235 | "\nExamples:\n" | |
2236 | + HelpExampleCli("settxfee", "0.00001") | |
2237 | + HelpExampleRpc("settxfee", "0.00001") | |
2238 | ); | |
2239 | ||
4401b2d7 EL |
2240 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2241 | ||
a943bde6 | 2242 | // Amount |
e76a3849 | 2243 | CAmount nAmount = AmountFromValue(params[0]); |
a943bde6 | 2244 | |
c6cb21d1 | 2245 | payTxFee = CFeeRate(nAmount, 1000); |
a943bde6 WL |
2246 | return true; |
2247 | } | |
2248 | ||
d014114d | 2249 | UniValue getwalletinfo(const UniValue& params, bool fHelp) |
a00ebb51 | 2250 | { |
b9fb692d | 2251 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2252 | return NullUniValue; |
70454796 | 2253 | |
a00ebb51 DN |
2254 | if (fHelp || params.size() != 0) |
2255 | throw runtime_error( | |
2256 | "getwalletinfo\n" | |
2257 | "Returns an object containing various wallet state info.\n" | |
2258 | "\nResult:\n" | |
2259 | "{\n" | |
2260 | " \"walletversion\": xxxxx, (numeric) the wallet version\n" | |
091b2116 RN |
2261 | " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + CURRENCY_UNIT + "\n" |
2262 | " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + CURRENCY_UNIT + "\n" | |
2263 | " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + CURRENCY_UNIT + "\n" | |
a00ebb51 DN |
2264 | " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n" |
2265 | " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" | |
2266 | " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" | |
2267 | " \"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" | |
091b2116 | 2268 | " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in " + CURRENCY_UNIT + "/kB\n" |
a00ebb51 DN |
2269 | "}\n" |
2270 | "\nExamples:\n" | |
2271 | + HelpExampleCli("getwalletinfo", "") | |
2272 | + HelpExampleRpc("getwalletinfo", "") | |
2273 | ); | |
2274 | ||
4401b2d7 EL |
2275 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2276 | ||
38fc4b70 | 2277 | UniValue obj(UniValue::VOBJ); |
a00ebb51 DN |
2278 | obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); |
2279 | obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); | |
8024d67d GM |
2280 | obj.push_back(Pair("unconfirmed_balance", ValueFromAmount(pwalletMain->GetUnconfirmedBalance()))); |
2281 | obj.push_back(Pair("immature_balance", ValueFromAmount(pwalletMain->GetImmatureBalance()))); | |
a00ebb51 | 2282 | obj.push_back(Pair("txcount", (int)pwalletMain->mapWallet.size())); |
d56e30ca | 2283 | obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); |
a00ebb51 DN |
2284 | obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); |
2285 | if (pwalletMain->IsCrypted()) | |
d56e30ca | 2286 | obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); |
6699b425 | 2287 | obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); |
a00ebb51 DN |
2288 | return obj; |
2289 | } | |
0f5954c4 | 2290 | |
d014114d | 2291 | UniValue resendwallettransactions(const UniValue& params, bool fHelp) |
0f5954c4 | 2292 | { |
b9fb692d | 2293 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2294 | return NullUniValue; |
70454796 | 2295 | |
0f5954c4 GA |
2296 | if (fHelp || params.size() != 0) |
2297 | throw runtime_error( | |
2298 | "resendwallettransactions\n" | |
2299 | "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n" | |
2300 | "Intended only for testing; the wallet code periodically re-broadcasts\n" | |
2301 | "automatically.\n" | |
2302 | "Returns array of transaction ids that were re-broadcast.\n" | |
2303 | ); | |
2304 | ||
2305 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
2306 | ||
2307 | std::vector<uint256> txids = pwalletMain->ResendWalletTransactionsBefore(GetTime()); | |
38fc4b70 | 2308 | UniValue result(UniValue::VARR); |
0f5954c4 GA |
2309 | BOOST_FOREACH(const uint256& txid, txids) |
2310 | { | |
2311 | result.push_back(txid.ToString()); | |
2312 | } | |
2313 | return result; | |
2314 | } | |
0b9dc9c8 | 2315 | |
d014114d | 2316 | UniValue listunspent(const UniValue& params, bool fHelp) |
0b9dc9c8 JS |
2317 | { |
2318 | if (!EnsureWalletIsAvailable(fHelp)) | |
9756b7bd | 2319 | return NullUniValue; |
70454796 | 2320 | |
0b9dc9c8 | 2321 | if (fHelp || params.size() > 3) |
ea9e82df JS |
2322 | throw runtime_error( |
2323 | "listunspent ( minconf maxconf [\"address\",...] )\n" | |
2324 | "\nReturns array of unspent transaction outputs\n" | |
2325 | "with between minconf and maxconf (inclusive) confirmations.\n" | |
2326 | "Optionally filter to only include txouts paid to specified addresses.\n" | |
2327 | "Results are an array of Objects, each of which has:\n" | |
2328 | "{txid, vout, scriptPubKey, amount, confirmations}\n" | |
2329 | "\nArguments:\n" | |
2330 | "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" | |
2331 | "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" | |
d2c1e4a8 | 2332 | "3. \"addresses\" (string) A json array of Zcash addresses to filter\n" |
ea9e82df | 2333 | " [\n" |
d2c1e4a8 | 2334 | " \"address\" (string) Zcash address\n" |
ea9e82df JS |
2335 | " ,...\n" |
2336 | " ]\n" | |
2337 | "\nResult\n" | |
2338 | "[ (array of json object)\n" | |
2339 | " {\n" | |
2340 | " \"txid\" : \"txid\", (string) the transaction id \n" | |
2341 | " \"vout\" : n, (numeric) the vout value\n" | |
d77a0ac4 | 2342 | " \"generated\" : true|false (boolean) true if txout is a coinbase transaction output\n" |
58c4c0bb | 2343 | " \"address\" : \"address\", (string) the zcash address\n" |
ea9e82df JS |
2344 | " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" |
2345 | " \"scriptPubKey\" : \"key\", (string) the script key\n" | |
091b2116 | 2346 | " \"amount\" : x.xxx, (numeric) the transaction amount in " + CURRENCY_UNIT + "\n" |
ea9e82df JS |
2347 | " \"confirmations\" : n (numeric) The number of confirmations\n" |
2348 | " }\n" | |
2349 | " ,...\n" | |
2350 | "]\n" | |
2351 | ||
2352 | "\nExamples\n" | |
2353 | + HelpExampleCli("listunspent", "") | |
70454796 JG |
2354 | + HelpExampleCli("listunspent", "6 9999999 \"[\\\"t1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"t1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") |
2355 | + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"t1PGFqEzfmQch1gKD3ra4k18PNj3tTUUSqg\\\",\\\"t1LtvqCaApEdUGFkpKMM4MstjcaL4dKg8SP\\\"]\"") | |
ea9e82df JS |
2356 | ); |
2357 | ||
9756b7bd | 2358 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR)); |
ea9e82df | 2359 | |
0b9dc9c8 JS |
2360 | int nMinDepth = 1; |
2361 | if (params.size() > 0) | |
ea9e82df JS |
2362 | nMinDepth = params[0].get_int(); |
2363 | ||
0b9dc9c8 JS |
2364 | int nMaxDepth = 9999999; |
2365 | if (params.size() > 1) | |
ea9e82df JS |
2366 | nMaxDepth = params[1].get_int(); |
2367 | ||
0b9dc9c8 JS |
2368 | set<CBitcoinAddress> setAddress; |
2369 | if (params.size() > 2) { | |
851f58f9 | 2370 | UniValue inputs = params[2].get_array(); |
cc71666a | 2371 | for (size_t idx = 0; idx < inputs.size(); idx++) { |
d014114d | 2372 | const UniValue& input = inputs[idx]; |
0b9dc9c8 JS |
2373 | CBitcoinAddress address(input.get_str()); |
2374 | if (!address.IsValid()) | |
58c4c0bb | 2375 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Zcash address: ")+input.get_str()); |
0b9dc9c8 | 2376 | if (setAddress.count(address)) |
ea9e82df JS |
2377 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str()); |
2378 | setAddress.insert(address); | |
0b9dc9c8 JS |
2379 | } |
2380 | } | |
ea9e82df | 2381 | |
38fc4b70 | 2382 | UniValue results(UniValue::VARR); |
0b9dc9c8 JS |
2383 | vector<COutput> vecOutputs; |
2384 | assert(pwalletMain != NULL); | |
2385 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
219953ce | 2386 | pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); |
0b9dc9c8 JS |
2387 | BOOST_FOREACH(const COutput& out, vecOutputs) { |
2388 | if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) | |
ea9e82df JS |
2389 | continue; |
2390 | ||
0b9dc9c8 JS |
2391 | if (setAddress.size()) { |
2392 | CTxDestination address; | |
2393 | if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) | |
ea9e82df JS |
2394 | continue; |
2395 | ||
0b9dc9c8 | 2396 | if (!setAddress.count(address)) |
ea9e82df | 2397 | continue; |
0b9dc9c8 | 2398 | } |
ea9e82df | 2399 | |
0b9dc9c8 JS |
2400 | CAmount nValue = out.tx->vout[out.i].nValue; |
2401 | const CScript& pk = out.tx->vout[out.i].scriptPubKey; | |
38fc4b70 | 2402 | UniValue entry(UniValue::VOBJ); |
805344dc | 2403 | entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); |
0b9dc9c8 | 2404 | entry.push_back(Pair("vout", out.i)); |
d77a0ac4 | 2405 | entry.push_back(Pair("generated", out.tx->IsCoinBase())); |
0b9dc9c8 JS |
2406 | CTxDestination address; |
2407 | if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { | |
2408 | entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); | |
2409 | if (pwalletMain->mapAddressBook.count(address)) | |
ea9e82df | 2410 | entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); |
0b9dc9c8 JS |
2411 | } |
2412 | entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); | |
2413 | if (pk.IsPayToScriptHash()) { | |
2414 | CTxDestination address; | |
2415 | if (ExtractDestination(pk, address)) { | |
8b08d953 | 2416 | const CScriptID& hash = boost::get<CScriptID>(address); |
0b9dc9c8 JS |
2417 | CScript redeemScript; |
2418 | if (pwalletMain->GetCScript(hash, redeemScript)) | |
ea9e82df | 2419 | entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); |
0b9dc9c8 JS |
2420 | } |
2421 | } | |
2422 | entry.push_back(Pair("amount",ValueFromAmount(nValue))); | |
2423 | entry.push_back(Pair("confirmations",out.nDepth)); | |
2424 | entry.push_back(Pair("spendable", out.fSpendable)); | |
2425 | results.push_back(entry); | |
2426 | } | |
ea9e82df | 2427 | |
0b9dc9c8 | 2428 | return results; |
c9fd9078 | 2429 | } |
730790f7 | 2430 | |
3d8013a0 MC |
2431 | UniValue fundrawtransaction(const UniValue& params, bool fHelp) |
2432 | { | |
2433 | if (!EnsureWalletIsAvailable(fHelp)) | |
2434 | return NullUniValue; | |
2435 | ||
2436 | if (fHelp || params.size() != 1) | |
2437 | throw runtime_error( | |
2438 | "fundrawtransaction \"hexstring\"\n" | |
2439 | "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n" | |
2440 | "This will not modify existing inputs, and will add one change output to the outputs.\n" | |
2441 | "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n" | |
2442 | "The inputs added will not be signed, use signrawtransaction for that.\n" | |
2443 | "\nArguments:\n" | |
2444 | "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" | |
2445 | "\nResult:\n" | |
2446 | "{\n" | |
2447 | " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n" | |
2448 | " \"fee\": n, (numeric) The fee added to the transaction\n" | |
2449 | " \"changepos\": n (numeric) The position of the added change output, or -1\n" | |
2450 | "}\n" | |
2451 | "\"hex\" \n" | |
2452 | "\nExamples:\n" | |
2453 | "\nCreate a transaction with no inputs\n" | |
2454 | + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") + | |
2455 | "\nAdd sufficient unsigned inputs to meet the output value\n" | |
2456 | + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") + | |
2457 | "\nSign the transaction\n" | |
2458 | + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") + | |
2459 | "\nSend the transaction\n" | |
2460 | + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") | |
2461 | ); | |
2462 | ||
2463 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); | |
2464 | ||
2465 | // parse hex string from parameter | |
2466 | CTransaction origTx; | |
2467 | if (!DecodeHexTx(origTx, params[0].get_str())) | |
2468 | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); | |
2469 | ||
2470 | CMutableTransaction tx(origTx); | |
2471 | CAmount nFee; | |
2472 | string strFailReason; | |
2473 | int nChangePos = -1; | |
2474 | if(!pwalletMain->FundTransaction(tx, nFee, nChangePos, strFailReason)) | |
2475 | throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); | |
2476 | ||
2477 | UniValue result(UniValue::VOBJ); | |
2478 | result.push_back(Pair("hex", EncodeHexTx(tx))); | |
2479 | result.push_back(Pair("changepos", nChangePos)); | |
2480 | result.push_back(Pair("fee", ValueFromAmount(nFee))); | |
2481 | ||
2482 | return result; | |
2483 | } | |
2484 | ||
0d37ae3a | 2485 | UniValue zc_sample_joinsplit(const UniValue& params, bool fHelp) |
1737627c SB |
2486 | { |
2487 | if (fHelp) { | |
2488 | throw runtime_error( | |
2489 | "zcsamplejoinsplit\n" | |
2490 | "\n" | |
2491 | "Perform a joinsplit and return the JSDescription.\n" | |
2492 | ); | |
2493 | } | |
2494 | ||
2495 | LOCK(cs_main); | |
2496 | ||
2497 | uint256 pubKeyHash; | |
2498 | uint256 anchor = ZCIncrementalMerkleTree().root(); | |
2499 | JSDescription samplejoinsplit(*pzcashParams, | |
2500 | pubKeyHash, | |
2501 | anchor, | |
2502 | {JSInput(), JSInput()}, | |
2503 | {JSOutput(), JSOutput()}, | |
2504 | 0, | |
2505 | 0); | |
2506 | ||
2507 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
2508 | ss << samplejoinsplit; | |
2509 | ||
2510 | return HexStr(ss.begin(), ss.end()); | |
2511 | } | |
2512 | ||
0d37ae3a | 2513 | UniValue zc_benchmark(const UniValue& params, bool fHelp) |
6962bb3d TH |
2514 | { |
2515 | if (!EnsureWalletIsAvailable(fHelp)) { | |
0d37ae3a | 2516 | return NullUniValue; |
6962bb3d TH |
2517 | } |
2518 | ||
2519 | if (fHelp || params.size() < 2) { | |
2520 | throw runtime_error( | |
2521 | "zcbenchmark benchmarktype samplecount\n" | |
2522 | "\n" | |
2523 | "Runs a benchmark of the selected type samplecount times,\n" | |
2524 | "returning the running times of each sample.\n" | |
2525 | "\n" | |
2526 | "Output: [\n" | |
2527 | " {\n" | |
2528 | " \"runningtime\": runningtime\n" | |
2529 | " },\n" | |
2530 | " {\n" | |
2531 | " \"runningtime\": runningtime\n" | |
2532 | " }\n" | |
2533 | " ...\n" | |
2534 | "]\n" | |
2535 | ); | |
2536 | } | |
2537 | ||
2538 | LOCK(cs_main); | |
2539 | ||
2540 | std::string benchmarktype = params[0].get_str(); | |
2541 | int samplecount = params[1].get_int(); | |
2542 | ||
2543 | if (samplecount <= 0) { | |
2544 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid samplecount"); | |
2545 | } | |
2546 | ||
2547 | std::vector<double> sample_times; | |
2548 | ||
1737627c | 2549 | JSDescription samplejoinsplit; |
2fbbde59 SB |
2550 | |
2551 | if (benchmarktype == "verifyjoinsplit") { | |
1737627c SB |
2552 | CDataStream ss(ParseHexV(params[2].get_str(), "js"), SER_NETWORK, PROTOCOL_VERSION); |
2553 | ss >> samplejoinsplit; | |
2fbbde59 SB |
2554 | } |
2555 | ||
6962bb3d TH |
2556 | for (int i = 0; i < samplecount; i++) { |
2557 | if (benchmarktype == "sleep") { | |
2558 | sample_times.push_back(benchmark_sleep()); | |
2559 | } else if (benchmarktype == "parameterloading") { | |
2560 | sample_times.push_back(benchmark_parameter_loading()); | |
2561 | } else if (benchmarktype == "createjoinsplit") { | |
4082dcb1 JG |
2562 | if (params.size() < 3) { |
2563 | sample_times.push_back(benchmark_create_joinsplit()); | |
2564 | } else { | |
2565 | int nThreads = params[2].get_int(); | |
2566 | std::vector<double> vals = benchmark_create_joinsplit_threaded(nThreads); | |
2567 | // Divide by nThreads^2 to get average seconds per JoinSplit because | |
2568 | // we are running one JoinSplit per thread. | |
2569 | sample_times.push_back(std::accumulate(vals.begin(), vals.end(), 0.0) / (nThreads*nThreads)); | |
2570 | } | |
6962bb3d | 2571 | } else if (benchmarktype == "verifyjoinsplit") { |
1737627c | 2572 | sample_times.push_back(benchmark_verify_joinsplit(samplejoinsplit)); |
2cc0a252 | 2573 | #ifdef ENABLE_MINING |
bf8def97 | 2574 | } else if (benchmarktype == "solveequihash") { |
f7478de6 | 2575 | if (params.size() < 3) { |
9e52ca32 | 2576 | sample_times.push_back(benchmark_solve_equihash()); |
f7478de6 JG |
2577 | } else { |
2578 | int nThreads = params[2].get_int(); | |
9e52ca32 JG |
2579 | std::vector<double> vals = benchmark_solve_equihash_threaded(nThreads); |
2580 | sample_times.insert(sample_times.end(), vals.begin(), vals.end()); | |
f7478de6 | 2581 | } |
2cc0a252 | 2582 | #endif |
bf8def97 | 2583 | } else if (benchmarktype == "verifyequihash") { |
a1cd1a27 | 2584 | sample_times.push_back(benchmark_verify_equihash()); |
f5edc37f | 2585 | } else if (benchmarktype == "validatelargetx") { |
818b94f9 JG |
2586 | // Number of inputs in the spending transaction that we will simulate |
2587 | int nInputs = 555; | |
2588 | if (params.size() >= 3) { | |
2589 | nInputs = params[2].get_int(); | |
2590 | } | |
2591 | sample_times.push_back(benchmark_large_tx(nInputs)); | |
0fbab55b | 2592 | } else if (benchmarktype == "trydecryptnotes") { |
88b7f3c2 JG |
2593 | int nAddrs = params[2].get_int(); |
2594 | sample_times.push_back(benchmark_try_decrypt_notes(nAddrs)); | |
0bb3d40f JG |
2595 | } else if (benchmarktype == "incnotewitnesses") { |
2596 | int nTxs = params[2].get_int(); | |
2597 | sample_times.push_back(benchmark_increment_note_witnesses(nTxs)); | |
c66c731a JG |
2598 | } else if (benchmarktype == "connectblockslow") { |
2599 | if (Params().NetworkIDString() != "regtest") { | |
2600 | throw JSONRPCError(RPC_TYPE_ERROR, "Benchmark must be run in regtest mode"); | |
2601 | } | |
2602 | sample_times.push_back(benchmark_connectblock_slow()); | |
a76174b7 JG |
2603 | } else if (benchmarktype == "sendtoaddress") { |
2604 | if (Params().NetworkIDString() != "regtest") { | |
2605 | throw JSONRPCError(RPC_TYPE_ERROR, "Benchmark must be run in regtest mode"); | |
2606 | } | |
2607 | auto amount = AmountFromValue(params[2]); | |
2608 | sample_times.push_back(benchmark_sendtoaddress(amount)); | |
2e8aefdc AG |
2609 | } else if (benchmarktype == "loadwallet") { |
2610 | if (Params().NetworkIDString() != "regtest") { | |
2611 | throw JSONRPCError(RPC_TYPE_ERROR, "Benchmark must be run in regtest mode"); | |
2612 | } | |
2613 | sample_times.push_back(benchmark_loadwallet()); | |
99dd50c3 JG |
2614 | } else if (benchmarktype == "listunspent") { |
2615 | sample_times.push_back(benchmark_listunspent()); | |
6962bb3d TH |
2616 | } else { |
2617 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid benchmarktype"); | |
2618 | } | |
2619 | } | |
2620 | ||
0d37ae3a | 2621 | UniValue results(UniValue::VARR); |
9e52ca32 | 2622 | for (auto time : sample_times) { |
0d37ae3a | 2623 | UniValue result(UniValue::VOBJ); |
9e52ca32 | 2624 | result.push_back(Pair("runningtime", time)); |
6962bb3d TH |
2625 | results.push_back(result); |
2626 | } | |
2627 | ||
2628 | return results; | |
2629 | } | |
2630 | ||
0d37ae3a | 2631 | UniValue zc_raw_receive(const UniValue& params, bool fHelp) |
a8ac403d | 2632 | { |
f15b9549 | 2633 | if (!EnsureWalletIsAvailable(fHelp)) { |
0d37ae3a | 2634 | return NullUniValue; |
f15b9549 NW |
2635 | } |
2636 | ||
2637 | if (fHelp || params.size() != 2) { | |
2638 | throw runtime_error( | |
eae37941 | 2639 | "zcrawreceive zcsecretkey encryptednote\n" |
f15b9549 | 2640 | "\n" |
ca0ec80b | 2641 | "DEPRECATED. Decrypts encryptednote and checks if the coin commitments\n" |
f15b9549 NW |
2642 | "are in the blockchain as indicated by the \"exists\" result.\n" |
2643 | "\n" | |
2644 | "Output: {\n" | |
2645 | " \"amount\": value,\n" | |
4bc00dc1 | 2646 | " \"note\": noteplaintext,\n" |
f15b9549 NW |
2647 | " \"exists\": exists\n" |
2648 | "}\n" | |
2649 | ); | |
2650 | } | |
2651 | ||
0d37ae3a | 2652 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VSTR)); |
f15b9549 | 2653 | |
a8ac403d SB |
2654 | LOCK(cs_main); |
2655 | ||
0d6864e4 SB |
2656 | CZCSpendingKey spendingkey(params[0].get_str()); |
2657 | SpendingKey k = spendingkey.Get(); | |
a8ac403d | 2658 | |
6c36a9fe SB |
2659 | uint256 epk; |
2660 | unsigned char nonce; | |
2661 | ZCNoteEncryption::Ciphertext ct; | |
2dc35992 | 2662 | uint256 h_sig; |
6c36a9fe SB |
2663 | |
2664 | { | |
4bc00dc1 | 2665 | CDataStream ssData(ParseHexV(params[1], "encrypted_note"), SER_NETWORK, PROTOCOL_VERSION); |
6c36a9fe SB |
2666 | try { |
2667 | ssData >> nonce; | |
2668 | ssData >> epk; | |
2669 | ssData >> ct; | |
2dc35992 | 2670 | ssData >> h_sig; |
6c36a9fe SB |
2671 | } catch(const std::exception &) { |
2672 | throw runtime_error( | |
4bc00dc1 | 2673 | "encrypted_note could not be decoded" |
6c36a9fe SB |
2674 | ); |
2675 | } | |
2676 | } | |
2677 | ||
642a1caf | 2678 | ZCNoteDecryption decryptor(k.receiving_key()); |
a8ac403d | 2679 | |
2dc35992 SB |
2680 | NotePlaintext npt = NotePlaintext::decrypt( |
2681 | decryptor, | |
2682 | ct, | |
2683 | epk, | |
2684 | h_sig, | |
2685 | nonce | |
2686 | ); | |
2687 | PaymentAddress payment_addr = k.address(); | |
2688 | Note decrypted_note = npt.note(payment_addr); | |
a8ac403d SB |
2689 | |
2690 | assert(pwalletMain != NULL); | |
2dc35992 | 2691 | std::vector<boost::optional<ZCIncrementalWitness>> witnesses; |
a8ac403d | 2692 | uint256 anchor; |
2dc35992 | 2693 | uint256 commitment = decrypted_note.cm(); |
4bc00dc1 | 2694 | pwalletMain->WitnessNoteCommitment( |
2dc35992 SB |
2695 | {commitment}, |
2696 | witnesses, | |
2697 | anchor | |
2698 | ); | |
a8ac403d SB |
2699 | |
2700 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
2dc35992 | 2701 | ss << npt; |
a8ac403d | 2702 | |
0d37ae3a | 2703 | UniValue result(UniValue::VOBJ); |
2dc35992 | 2704 | result.push_back(Pair("amount", ValueFromAmount(decrypted_note.value))); |
4bc00dc1 | 2705 | result.push_back(Pair("note", HexStr(ss.begin(), ss.end()))); |
2dc35992 | 2706 | result.push_back(Pair("exists", (bool) witnesses[0])); |
a8ac403d SB |
2707 | return result; |
2708 | } | |
2709 | ||
2dc35992 SB |
2710 | |
2711 | ||
0d37ae3a | 2712 | UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp) |
730790f7 | 2713 | { |
f15b9549 | 2714 | if (!EnsureWalletIsAvailable(fHelp)) { |
0d37ae3a | 2715 | return NullUniValue; |
f15b9549 NW |
2716 | } |
2717 | ||
2718 | if (fHelp || params.size() != 5) { | |
2719 | throw runtime_error( | |
eae37941 | 2720 | "zcrawjoinsplit rawtx inputs outputs vpub_old vpub_new\n" |
4bc00dc1 | 2721 | " inputs: a JSON object mapping {note: zcsecretkey, ...}\n" |
f15b9549 NW |
2722 | " outputs: a JSON object mapping {zcaddr: value, ...}\n" |
2723 | "\n" | |
ca0ec80b | 2724 | "DEPRECATED. Splices a joinsplit into rawtx. Inputs are unilaterally confidential.\n" |
f15b9549 NW |
2725 | "Outputs are confidential between sender/receiver. The vpub_old and\n" |
2726 | "vpub_new values are globally public and move transparent value into\n" | |
2727 | "or out of the confidential value store, respectively.\n" | |
2728 | "\n" | |
2729 | "Note: The caller is responsible for delivering the output enc1 and\n" | |
2730 | "enc2 to the appropriate recipients, as well as signing rawtxout and\n" | |
2731 | "ensuring it is mined. (A future RPC call will deliver the confidential\n" | |
2732 | "payments in-band on the blockchain.)\n" | |
2733 | "\n" | |
2734 | "Output: {\n" | |
4bc00dc1 DH |
2735 | " \"encryptednote1\": enc1,\n" |
2736 | " \"encryptednote2\": enc2,\n" | |
f15b9549 NW |
2737 | " \"rawtxn\": rawtxout\n" |
2738 | "}\n" | |
2739 | ); | |
2740 | } | |
2741 | ||
a8ac403d | 2742 | LOCK(cs_main); |
730790f7 SB |
2743 | |
2744 | CTransaction tx; | |
2745 | if (!DecodeHexTx(tx, params[0].get_str())) | |
2746 | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); | |
2747 | ||
0d37ae3a JG |
2748 | UniValue inputs = params[1].get_obj(); |
2749 | UniValue outputs = params[2].get_obj(); | |
730790f7 SB |
2750 | |
2751 | CAmount vpub_old(0); | |
2752 | CAmount vpub_new(0); | |
2753 | ||
2754 | if (params[3].get_real() != 0.0) | |
2755 | vpub_old = AmountFromValue(params[3]); | |
2756 | ||
2757 | if (params[4].get_real() != 0.0) | |
2758 | vpub_new = AmountFromValue(params[4]); | |
2759 | ||
b7e4abd6 SB |
2760 | std::vector<JSInput> vjsin; |
2761 | std::vector<JSOutput> vjsout; | |
2dc35992 SB |
2762 | std::vector<Note> notes; |
2763 | std::vector<SpendingKey> keys; | |
2764 | std::vector<uint256> commitments; | |
a8ac403d | 2765 | |
0d37ae3a JG |
2766 | for (const string& name_ : inputs.getKeys()) { |
2767 | CZCSpendingKey spendingkey(inputs[name_].get_str()); | |
0d6864e4 | 2768 | SpendingKey k = spendingkey.Get(); |
a8ac403d | 2769 | |
2dc35992 | 2770 | keys.push_back(k); |
a8ac403d | 2771 | |
2dc35992 | 2772 | NotePlaintext npt; |
a8ac403d | 2773 | |
2dc35992 | 2774 | { |
0d37ae3a | 2775 | CDataStream ssData(ParseHexV(name_, "note"), SER_NETWORK, PROTOCOL_VERSION); |
2dc35992 | 2776 | ssData >> npt; |
a8ac403d SB |
2777 | } |
2778 | ||
2dc35992 SB |
2779 | PaymentAddress addr = k.address(); |
2780 | Note note = npt.note(addr); | |
2781 | notes.push_back(note); | |
2782 | commitments.push_back(note.cm()); | |
2783 | } | |
2784 | ||
2785 | uint256 anchor; | |
2786 | std::vector<boost::optional<ZCIncrementalWitness>> witnesses; | |
4bc00dc1 | 2787 | pwalletMain->WitnessNoteCommitment(commitments, witnesses, anchor); |
2dc35992 SB |
2788 | |
2789 | assert(witnesses.size() == notes.size()); | |
2790 | assert(notes.size() == keys.size()); | |
2791 | ||
2792 | { | |
2793 | for (size_t i = 0; i < witnesses.size(); i++) { | |
2794 | if (!witnesses[i]) { | |
2795 | throw runtime_error( | |
b7e4abd6 | 2796 | "joinsplit input could not be found in tree" |
2dc35992 SB |
2797 | ); |
2798 | } | |
2799 | ||
b7e4abd6 | 2800 | vjsin.push_back(JSInput(*witnesses[i], notes[i], keys[i])); |
2dc35992 | 2801 | } |
730790f7 | 2802 | } |
730790f7 | 2803 | |
b7e4abd6 SB |
2804 | while (vjsin.size() < ZC_NUM_JS_INPUTS) { |
2805 | vjsin.push_back(JSInput()); | |
a8ac403d | 2806 | } |
730790f7 | 2807 | |
0d37ae3a JG |
2808 | for (const string& name_ : outputs.getKeys()) { |
2809 | CZCPaymentAddress pubaddr(name_); | |
e104fcdd | 2810 | PaymentAddress addrTo = pubaddr.Get(); |
0d37ae3a | 2811 | CAmount nAmount = AmountFromValue(outputs[name_]); |
730790f7 | 2812 | |
b7e4abd6 | 2813 | vjsout.push_back(JSOutput(addrTo, nAmount)); |
730790f7 SB |
2814 | } |
2815 | ||
b7e4abd6 SB |
2816 | while (vjsout.size() < ZC_NUM_JS_OUTPUTS) { |
2817 | vjsout.push_back(JSOutput()); | |
730790f7 SB |
2818 | } |
2819 | ||
2820 | // TODO | |
b7e4abd6 SB |
2821 | if (vjsout.size() != ZC_NUM_JS_INPUTS || vjsin.size() != ZC_NUM_JS_OUTPUTS) { |
2822 | throw runtime_error("unsupported joinsplit input/output counts"); | |
730790f7 SB |
2823 | } |
2824 | ||
320f2cc7 SB |
2825 | uint256 joinSplitPubKey; |
2826 | unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; | |
2827 | crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); | |
6aae9d1a TH |
2828 | |
2829 | CMutableTransaction mtx(tx); | |
2830 | mtx.nVersion = 2; | |
2831 | mtx.joinSplitPubKey = joinSplitPubKey; | |
2832 | ||
22de1602 SB |
2833 | JSDescription jsdesc(*pzcashParams, |
2834 | joinSplitPubKey, | |
2835 | anchor, | |
2836 | {vjsin[0], vjsin[1]}, | |
2837 | {vjsout[0], vjsout[1]}, | |
2838 | vpub_old, | |
2839 | vpub_new); | |
320f2cc7 | 2840 | |
bc59f537 SB |
2841 | { |
2842 | auto verifier = libzcash::ProofVerifier::Strict(); | |
2843 | assert(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey)); | |
2844 | } | |
730790f7 | 2845 | |
22de1602 | 2846 | mtx.vjoinsplit.push_back(jsdesc); |
730790f7 | 2847 | |
6aae9d1a TH |
2848 | // Empty output script. |
2849 | CScript scriptCode; | |
2850 | CTransaction signTx(mtx); | |
be126699 JG |
2851 | auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); |
2852 | uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); | |
6aae9d1a TH |
2853 | |
2854 | // Add the signature | |
320f2cc7 SB |
2855 | assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, |
2856 | dataToBeSigned.begin(), 32, | |
2857 | joinSplitPrivKey | |
2858 | ) == 0); | |
2859 | ||
2860 | // Sanity check | |
2861 | assert(crypto_sign_verify_detached(&mtx.joinSplitSig[0], | |
2862 | dataToBeSigned.begin(), 32, | |
2863 | mtx.joinSplitPubKey.begin() | |
2864 | ) == 0); | |
6aae9d1a | 2865 | |
730790f7 SB |
2866 | CTransaction rawTx(mtx); |
2867 | ||
2868 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
2869 | ss << rawTx; | |
2870 | ||
4bc00dc1 DH |
2871 | std::string encryptedNote1; |
2872 | std::string encryptedNote2; | |
6c36a9fe SB |
2873 | { |
2874 | CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); | |
2875 | ss2 << ((unsigned char) 0x00); | |
22de1602 SB |
2876 | ss2 << jsdesc.ephemeralKey; |
2877 | ss2 << jsdesc.ciphertexts[0]; | |
2878 | ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey); | |
6c36a9fe | 2879 | |
4bc00dc1 | 2880 | encryptedNote1 = HexStr(ss2.begin(), ss2.end()); |
6c36a9fe SB |
2881 | } |
2882 | { | |
2883 | CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); | |
2884 | ss2 << ((unsigned char) 0x01); | |
22de1602 SB |
2885 | ss2 << jsdesc.ephemeralKey; |
2886 | ss2 << jsdesc.ciphertexts[1]; | |
2887 | ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey); | |
6c36a9fe | 2888 | |
4bc00dc1 | 2889 | encryptedNote2 = HexStr(ss2.begin(), ss2.end()); |
6c36a9fe SB |
2890 | } |
2891 | ||
0d37ae3a | 2892 | UniValue result(UniValue::VOBJ); |
4bc00dc1 DH |
2893 | result.push_back(Pair("encryptednote1", encryptedNote1)); |
2894 | result.push_back(Pair("encryptednote2", encryptedNote2)); | |
730790f7 SB |
2895 | result.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end()))); |
2896 | return result; | |
2897 | } | |
2898 | ||
0d37ae3a | 2899 | UniValue zc_raw_keygen(const UniValue& params, bool fHelp) |
730790f7 | 2900 | { |
f15b9549 | 2901 | if (!EnsureWalletIsAvailable(fHelp)) { |
0d37ae3a | 2902 | return NullUniValue; |
f15b9549 NW |
2903 | } |
2904 | ||
2905 | if (fHelp || params.size() != 0) { | |
2906 | throw runtime_error( | |
eae37941 | 2907 | "zcrawkeygen\n" |
f15b9549 | 2908 | "\n" |
ca0ec80b | 2909 | "DEPRECATED. Generate a zcaddr which can send and receive confidential values.\n" |
f15b9549 NW |
2910 | "\n" |
2911 | "Output: {\n" | |
2912 | " \"zcaddress\": zcaddr,\n" | |
2913 | " \"zcsecretkey\": zcsecretkey,\n" | |
7b8d4f87 | 2914 | " \"zcviewingkey\": zcviewingkey,\n" |
f15b9549 NW |
2915 | "}\n" |
2916 | ); | |
2917 | } | |
2918 | ||
2dc35992 SB |
2919 | auto k = SpendingKey::random(); |
2920 | auto addr = k.address(); | |
2921 | auto viewing_key = k.viewing_key(); | |
730790f7 | 2922 | |
e104fcdd | 2923 | CZCPaymentAddress pubaddr(addr); |
0d6864e4 | 2924 | CZCSpendingKey spendingkey(k); |
7b8d4f87 | 2925 | CZCViewingKey viewingkey(viewing_key); |
730790f7 | 2926 | |
0d37ae3a | 2927 | UniValue result(UniValue::VOBJ); |
e104fcdd | 2928 | result.push_back(Pair("zcaddress", pubaddr.ToString())); |
0d6864e4 | 2929 | result.push_back(Pair("zcsecretkey", spendingkey.ToString())); |
7b8d4f87 | 2930 | result.push_back(Pair("zcviewingkey", viewingkey.ToString())); |
730790f7 | 2931 | return result; |
f15b9549 | 2932 | } |
c1c45943 S |
2933 | |
2934 | ||
0d37ae3a | 2935 | UniValue z_getnewaddress(const UniValue& params, bool fHelp) |
c1c45943 S |
2936 | { |
2937 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 2938 | return NullUniValue; |
c1c45943 | 2939 | |
49e591eb | 2940 | if (fHelp || params.size() > 0) |
c1c45943 S |
2941 | throw runtime_error( |
2942 | "z_getnewaddress\n" | |
2943 | "\nReturns a new zaddr for receiving payments.\n" | |
2944 | "\nArguments:\n" | |
2945 | "\nResult:\n" | |
2946 | "\"zcashaddress\" (string) The new zaddr\n" | |
2947 | "\nExamples:\n" | |
2948 | + HelpExampleCli("z_getnewaddress", "") | |
2949 | + HelpExampleRpc("z_getnewaddress", "") | |
2950 | ); | |
2951 | ||
2952 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
2953 | ||
73699cea S |
2954 | EnsureWalletIsUnlocked(); |
2955 | ||
c1c45943 S |
2956 | CZCPaymentAddress pubaddr = pwalletMain->GenerateNewZKey(); |
2957 | std::string result = pubaddr.ToString(); | |
2958 | return result; | |
2959 | } | |
2960 | ||
e709997f | 2961 | |
0d37ae3a | 2962 | UniValue z_listaddresses(const UniValue& params, bool fHelp) |
e709997f S |
2963 | { |
2964 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 2965 | return NullUniValue; |
e709997f S |
2966 | |
2967 | if (fHelp || params.size() > 1) | |
2968 | throw runtime_error( | |
44e37656 | 2969 | "z_listaddresses ( includeWatchonly )\n" |
e709997f S |
2970 | "\nReturns the list of zaddr belonging to the wallet.\n" |
2971 | "\nArguments:\n" | |
44e37656 | 2972 | "1. includeWatchonly (bool, optional, default=false) Also include watchonly addresses (see 'z_importviewingkey')\n" |
e709997f S |
2973 | "\nResult:\n" |
2974 | "[ (json array of string)\n" | |
2975 | " \"zaddr\" (string) a zaddr belonging to the wallet\n" | |
2976 | " ,...\n" | |
2977 | "]\n" | |
2978 | "\nExamples:\n" | |
2979 | + HelpExampleCli("z_listaddresses", "") | |
2980 | + HelpExampleRpc("z_listaddresses", "") | |
2981 | ); | |
2982 | ||
2983 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
2984 | ||
44e37656 JG |
2985 | bool fIncludeWatchonly = false; |
2986 | if (params.size() > 0) { | |
2987 | fIncludeWatchonly = params[0].get_bool(); | |
2988 | } | |
2989 | ||
0d37ae3a | 2990 | UniValue ret(UniValue::VARR); |
e709997f S |
2991 | std::set<libzcash::PaymentAddress> addresses; |
2992 | pwalletMain->GetPaymentAddresses(addresses); | |
2993 | for (auto addr : addresses ) { | |
44e37656 JG |
2994 | if (fIncludeWatchonly || pwalletMain->HaveSpendingKey(addr)) { |
2995 | ret.push_back(CZCPaymentAddress(addr).ToString()); | |
2996 | } | |
e709997f S |
2997 | } |
2998 | return ret; | |
2999 | } | |
3000 | ||
44e37656 | 3001 | CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ignoreUnspendable=true) { |
a0a3334c S |
3002 | set<CBitcoinAddress> setAddress; |
3003 | vector<COutput> vecOutputs; | |
70454796 JG |
3004 | CAmount balance = 0; |
3005 | ||
a0a3334c S |
3006 | if (transparentAddress.length() > 0) { |
3007 | CBitcoinAddress taddr = CBitcoinAddress(transparentAddress); | |
3008 | if (!taddr.IsValid()) { | |
3009 | throw std::runtime_error("invalid transparent address"); | |
3010 | } | |
3011 | setAddress.insert(taddr); | |
3012 | } | |
70454796 | 3013 | |
a0a3334c S |
3014 | LOCK2(cs_main, pwalletMain->cs_wallet); |
3015 | ||
3016 | pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); | |
3017 | ||
3018 | BOOST_FOREACH(const COutput& out, vecOutputs) { | |
3019 | if (out.nDepth < minDepth) { | |
3020 | continue; | |
3021 | } | |
3022 | ||
44e37656 JG |
3023 | if (ignoreUnspendable && !out.fSpendable) { |
3024 | continue; | |
3025 | } | |
3026 | ||
a0a3334c S |
3027 | if (setAddress.size()) { |
3028 | CTxDestination address; | |
3029 | if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { | |
3030 | continue; | |
3031 | } | |
3032 | ||
3033 | if (!setAddress.count(address)) { | |
3034 | continue; | |
3035 | } | |
3036 | } | |
70454796 | 3037 | |
a0a3334c S |
3038 | CAmount nValue = out.tx->vout[out.i].nValue; |
3039 | balance += nValue; | |
3040 | } | |
3041 | return balance; | |
3042 | } | |
3043 | ||
44e37656 | 3044 | CAmount getBalanceZaddr(std::string address, int minDepth = 1, bool ignoreUnspendable=true) { |
a0a3334c | 3045 | CAmount balance = 0; |
a9743bc8 | 3046 | std::vector<CNotePlaintextEntry> entries; |
a0a3334c | 3047 | LOCK2(cs_main, pwalletMain->cs_wallet); |
44e37656 | 3048 | pwalletMain->GetFilteredNotes(entries, address, minDepth, true, ignoreUnspendable); |
a9743bc8 S |
3049 | for (auto & entry : entries) { |
3050 | balance += CAmount(entry.plaintext.value); | |
a0a3334c | 3051 | } |
a0a3334c S |
3052 | return balance; |
3053 | } | |
3054 | ||
3055 | ||
0d37ae3a | 3056 | UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) |
6c41028f S |
3057 | { |
3058 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3059 | return NullUniValue; |
6c41028f S |
3060 | |
3061 | if (fHelp || params.size()==0 || params.size() >2) | |
3062 | throw runtime_error( | |
3063 | "z_listreceivedbyaddress \"address\" ( minconf )\n" | |
3064 | "\nReturn a list of amounts received by a zaddr belonging to the node’s wallet.\n" | |
3065 | "\nArguments:\n" | |
3066 | "1. \"address\" (string) The private address.\n" | |
3067 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" | |
3068 | "\nResult:\n" | |
3069 | "{\n" | |
3070 | " \"txid\": xxxxx, (string) the transaction id\n" | |
3071 | " \"amount\": xxxxx, (numeric) the amount of value in the note\n" | |
3072 | " \"memo\": xxxxx, (string) hexademical string representation of memo field\n" | |
3073 | "}\n" | |
337a99a2 JG |
3074 | "\nExamples:\n" |
3075 | + HelpExampleCli("z_listreceivedbyaddress", "\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") | |
3076 | + HelpExampleRpc("z_listreceivedbyaddress", "\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") | |
6c41028f S |
3077 | ); |
3078 | ||
3079 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3080 | ||
3081 | int nMinDepth = 1; | |
3082 | if (params.size() > 1) { | |
3083 | nMinDepth = params[1].get_int(); | |
3084 | } | |
3085 | if (nMinDepth < 0) { | |
3086 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); | |
3087 | } | |
70454796 | 3088 | |
6c41028f S |
3089 | // Check that the from address is valid. |
3090 | auto fromaddress = params[0].get_str(); | |
3091 | ||
3092 | libzcash::PaymentAddress zaddr; | |
3093 | CZCPaymentAddress address(fromaddress); | |
3094 | try { | |
3095 | zaddr = address.Get(); | |
fce72608 | 3096 | } catch (const std::runtime_error&) { |
6c41028f S |
3097 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr."); |
3098 | } | |
3099 | ||
44e37656 JG |
3100 | if (!(pwalletMain->HaveSpendingKey(zaddr) || pwalletMain->HaveViewingKey(zaddr))) { |
3101 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found."); | |
6c41028f | 3102 | } |
70454796 JG |
3103 | |
3104 | ||
0d37ae3a | 3105 | UniValue result(UniValue::VARR); |
6c41028f | 3106 | std::vector<CNotePlaintextEntry> entries; |
44e37656 | 3107 | pwalletMain->GetFilteredNotes(entries, fromaddress, nMinDepth, false, false); |
6c41028f | 3108 | for (CNotePlaintextEntry & entry : entries) { |
0d37ae3a | 3109 | UniValue obj(UniValue::VOBJ); |
6c41028f S |
3110 | obj.push_back(Pair("txid",entry.jsop.hash.ToString())); |
3111 | obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.plaintext.value)))); | |
3112 | std::string data(entry.plaintext.memo.begin(), entry.plaintext.memo.end()); | |
3113 | obj.push_back(Pair("memo", HexStr(data))); | |
3114 | result.push_back(obj); | |
3115 | } | |
3116 | return result; | |
3117 | } | |
3118 | ||
3119 | ||
0d37ae3a | 3120 | UniValue z_getbalance(const UniValue& params, bool fHelp) |
a0a3334c S |
3121 | { |
3122 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3123 | return NullUniValue; |
a0a3334c S |
3124 | |
3125 | if (fHelp || params.size()==0 || params.size() >2) | |
3126 | throw runtime_error( | |
3127 | "z_getbalance \"address\" ( minconf )\n" | |
3128 | "\nReturns the balance of a taddr or zaddr belonging to the node’s wallet.\n" | |
2bbfe6c4 JG |
3129 | "\nCAUTION: If address is a watch-only zaddr, the returned balance may be larger than the actual balance," |
3130 | "\nbecause spends cannot be detected with incoming viewing keys.\n" | |
a0a3334c S |
3131 | "\nArguments:\n" |
3132 | "1. \"address\" (string) The selected address. It may be a transparent or private address.\n" | |
3133 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" | |
3134 | "\nResult:\n" | |
6dec2d03 | 3135 | "amount (numeric) The total amount in " + CURRENCY_UNIT + " received for this address.\n" |
a0a3334c S |
3136 | "\nExamples:\n" |
3137 | "\nThe total amount received by address \"myaddress\"\n" | |
3138 | + HelpExampleCli("z_getbalance", "\"myaddress\"") + | |
3139 | "\nThe total amount received by address \"myaddress\" at least 5 blocks confirmed\n" | |
3140 | + HelpExampleCli("z_getbalance", "\"myaddress\" 5") + | |
3141 | "\nAs a json rpc call\n" | |
3142 | + HelpExampleRpc("z_getbalance", "\"myaddress\", 5") | |
3143 | ); | |
3144 | ||
3145 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3146 | ||
3147 | int nMinDepth = 1; | |
3148 | if (params.size() > 1) { | |
3149 | nMinDepth = params[1].get_int(); | |
3150 | } | |
12448b64 S |
3151 | if (nMinDepth < 0) { |
3152 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); | |
3153 | } | |
70454796 | 3154 | |
a0a3334c S |
3155 | // Check that the from address is valid. |
3156 | auto fromaddress = params[0].get_str(); | |
3157 | bool fromTaddr = false; | |
3158 | CBitcoinAddress taddr(fromaddress); | |
3159 | fromTaddr = taddr.IsValid(); | |
3160 | libzcash::PaymentAddress zaddr; | |
3161 | if (!fromTaddr) { | |
3162 | CZCPaymentAddress address(fromaddress); | |
3163 | try { | |
3164 | zaddr = address.Get(); | |
fce72608 | 3165 | } catch (const std::runtime_error&) { |
a0a3334c S |
3166 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); |
3167 | } | |
44e37656 JG |
3168 | if (!(pwalletMain->HaveSpendingKey(zaddr) || pwalletMain->HaveViewingKey(zaddr))) { |
3169 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found."); | |
12448b64 | 3170 | } |
a0a3334c S |
3171 | } |
3172 | ||
3173 | CAmount nBalance = 0; | |
3174 | if (fromTaddr) { | |
44e37656 | 3175 | nBalance = getBalanceTaddr(fromaddress, nMinDepth, false); |
a0a3334c | 3176 | } else { |
44e37656 | 3177 | nBalance = getBalanceZaddr(fromaddress, nMinDepth, false); |
a0a3334c S |
3178 | } |
3179 | ||
3180 | return ValueFromAmount(nBalance); | |
3181 | } | |
3182 | ||
3183 | ||
0d37ae3a | 3184 | UniValue z_gettotalbalance(const UniValue& params, bool fHelp) |
a0a3334c S |
3185 | { |
3186 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3187 | return NullUniValue; |
a0a3334c | 3188 | |
44e37656 | 3189 | if (fHelp || params.size() > 2) |
a0a3334c | 3190 | throw runtime_error( |
44e37656 | 3191 | "z_gettotalbalance ( minconf includeWatchonly )\n" |
a0a3334c | 3192 | "\nReturn the total value of funds stored in the node’s wallet.\n" |
2bbfe6c4 JG |
3193 | "\nCAUTION: If the wallet contains watch-only zaddrs, the returned private balance may be larger than the actual balance," |
3194 | "\nbecause spends cannot be detected with incoming viewing keys.\n" | |
a0a3334c S |
3195 | "\nArguments:\n" |
3196 | "1. minconf (numeric, optional, default=1) Only include private and transparent transactions confirmed at least this many times.\n" | |
44e37656 | 3197 | "2. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress' and 'z_importviewingkey')\n" |
a0a3334c S |
3198 | "\nResult:\n" |
3199 | "{\n" | |
3200 | " \"transparent\": xxxxx, (numeric) the total balance of transparent funds\n" | |
3201 | " \"private\": xxxxx, (numeric) the total balance of private funds\n" | |
3202 | " \"total\": xxxxx, (numeric) the total balance of both transparent and private funds\n" | |
3203 | "}\n" | |
3204 | "\nExamples:\n" | |
3205 | "\nThe total amount in the wallet\n" | |
3206 | + HelpExampleCli("z_gettotalbalance", "") + | |
3207 | "\nThe total amount in the wallet at least 5 blocks confirmed\n" | |
3208 | + HelpExampleCli("z_gettotalbalance", "5") + | |
3209 | "\nAs a json rpc call\n" | |
3210 | + HelpExampleRpc("z_gettotalbalance", "5") | |
3211 | ); | |
3212 | ||
3213 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3214 | ||
3215 | int nMinDepth = 1; | |
44e37656 | 3216 | if (params.size() > 0) { |
a0a3334c S |
3217 | nMinDepth = params[0].get_int(); |
3218 | } | |
12448b64 S |
3219 | if (nMinDepth < 0) { |
3220 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); | |
3221 | } | |
a0a3334c | 3222 | |
44e37656 JG |
3223 | bool fIncludeWatchonly = false; |
3224 | if (params.size() > 1) { | |
3225 | fIncludeWatchonly = params[1].get_bool(); | |
3226 | } | |
3227 | ||
a0a3334c | 3228 | // getbalance and "getbalance * 1 true" should return the same number |
70454796 | 3229 | // but they don't because wtx.GetAmounts() does not handle tx where there are no outputs |
a0a3334c S |
3230 | // pwalletMain->GetBalance() does not accept min depth parameter |
3231 | // so we use our own method to get balance of utxos. | |
44e37656 JG |
3232 | CAmount nBalance = getBalanceTaddr("", nMinDepth, !fIncludeWatchonly); |
3233 | CAmount nPrivateBalance = getBalanceZaddr("", nMinDepth, !fIncludeWatchonly); | |
a0a3334c | 3234 | CAmount nTotalBalance = nBalance + nPrivateBalance; |
0d37ae3a | 3235 | UniValue result(UniValue::VOBJ); |
f54db399 JG |
3236 | result.push_back(Pair("transparent", FormatMoney(nBalance))); |
3237 | result.push_back(Pair("private", FormatMoney(nPrivateBalance))); | |
3238 | result.push_back(Pair("total", FormatMoney(nTotalBalance))); | |
a0a3334c S |
3239 | return result; |
3240 | } | |
3241 | ||
0d37ae3a | 3242 | UniValue z_getoperationresult(const UniValue& params, bool fHelp) |
c1eae280 S |
3243 | { |
3244 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3245 | return NullUniValue; |
c1eae280 S |
3246 | |
3247 | if (fHelp || params.size() > 1) | |
3248 | throw runtime_error( | |
3249 | "z_getoperationresult ([\"operationid\", ... ]) \n" | |
3250 | "\nRetrieve the result and status of an operation which has finished, and then remove the operation from memory." | |
3251 | + HelpRequiringPassphrase() + "\n" | |
3252 | "\nArguments:\n" | |
3253 | "1. \"operationid\" (array, optional) A list of operation ids we are interested in. If not provided, examine all operations known to the node.\n" | |
3254 | "\nResult:\n" | |
3255 | "\" [object, ...]\" (array) A list of JSON objects\n" | |
337a99a2 JG |
3256 | "\nExamples:\n" |
3257 | + HelpExampleCli("z_getoperationresult", "'[\"operationid\", ... ]'") | |
3258 | + HelpExampleRpc("z_getoperationresult", "'[\"operationid\", ... ]'") | |
c1eae280 | 3259 | ); |
70454796 | 3260 | |
c1eae280 S |
3261 | // This call will remove finished operations |
3262 | return z_getoperationstatus_IMPL(params, true); | |
3263 | } | |
fc72c078 | 3264 | |
0d37ae3a | 3265 | UniValue z_getoperationstatus(const UniValue& params, bool fHelp) |
fc72c078 S |
3266 | { |
3267 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3268 | return NullUniValue; |
fc72c078 | 3269 | |
34f0001c | 3270 | if (fHelp || params.size() > 1) |
fc72c078 | 3271 | throw runtime_error( |
34f0001c | 3272 | "z_getoperationstatus ([\"operationid\", ... ]) \n" |
c1eae280 | 3273 | "\nGet operation status and any associated result or error data. The operation will remain in memory." |
fc72c078 S |
3274 | + HelpRequiringPassphrase() + "\n" |
3275 | "\nArguments:\n" | |
c1eae280 | 3276 | "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 | 3277 | "\nResult:\n" |
34f0001c | 3278 | "\" [object, ...]\" (array) A list of JSON objects\n" |
337a99a2 JG |
3279 | "\nExamples:\n" |
3280 | + HelpExampleCli("z_getoperationstatus", "'[\"operationid\", ... ]'") | |
3281 | + HelpExampleRpc("z_getoperationstatus", "'[\"operationid\", ... ]'") | |
fc72c078 | 3282 | ); |
70454796 | 3283 | |
c1eae280 S |
3284 | // This call is idempotent so we don't want to remove finished operations |
3285 | return z_getoperationstatus_IMPL(params, false); | |
3286 | } | |
fc72c078 | 3287 | |
0d37ae3a | 3288 | UniValue z_getoperationstatus_IMPL(const UniValue& params, bool fRemoveFinishedOperations=false) |
c1eae280 | 3289 | { |
fc72c078 S |
3290 | LOCK2(cs_main, pwalletMain->cs_wallet); |
3291 | ||
34f0001c S |
3292 | std::set<AsyncRPCOperationId> filter; |
3293 | if (params.size()==1) { | |
0d37ae3a | 3294 | UniValue ids = params[0].get_array(); |
c24109ec | 3295 | for (const UniValue & v : ids.getValues()) { |
34f0001c S |
3296 | filter.insert(v.get_str()); |
3297 | } | |
fc72c078 | 3298 | } |
34f0001c | 3299 | bool useFilter = (filter.size()>0); |
fc72c078 | 3300 | |
0d37ae3a | 3301 | UniValue ret(UniValue::VARR); |
34f0001c S |
3302 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); |
3303 | std::vector<AsyncRPCOperationId> ids = q->getAllOperationIds(); | |
3304 | ||
3305 | for (auto id : ids) { | |
3306 | if (useFilter && !filter.count(id)) | |
3307 | continue; | |
70454796 | 3308 | |
34f0001c S |
3309 | std::shared_ptr<AsyncRPCOperation> operation = q->getOperationForId(id); |
3310 | if (!operation) { | |
3311 | continue; | |
3312 | // It's possible that the operation was removed from the internal queue and map during this loop | |
3313 | // throw JSONRPCError(RPC_INVALID_PARAMETER, "No operation exists for that id."); | |
3314 | } | |
fc72c078 | 3315 | |
2f21206c S |
3316 | UniValue obj = operation->getStatus(); |
3317 | std::string s = obj["status"].get_str(); | |
c1eae280 S |
3318 | if (fRemoveFinishedOperations) { |
3319 | // Caller is only interested in retrieving finished results | |
2f21206c S |
3320 | if ("success"==s || "failed"==s || "cancelled"==s) { |
3321 | ret.push_back(obj); | |
c1eae280 S |
3322 | q->popOperationForId(id); |
3323 | } | |
3324 | } else { | |
2f21206c | 3325 | ret.push_back(obj); |
34f0001c | 3326 | } |
fc72c078 S |
3327 | } |
3328 | ||
0d37ae3a JG |
3329 | std::vector<UniValue> arrTmp = ret.getValues(); |
3330 | ||
2d2f3d18 | 3331 | // sort results chronologically by creation_time |
0d37ae3a | 3332 | std::sort(arrTmp.begin(), arrTmp.end(), [](UniValue a, UniValue b) -> bool { |
2d2f3d18 S |
3333 | const int64_t t1 = find_value(a.get_obj(), "creation_time").get_int64(); |
3334 | const int64_t t2 = find_value(b.get_obj(), "creation_time").get_int64(); | |
3335 | return t1 < t2; | |
3336 | }); | |
3337 | ||
0d37ae3a JG |
3338 | ret.clear(); |
3339 | ret.setArray(); | |
3340 | ret.push_backV(arrTmp); | |
3341 | ||
34f0001c | 3342 | return ret; |
fc72c078 S |
3343 | } |
3344 | ||
3920292b S |
3345 | |
3346 | // Here we define the maximum number of zaddr outputs that can be included in a transaction. | |
3347 | // If input notes are small, we might actually require more than one joinsplit per zaddr output. | |
3348 | // For now though, we assume we use one joinsplit per zaddr output (and the second output note is change). | |
3349 | // We reduce the result by 1 to ensure there is room for non-joinsplit CTransaction data. | |
3350 | #define Z_SENDMANY_MAX_ZADDR_OUTPUTS ((MAX_TX_SIZE / JSDescription().GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION)) - 1) | |
3351 | ||
3352 | // transaction.h comment: spending taddr output requires CTxIn >= 148 bytes and typical taddr txout is 34 bytes | |
3353 | #define CTXIN_SPEND_DUST_SIZE 148 | |
3354 | #define CTXOUT_REGULAR_SIZE 34 | |
3355 | ||
0d37ae3a | 3356 | UniValue z_sendmany(const UniValue& params, bool fHelp) |
fc72c078 S |
3357 | { |
3358 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3359 | return NullUniValue; |
fc72c078 | 3360 | |
af53da02 | 3361 | if (fHelp || params.size() < 2 || params.size() > 4) |
fc72c078 | 3362 | throw runtime_error( |
af53da02 | 3363 | "z_sendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf ) ( fee )\n" |
fc72c078 | 3364 | "\nSend multiple times. Amounts are double-precision floating point numbers." |
b7d7b2ad | 3365 | "\nChange from a taddr flows to a new taddr address, while change from zaddr returns to itself." |
48f9c65b | 3366 | "\nWhen sending coinbase UTXOs to a zaddr, change is not allowed. The entire value of the UTXO(s) must be consumed." |
3920292b | 3367 | + strprintf("\nCurrently, the maximum number of zaddr outputs is %d due to transaction size limits.\n", Z_SENDMANY_MAX_ZADDR_OUTPUTS) |
fc72c078 S |
3368 | + HelpRequiringPassphrase() + "\n" |
3369 | "\nArguments:\n" | |
3370 | "1. \"fromaddress\" (string, required) The taddr or zaddr to send the funds from.\n" | |
3371 | "2. \"amounts\" (array, required) An array of json objects representing the amounts to send.\n" | |
3372 | " [{\n" | |
3373 | " \"address\":address (string, required) The address is a taddr or zaddr\n" | |
6dec2d03 | 3374 | " \"amount\":amount (numeric, required) The numeric amount in " + CURRENCY_UNIT + " is the value\n" |
fc72c078 S |
3375 | " \"memo\":memo (string, optional) If the address is a zaddr, raw data represented in hexadecimal string format\n" |
3376 | " }, ... ]\n" | |
3377 | "3. minconf (numeric, optional, default=1) Only use funds confirmed at least this many times.\n" | |
af53da02 S |
3378 | "4. fee (numeric, optional, default=" |
3379 | + strprintf("%s", FormatMoney(ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n" | |
fc72c078 S |
3380 | "\nResult:\n" |
3381 | "\"operationid\" (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" | |
337a99a2 JG |
3382 | "\nExamples:\n" |
3383 | + HelpExampleCli("z_sendmany", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" '[{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\" ,\"amount\": 5.0}]'") | |
3384 | + HelpExampleRpc("z_sendmany", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", [{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\" ,\"amount\": 5.0}]") | |
fc72c078 S |
3385 | ); |
3386 | ||
3387 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3388 | ||
fc72c078 S |
3389 | // Check that the from address is valid. |
3390 | auto fromaddress = params[0].get_str(); | |
3391 | bool fromTaddr = false; | |
3392 | CBitcoinAddress taddr(fromaddress); | |
3393 | fromTaddr = taddr.IsValid(); | |
3394 | libzcash::PaymentAddress zaddr; | |
3395 | if (!fromTaddr) { | |
3396 | CZCPaymentAddress address(fromaddress); | |
3397 | try { | |
3398 | zaddr = address.Get(); | |
fce72608 | 3399 | } catch (const std::runtime_error&) { |
fc72c078 S |
3400 | // invalid |
3401 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); | |
3402 | } | |
3403 | } | |
3404 | ||
dafb8161 | 3405 | // Check that we have the spending key |
fc72c078 S |
3406 | if (!fromTaddr) { |
3407 | if (!pwalletMain->HaveSpendingKey(zaddr)) { | |
3408 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found."); | |
3409 | } | |
3410 | } | |
3411 | ||
0d37ae3a | 3412 | UniValue outputs = params[1].get_array(); |
fc72c078 S |
3413 | |
3414 | if (outputs.size()==0) | |
3415 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amounts array is empty."); | |
3416 | ||
3417 | // Keep track of addresses to spot duplicates | |
3418 | set<std::string> setAddress; | |
3419 | ||
3420 | // Recipients | |
dafb8161 S |
3421 | std::vector<SendManyRecipient> taddrRecipients; |
3422 | std::vector<SendManyRecipient> zaddrRecipients; | |
af53da02 | 3423 | CAmount nTotalOut = 0; |
fc72c078 | 3424 | |
0d37ae3a JG |
3425 | for (const UniValue& o : outputs.getValues()) { |
3426 | if (!o.isObject()) | |
fc72c078 | 3427 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); |
fc72c078 | 3428 | |
fc72c078 | 3429 | // sanity check, report error if unknown key-value pairs |
0d37ae3a JG |
3430 | for (const string& name_ : o.getKeys()) { |
3431 | std::string s = name_; | |
fc72c078 S |
3432 | if (s != "address" && s != "amount" && s!="memo") |
3433 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown key: ")+s); | |
3434 | } | |
3435 | ||
3436 | string address = find_value(o, "address").get_str(); | |
3437 | bool isZaddr = false; | |
3438 | CBitcoinAddress taddr(address); | |
3439 | if (!taddr.IsValid()) { | |
3440 | try { | |
3441 | CZCPaymentAddress zaddr(address); | |
3442 | zaddr.Get(); | |
3443 | isZaddr = true; | |
fce72608 | 3444 | } catch (const std::runtime_error&) { |
fc72c078 S |
3445 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ")+address ); |
3446 | } | |
3447 | } | |
3448 | ||
3449 | if (setAddress.count(address)) | |
3450 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+address); | |
3451 | setAddress.insert(address); | |
3452 | ||
0d37ae3a | 3453 | UniValue memoValue = find_value(o, "memo"); |
fc72c078 | 3454 | string memo; |
0d37ae3a | 3455 | if (!memoValue.isNull()) { |
fc72c078 S |
3456 | memo = memoValue.get_str(); |
3457 | if (!isZaddr) { | |
3458 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo can not be used with a taddr. It can only be used with a zaddr."); | |
3459 | } else if (!IsHex(memo)) { | |
3460 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format."); | |
3461 | } | |
6114cfe7 | 3462 | if (memo.length() > ZC_MEMO_SIZE*2) { |
dafb8161 S |
3463 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE )); |
3464 | } | |
fc72c078 S |
3465 | } |
3466 | ||
0d37ae3a | 3467 | UniValue av = find_value(o, "amount"); |
fc72c078 S |
3468 | CAmount nAmount = AmountFromValue( av ); |
3469 | if (nAmount < 0) | |
3470 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amount must be positive"); | |
3471 | ||
dafb8161 S |
3472 | if (isZaddr) { |
3473 | zaddrRecipients.push_back( SendManyRecipient(address, nAmount, memo) ); | |
3474 | } else { | |
3475 | taddrRecipients.push_back( SendManyRecipient(address, nAmount, memo) ); | |
3476 | } | |
af53da02 S |
3477 | |
3478 | nTotalOut += nAmount; | |
fc72c078 S |
3479 | } |
3480 | ||
3920292b S |
3481 | // Check the number of zaddr outputs does not exceed the limit. |
3482 | if (zaddrRecipients.size() > Z_SENDMANY_MAX_ZADDR_OUTPUTS) { | |
3483 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, too many zaddr outputs"); | |
3484 | } | |
3485 | ||
3486 | // As a sanity check, estimate and verify that the size of the transaction will be valid. | |
3487 | // Depending on the input notes, the actual tx size may turn out to be larger and perhaps invalid. | |
3488 | size_t txsize = 0; | |
3489 | CMutableTransaction mtx; | |
3490 | mtx.nVersion = 2; | |
3491 | for (int i = 0; i < zaddrRecipients.size(); i++) { | |
3492 | mtx.vjoinsplit.push_back(JSDescription()); | |
3493 | } | |
3494 | CTransaction tx(mtx); | |
3495 | txsize += tx.GetSerializeSize(SER_NETWORK, tx.nVersion); | |
3496 | if (fromTaddr) { | |
3497 | txsize += CTXIN_SPEND_DUST_SIZE; | |
3498 | txsize += CTXOUT_REGULAR_SIZE; // There will probably be taddr change | |
3499 | } | |
3500 | txsize += CTXOUT_REGULAR_SIZE * taddrRecipients.size(); | |
3501 | if (txsize > MAX_TX_SIZE) { | |
3502 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Too many outputs, size of raw transaction would be larger than limit of %d bytes", MAX_TX_SIZE )); | |
3503 | } | |
3504 | ||
fc72c078 S |
3505 | // Minimum confirmations |
3506 | int nMinDepth = 1; | |
12448b64 | 3507 | if (params.size() > 2) { |
fc72c078 | 3508 | nMinDepth = params[2].get_int(); |
12448b64 S |
3509 | } |
3510 | if (nMinDepth < 0) { | |
3511 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); | |
3512 | } | |
fc72c078 | 3513 | |
af53da02 S |
3514 | // Fee in Zatoshis, not currency format) |
3515 | CAmount nFee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE; | |
3516 | if (params.size() > 3) { | |
7eccce4e S |
3517 | if (params[3].get_real() == 0.0) { |
3518 | nFee = 0; | |
3519 | } else { | |
3520 | nFee = AmountFromValue( params[3] ); | |
3521 | } | |
3522 | ||
af53da02 S |
3523 | // Check that the user specified fee is sane. |
3524 | if (nFee > nTotalOut) { | |
3525 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the sum of outputs %s", FormatMoney(nFee), FormatMoney(nTotalOut))); | |
3526 | } | |
3527 | } | |
3528 | ||
8aa7937d | 3529 | // Use input parameters as the optional context info to be returned by z_getoperationstatus and z_getoperationresult. |
0d37ae3a | 3530 | UniValue o(UniValue::VOBJ); |
8aa7937d S |
3531 | o.push_back(Pair("fromaddress", params[0])); |
3532 | o.push_back(Pair("amounts", params[1])); | |
3533 | o.push_back(Pair("minconf", nMinDepth)); | |
3534 | o.push_back(Pair("fee", std::stod(FormatMoney(nFee)))); | |
0d37ae3a | 3535 | UniValue contextInfo = o; |
8aa7937d | 3536 | |
072099d7 | 3537 | // Contextual transaction we will build on |
9bb37bf0 JG |
3538 | int nextBlockHeight = chainActive.Height() + 1; |
3539 | CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextBlockHeight); | |
e6cd2a83 S |
3540 | bool isShielded = !fromTaddr || zaddrRecipients.size() > 0; |
3541 | if (contextualTx.nVersion == 1 && isShielded) { | |
072099d7 S |
3542 | contextualTx.nVersion = 2; // Tx format should support vjoinsplits |
3543 | } | |
9bb37bf0 JG |
3544 | if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) { |
3545 | contextualTx.nExpiryHeight = nextBlockHeight + expiryDelta; | |
3546 | } | |
072099d7 | 3547 | |
dafb8161 | 3548 | // Create operation and add to global queue |
fc72c078 | 3549 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); |
072099d7 | 3550 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(contextualTx, fromaddress, taddrRecipients, zaddrRecipients, nMinDepth, nFee, contextInfo) ); |
fc72c078 S |
3551 | q->addOperation(operation); |
3552 | AsyncRPCOperationId operationId = operation->getId(); | |
3553 | return operationId; | |
fc72c078 | 3554 | } |
34f0001c S |
3555 | |
3556 | ||
06c19063 S |
3557 | /** |
3558 | When estimating the number of coinbase utxos we can shield in a single transaction: | |
3559 | 1. Joinsplit description is 1802 bytes. | |
3560 | 2. Transaction overhead ~ 100 bytes | |
3561 | 3. Spending a typical P2PKH is >=148 bytes, as defined in CTXIN_SPEND_DUST_SIZE. | |
3562 | 4. Spending a multi-sig P2SH address can vary greatly: | |
3563 | https://github.com/bitcoin/bitcoin/blob/c3ad56f4e0b587d8d763af03d743fdfc2d180c9b/src/main.cpp#L517 | |
3564 | In real-world coinbase utxos, we consider a 3-of-3 multisig, where the size is roughly: | |
3565 | (3*(33+1))+3 = 105 byte redeem script | |
3566 | 105 + 1 + 3*(73+1) = 328 bytes of scriptSig, rounded up to 400 based on testnet experiments. | |
3567 | */ | |
3568 | #define CTXIN_SPEND_P2SH_SIZE 400 | |
3569 | ||
c5dabd2b S |
3570 | #define SHIELD_COINBASE_DEFAULT_LIMIT 50 |
3571 | ||
06c19063 S |
3572 | UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) |
3573 | { | |
3574 | if (!EnsureWalletIsAvailable(fHelp)) | |
3575 | return NullUniValue; | |
3576 | ||
c5dabd2b | 3577 | if (fHelp || params.size() < 2 || params.size() > 4) |
06c19063 | 3578 | throw runtime_error( |
c5dabd2b | 3579 | "z_shieldcoinbase \"fromaddress\" \"tozaddress\" ( fee ) ( limit )\n" |
06c19063 S |
3580 | "\nShield transparent coinbase funds by sending to a shielded zaddr. This is an asynchronous operation and utxos" |
3581 | "\nselected for shielding will be locked. If there is an error, they are unlocked. The RPC call `listlockunspent`" | |
c5dabd2b S |
3582 | "\ncan be used to return a list of locked utxos. The number of coinbase utxos selected for shielding can be limited" |
3583 | "\nby the caller. If the limit parameter is set to zero, the -mempooltxinputlimit option will determine the number" | |
3584 | "\nof uxtos. Any limit is constrained by the consensus rule defining a maximum transaction size of " | |
06c19063 S |
3585 | + strprintf("%d bytes.", MAX_TX_SIZE) |
3586 | + HelpRequiringPassphrase() + "\n" | |
3587 | "\nArguments:\n" | |
3588 | "1. \"fromaddress\" (string, required) The address is a taddr or \"*\" for all taddrs belonging to the wallet.\n" | |
3589 | "2. \"toaddress\" (string, required) The address is a zaddr.\n" | |
3590 | "3. fee (numeric, optional, default=" | |
3591 | + strprintf("%s", FormatMoney(SHIELD_COINBASE_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n" | |
c5dabd2b S |
3592 | "4. limit (numeric, optional, default=" |
3593 | + strprintf("%d", SHIELD_COINBASE_DEFAULT_LIMIT) + ") Limit on the maximum number of utxos to shield. Set to 0 to use node option -mempooltxinputlimit.\n" | |
06c19063 S |
3594 | "\nResult:\n" |
3595 | "{\n" | |
06c19063 S |
3596 | " \"remainingUTXOs\": xxx (numeric) Number of coinbase utxos still available for shielding.\n" |
3597 | " \"remainingValue\": xxx (numeric) Value of coinbase utxos still available for shielding.\n" | |
9eb8089e JG |
3598 | " \"shieldingUTXOs\": xxx (numeric) Number of coinbase utxos being shielded.\n" |
3599 | " \"shieldingValue\": xxx (numeric) Value of coinbase utxos being shielded.\n" | |
3600 | " \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" | |
06c19063 | 3601 | "}\n" |
337a99a2 JG |
3602 | "\nExamples:\n" |
3603 | + HelpExampleCli("z_shieldcoinbase", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\" \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") | |
3604 | + HelpExampleRpc("z_shieldcoinbase", "\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\", \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") | |
06c19063 S |
3605 | ); |
3606 | ||
3607 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3608 | ||
3609 | // Validate the from address | |
3610 | auto fromaddress = params[0].get_str(); | |
3611 | bool isFromWildcard = fromaddress == "*"; | |
3612 | CBitcoinAddress taddr; | |
3613 | if (!isFromWildcard) { | |
3614 | taddr = CBitcoinAddress(fromaddress); | |
3615 | if (!taddr.IsValid()) { | |
3616 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or \"*\"."); | |
3617 | } | |
3618 | } | |
3619 | ||
3620 | // Validate the destination address | |
3621 | auto destaddress = params[1].get_str(); | |
3622 | try { | |
3623 | CZCPaymentAddress pa(destaddress); | |
3624 | libzcash::PaymentAddress zaddr = pa.Get(); | |
3625 | } catch (const std::runtime_error&) { | |
3626 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); | |
3627 | } | |
3628 | ||
3629 | // Convert fee from currency format to zatoshis | |
3630 | CAmount nFee = SHIELD_COINBASE_DEFAULT_MINERS_FEE; | |
3631 | if (params.size() > 2) { | |
3632 | if (params[2].get_real() == 0.0) { | |
3633 | nFee = 0; | |
3634 | } else { | |
3635 | nFee = AmountFromValue( params[2] ); | |
3636 | } | |
3637 | } | |
3638 | ||
c5dabd2b S |
3639 | int nLimit = SHIELD_COINBASE_DEFAULT_LIMIT; |
3640 | if (params.size() > 3) { | |
3641 | nLimit = params[3].get_int(); | |
3642 | if (nLimit < 0) { | |
3643 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of utxos cannot be negative"); | |
3644 | } | |
3645 | } | |
3646 | ||
06c19063 S |
3647 | // Prepare to get coinbase utxos |
3648 | std::vector<ShieldCoinbaseUTXO> inputs; | |
3649 | CAmount shieldedValue = 0; | |
3650 | CAmount remainingValue = 0; | |
3651 | size_t estimatedTxSize = 2000; // 1802 joinsplit description + tx overhead + wiggle room | |
3652 | size_t utxoCounter = 0; | |
3653 | bool maxedOutFlag = false; | |
c5dabd2b | 3654 | size_t mempoolLimit = (nLimit != 0) ? nLimit : (size_t)GetArg("-mempooltxinputlimit", 0); |
06c19063 S |
3655 | |
3656 | // Set of addresses to filter utxos by | |
3657 | set<CBitcoinAddress> setAddress = {}; | |
3658 | if (!isFromWildcard) { | |
3659 | setAddress.insert(taddr); | |
3660 | } | |
3661 | ||
3662 | // Get available utxos | |
3663 | vector<COutput> vecOutputs; | |
3664 | pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, true); | |
3665 | ||
3666 | // Find unspent coinbase utxos and update estimated size | |
3667 | BOOST_FOREACH(const COutput& out, vecOutputs) { | |
3668 | if (!out.fSpendable) { | |
3669 | continue; | |
3670 | } | |
3671 | ||
3672 | CTxDestination address; | |
3673 | if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { | |
3674 | continue; | |
3675 | } | |
3676 | // If taddr is not wildcard "*", filter utxos | |
3677 | if (setAddress.size()>0 && !setAddress.count(address)) { | |
3678 | continue; | |
3679 | } | |
3680 | ||
3681 | if (!out.tx->IsCoinBase()) { | |
3682 | continue; | |
3683 | } | |
3684 | ||
3685 | utxoCounter++; | |
3686 | CAmount nValue = out.tx->vout[out.i].nValue; | |
3687 | ||
3688 | if (!maxedOutFlag) { | |
3689 | CBitcoinAddress ba(address); | |
3690 | size_t increase = (ba.IsScript()) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_DUST_SIZE; | |
3691 | if (estimatedTxSize + increase >= MAX_TX_SIZE || | |
3692 | (mempoolLimit > 0 && utxoCounter > mempoolLimit)) | |
3693 | { | |
3694 | maxedOutFlag = true; | |
3695 | } else { | |
3696 | estimatedTxSize += increase; | |
3697 | ShieldCoinbaseUTXO utxo = {out.tx->GetHash(), out.i, nValue}; | |
3698 | inputs.push_back(utxo); | |
3699 | shieldedValue += nValue; | |
3700 | } | |
3701 | } | |
3702 | ||
3703 | if (maxedOutFlag) { | |
3704 | remainingValue += nValue; | |
3705 | } | |
3706 | } | |
3707 | ||
3708 | size_t numUtxos = inputs.size(); | |
3709 | ||
3710 | if (numUtxos == 0) { | |
3711 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any coinbase funds to shield."); | |
3712 | } | |
3713 | ||
3714 | if (shieldedValue < nFee) { | |
3715 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, | |
3716 | strprintf("Insufficient coinbase funds, have %s, which is less than miners fee %s", | |
3717 | FormatMoney(shieldedValue), FormatMoney(nFee))); | |
3718 | } | |
3719 | ||
3720 | // Check that the user specified fee is sane (if too high, it can result in error -25 absurd fee) | |
3721 | CAmount netAmount = shieldedValue - nFee; | |
3722 | if (nFee > netAmount) { | |
3723 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the net amount to be shielded %s", FormatMoney(nFee), FormatMoney(netAmount))); | |
3724 | } | |
3725 | ||
3726 | // Keep record of parameters in context object | |
3727 | UniValue contextInfo(UniValue::VOBJ); | |
3728 | contextInfo.push_back(Pair("fromaddress", params[0])); | |
3729 | contextInfo.push_back(Pair("toaddress", params[1])); | |
3730 | contextInfo.push_back(Pair("fee", ValueFromAmount(nFee))); | |
3731 | ||
072099d7 | 3732 | // Contextual transaction we will build on |
9bb37bf0 | 3733 | int nextBlockHeight = chainActive.Height() + 1; |
072099d7 | 3734 | CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction( |
9bb37bf0 | 3735 | Params().GetConsensus(), nextBlockHeight); |
072099d7 S |
3736 | if (contextualTx.nVersion == 1) { |
3737 | contextualTx.nVersion = 2; // Tx format should support vjoinsplits | |
3738 | } | |
9bb37bf0 JG |
3739 | if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) { |
3740 | contextualTx.nExpiryHeight = nextBlockHeight + expiryDelta; | |
3741 | } | |
072099d7 | 3742 | |
06c19063 S |
3743 | // Create operation and add to global queue |
3744 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); | |
072099d7 | 3745 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(contextualTx, inputs, destaddress, nFee, contextInfo) ); |
06c19063 S |
3746 | q->addOperation(operation); |
3747 | AsyncRPCOperationId operationId = operation->getId(); | |
3748 | ||
3749 | // Return continuation information | |
3750 | UniValue o(UniValue::VOBJ); | |
3751 | o.push_back(Pair("remainingUTXOs", utxoCounter - numUtxos)); | |
3752 | o.push_back(Pair("remainingValue", ValueFromAmount(remainingValue))); | |
3753 | o.push_back(Pair("shieldingUTXOs", numUtxos)); | |
3754 | o.push_back(Pair("shieldingValue", ValueFromAmount(shieldedValue))); | |
3755 | o.push_back(Pair("opid", operationId)); | |
3756 | return o; | |
3757 | } | |
3758 | ||
3759 | ||
6e9c7629 JG |
3760 | #define MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT 50 |
3761 | #define MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT 10 | |
3762 | ||
3763 | #define JOINSPLIT_SIZE JSDescription().GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) | |
3764 | ||
3765 | UniValue z_mergetoaddress(const UniValue& params, bool fHelp) | |
3766 | { | |
3767 | if (!EnsureWalletIsAvailable(fHelp)) | |
3768 | return NullUniValue; | |
3769 | ||
553a5c1a JG |
3770 | auto fEnableMergeToAddress = fExperimentalMode && GetBoolArg("-zmergetoaddress", false); |
3771 | std::string strDisabledMsg = ""; | |
3772 | if (!fEnableMergeToAddress) { | |
3773 | strDisabledMsg = "\nWARNING: z_mergetoaddress is DISABLED but can be enabled as an experimental feature.\n"; | |
3774 | } | |
3775 | ||
6e9c7629 JG |
3776 | if (fHelp || params.size() < 2 || params.size() > 6) |
3777 | throw runtime_error( | |
3778 | "z_mergetoaddress [\"fromaddress\", ... ] \"toaddress\" ( fee ) ( transparent_limit ) ( shielded_limit ) ( memo )\n" | |
553a5c1a | 3779 | + strDisabledMsg + |
6e9c7629 JG |
3780 | "\nMerge multiple UTXOs and notes into a single UTXO or note. Coinbase UTXOs are ignored; use `z_shieldcoinbase`" |
3781 | "\nto combine those into a single note." | |
3782 | "\n\nThis is an asynchronous operation, and UTXOs selected for merging will be locked. If there is an error, they" | |
3783 | "\nare unlocked. The RPC call `listlockunspent` can be used to return a list of locked UTXOs." | |
3784 | "\n\nThe number of UTXOs and notes selected for merging can be limited by the caller. If the transparent limit" | |
3785 | "\nparameter is set to zero, the -mempooltxinputlimit option will determine the number of UTXOs. Any limit is" | |
3786 | "\nconstrained by the consensus rule defining a maximum transaction size of " | |
3787 | + strprintf("%d bytes.", MAX_TX_SIZE) | |
3788 | + HelpRequiringPassphrase() + "\n" | |
3789 | "\nArguments:\n" | |
3790 | "1. fromaddresses (string, required) A JSON array with addresses.\n" | |
3791 | " The following special strings are accepted inside the array:\n" | |
3792 | " - \"*\": Merge both UTXOs and notes from all addresses belonging to the wallet.\n" | |
3793 | " - \"ANY_TADDR\": Merge UTXOs from all t-addrs belonging to the wallet.\n" | |
3794 | " - \"ANY_ZADDR\": Merge notes from all z-addrs belonging to the wallet.\n" | |
3795 | " If a special string is given, any given addresses of that type will be ignored.\n" | |
3796 | " [\n" | |
3797 | " \"address\" (string) Can be a t-addr or a z-addr\n" | |
3798 | " ,...\n" | |
3799 | " ]\n" | |
3800 | "2. \"toaddress\" (string, required) The t-addr or z-addr to send the funds to.\n" | |
3801 | "3. fee (numeric, optional, default=" | |
3802 | + strprintf("%s", FormatMoney(MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n" | |
3803 | "4. transparent_limit (numeric, optional, default=" | |
3804 | + strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT) + ") Limit on the maximum number of UTXOs to merge. Set to 0 to use node option -mempooltxinputlimit.\n" | |
3805 | "4. shielded_limit (numeric, optional, default=" | |
3806 | + strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT) + ") Limit on the maximum number of notes to merge. Set to 0 to merge as many as will fit in the transaction.\n" | |
3807 | "5. \"memo\" (string, optional) Encoded as hex. When toaddress is a z-addr, this will be stored in the memo field of the new note.\n" | |
3808 | "\nResult:\n" | |
3809 | "{\n" | |
3810 | " \"remainingUTXOs\": xxx (numeric) Number of UTXOs still available for merging.\n" | |
3811 | " \"remainingTransparentValue\": xxx (numeric) Value of UTXOs still available for merging.\n" | |
3812 | " \"remainingNotes\": xxx (numeric) Number of notes still available for merging.\n" | |
3813 | " \"remainingShieldedValue\": xxx (numeric) Value of notes still available for merging.\n" | |
3814 | " \"mergingUTXOs\": xxx (numeric) Number of UTXOs being merged.\n" | |
3815 | " \"mergingTransparentValue\": xxx (numeric) Value of UTXOs being merged.\n" | |
3816 | " \"mergingNotes\": xxx (numeric) Number of notes being merged.\n" | |
3817 | " \"mergingShieldedValue\": xxx (numeric) Value of notes being merged.\n" | |
3818 | " \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" | |
3819 | "}\n" | |
3820 | "\nExamples:\n" | |
3821 | + HelpExampleCli("z_mergetoaddress", "'[\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"]' ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf") | |
3822 | + HelpExampleRpc("z_mergetoaddress", "[\"t1M72Sfpbz1BPpXFHz9m3CdqATR44Jvaydd\"], \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") | |
3823 | ); | |
3824 | ||
553a5c1a JG |
3825 | if (!fEnableMergeToAddress) { |
3826 | throw JSONRPCError(RPC_WALLET_ERROR, "Error: z_mergetoaddress is disabled."); | |
3827 | } | |
3828 | ||
6e9c7629 JG |
3829 | LOCK2(cs_main, pwalletMain->cs_wallet); |
3830 | ||
3831 | bool useAny = false; | |
3832 | bool useAnyUTXO = false; | |
3833 | bool useAnyNote = false; | |
3834 | std::set<CBitcoinAddress> taddrs = {}; | |
3835 | std::set<libzcash::PaymentAddress> zaddrs = {}; | |
3836 | ||
3837 | UniValue addresses = params[0].get_array(); | |
3838 | if (addresses.size()==0) | |
3839 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, fromaddresses array is empty."); | |
3840 | ||
3841 | // Keep track of addresses to spot duplicates | |
3842 | std::set<std::string> setAddress; | |
3843 | ||
3844 | // Sources | |
3845 | for (const UniValue& o : addresses.getValues()) { | |
3846 | if (!o.isStr()) | |
3847 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected string"); | |
3848 | ||
3849 | std::string address = o.get_str(); | |
3850 | if (address == "*") { | |
3851 | useAny = true; | |
3852 | } else if (address == "ANY_TADDR") { | |
3853 | useAnyUTXO = true; | |
3854 | } else if (address == "ANY_ZADDR") { | |
3855 | useAnyNote = true; | |
3856 | } else { | |
3857 | CBitcoinAddress taddr(address); | |
3858 | if (taddr.IsValid()) { | |
3859 | // Ignore any listed t-addrs if we are using all of them | |
3860 | if (!(useAny || useAnyUTXO)) { | |
3861 | taddrs.insert(taddr); | |
3862 | } | |
3863 | } else { | |
3864 | try { | |
3865 | CZCPaymentAddress zaddr(address); | |
3866 | // Ignore listed z-addrs if we are using all of them | |
3867 | if (!(useAny || useAnyNote)) { | |
3868 | zaddrs.insert(zaddr.Get()); | |
3869 | } | |
3870 | } catch (const std::runtime_error&) { | |
3871 | throw JSONRPCError( | |
3872 | RPC_INVALID_PARAMETER, | |
3873 | string("Invalid parameter, unknown address format: ") + address); | |
3874 | } | |
3875 | } | |
3876 | } | |
3877 | ||
3878 | if (setAddress.count(address)) | |
3879 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + address); | |
3880 | setAddress.insert(address); | |
3881 | } | |
3882 | ||
3883 | // Validate the destination address | |
3884 | auto destaddress = params[1].get_str(); | |
3885 | bool isToZaddr = false; | |
3886 | CBitcoinAddress taddr(destaddress); | |
3887 | if (!taddr.IsValid()) { | |
3888 | try { | |
3889 | CZCPaymentAddress zaddr(destaddress); | |
3890 | zaddr.Get(); | |
3891 | isToZaddr = true; | |
3892 | } catch (const std::runtime_error&) { | |
3893 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); | |
3894 | } | |
3895 | } | |
3896 | ||
3897 | // Convert fee from currency format to zatoshis | |
3898 | CAmount nFee = SHIELD_COINBASE_DEFAULT_MINERS_FEE; | |
3899 | if (params.size() > 2) { | |
3900 | if (params[2].get_real() == 0.0) { | |
3901 | nFee = 0; | |
3902 | } else { | |
3903 | nFee = AmountFromValue( params[2] ); | |
3904 | } | |
3905 | } | |
3906 | ||
3907 | int nUTXOLimit = MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT; | |
3908 | if (params.size() > 3) { | |
3909 | nUTXOLimit = params[3].get_int(); | |
3910 | if (nUTXOLimit < 0) { | |
3911 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of UTXOs cannot be negative"); | |
3912 | } | |
3913 | } | |
3914 | ||
3915 | int nNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT; | |
3916 | if (params.size() > 4) { | |
3917 | nNoteLimit = params[4].get_int(); | |
3918 | if (nNoteLimit < 0) { | |
3919 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of notes cannot be negative"); | |
3920 | } | |
3921 | } | |
3922 | ||
3923 | std::string memo; | |
3924 | if (params.size() > 5) { | |
3925 | memo = params[5].get_str(); | |
3926 | if (!isToZaddr) { | |
3927 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo can not be used with a taddr. It can only be used with a zaddr."); | |
3928 | } else if (!IsHex(memo)) { | |
3929 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format."); | |
3930 | } | |
3931 | if (memo.length() > ZC_MEMO_SIZE*2) { | |
3932 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE )); | |
3933 | } | |
3934 | } | |
3935 | ||
3936 | MergeToAddressRecipient recipient(destaddress, memo); | |
3937 | ||
3938 | // Prepare to get UTXOs and notes | |
3939 | std::vector<MergeToAddressInputUTXO> utxoInputs; | |
3940 | std::vector<MergeToAddressInputNote> noteInputs; | |
3941 | CAmount mergedUTXOValue = 0; | |
3942 | CAmount mergedNoteValue = 0; | |
3943 | CAmount remainingUTXOValue = 0; | |
3944 | CAmount remainingNoteValue = 0; | |
3945 | size_t utxoCounter = 0; | |
3946 | size_t noteCounter = 0; | |
3947 | bool maxedOutUTXOsFlag = false; | |
3948 | bool maxedOutNotesFlag = false; | |
3949 | size_t mempoolLimit = (nUTXOLimit != 0) ? nUTXOLimit : (size_t)GetArg("-mempooltxinputlimit", 0); | |
3950 | ||
3951 | size_t estimatedTxSize = 200; // tx overhead + wiggle room | |
3952 | if (isToZaddr) { | |
3953 | estimatedTxSize += JOINSPLIT_SIZE; | |
3954 | } | |
3955 | ||
3956 | if (useAny || useAnyUTXO || taddrs.size() > 0) { | |
3957 | // Get available utxos | |
3958 | vector<COutput> vecOutputs; | |
3959 | pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, false); | |
3960 | ||
3961 | // Find unspent utxos and update estimated size | |
3962 | for (const COutput& out : vecOutputs) { | |
3963 | if (!out.fSpendable) { | |
3964 | continue; | |
3965 | } | |
3966 | ||
3967 | CTxDestination address; | |
3968 | if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { | |
3969 | continue; | |
3970 | } | |
3971 | // If taddr is not wildcard "*", filter utxos | |
3972 | if (taddrs.size() > 0 && !taddrs.count(address)) { | |
3973 | continue; | |
3974 | } | |
3975 | ||
3976 | utxoCounter++; | |
3977 | CAmount nValue = out.tx->vout[out.i].nValue; | |
3978 | ||
3979 | if (!maxedOutUTXOsFlag) { | |
3980 | CBitcoinAddress ba(address); | |
3981 | size_t increase = (ba.IsScript()) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_DUST_SIZE; | |
3982 | if (estimatedTxSize + increase >= MAX_TX_SIZE || | |
3983 | (mempoolLimit > 0 && utxoCounter > mempoolLimit)) | |
3984 | { | |
3985 | maxedOutUTXOsFlag = true; | |
3986 | } else { | |
3987 | estimatedTxSize += increase; | |
3988 | COutPoint utxo(out.tx->GetHash(), out.i); | |
3989 | utxoInputs.emplace_back(utxo, nValue); | |
3990 | mergedUTXOValue += nValue; | |
3991 | } | |
3992 | } | |
3993 | ||
3994 | if (maxedOutUTXOsFlag) { | |
3995 | remainingUTXOValue += nValue; | |
3996 | } | |
3997 | } | |
3998 | } | |
3999 | ||
4000 | if (useAny || useAnyNote || zaddrs.size() > 0) { | |
4001 | // Get available notes | |
4002 | std::vector<CNotePlaintextEntry> entries; | |
4003 | pwalletMain->GetFilteredNotes(entries, zaddrs); | |
4004 | ||
4005 | // Find unspent notes and update estimated size | |
4006 | for (CNotePlaintextEntry& entry : entries) { | |
4007 | noteCounter++; | |
4008 | CAmount nValue = entry.plaintext.value; | |
4009 | ||
4010 | if (!maxedOutNotesFlag) { | |
4011 | // If we haven't added any notes yet and the merge is to a | |
4012 | // z-address, we have already accounted for the first JoinSplit. | |
4013 | size_t increase = (noteInputs.empty() && !isToZaddr) || (noteInputs.size() % 2 == 0) ? JOINSPLIT_SIZE : 0; | |
4014 | if (estimatedTxSize + increase >= MAX_TX_SIZE || | |
4015 | (nNoteLimit > 0 && noteCounter > nNoteLimit)) | |
4016 | { | |
4017 | maxedOutNotesFlag = true; | |
4018 | } else { | |
4019 | estimatedTxSize += increase; | |
4020 | SpendingKey zkey; | |
4021 | pwalletMain->GetSpendingKey(entry.address, zkey); | |
4022 | noteInputs.emplace_back(entry.jsop, entry.plaintext.note(entry.address), nValue, zkey); | |
4023 | mergedNoteValue += nValue; | |
4024 | } | |
4025 | } | |
4026 | ||
4027 | if (maxedOutNotesFlag) { | |
4028 | remainingNoteValue += nValue; | |
4029 | } | |
4030 | } | |
4031 | } | |
4032 | ||
4033 | size_t numUtxos = utxoInputs.size(); | |
4034 | size_t numNotes = noteInputs.size(); | |
4035 | ||
4036 | if (numUtxos == 0 && numNotes == 0) { | |
4037 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any funds to merge."); | |
4038 | } | |
4039 | ||
4040 | // Sanity check: Don't do anything if: | |
4041 | // - We only have one from address | |
4042 | // - It's equal to toaddress | |
4043 | // - The address only contains a single UTXO or note | |
4044 | if (setAddress.size() == 1 && setAddress.count(destaddress) && (numUtxos + numNotes) == 1) { | |
4045 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Destination address is also the only source address, and all its funds are already merged."); | |
4046 | } | |
4047 | ||
4048 | CAmount mergedValue = mergedUTXOValue + mergedNoteValue; | |
4049 | if (mergedValue < nFee) { | |
4050 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, | |
4051 | strprintf("Insufficient funds, have %s, which is less than miners fee %s", | |
4052 | FormatMoney(mergedValue), FormatMoney(nFee))); | |
4053 | } | |
4054 | ||
4055 | // Check that the user specified fee is sane (if too high, it can result in error -25 absurd fee) | |
4056 | CAmount netAmount = mergedValue - nFee; | |
4057 | if (nFee > netAmount) { | |
4058 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the net amount to be shielded %s", FormatMoney(nFee), FormatMoney(netAmount))); | |
4059 | } | |
4060 | ||
4061 | // Keep record of parameters in context object | |
4062 | UniValue contextInfo(UniValue::VOBJ); | |
4063 | contextInfo.push_back(Pair("fromaddresses", params[0])); | |
4064 | contextInfo.push_back(Pair("toaddress", params[1])); | |
4065 | contextInfo.push_back(Pair("fee", ValueFromAmount(nFee))); | |
4066 | ||
4067 | // Contextual transaction we will build on | |
4068 | CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction( | |
4069 | Params().GetConsensus(), | |
4070 | chainActive.Height() + 1); | |
4071 | bool isShielded = numNotes > 0 || isToZaddr; | |
4072 | if (contextualTx.nVersion == 1 && isShielded) { | |
4073 | contextualTx.nVersion = 2; // Tx format should support vjoinsplit | |
4074 | } | |
4075 | ||
4076 | // Create operation and add to global queue | |
4077 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); | |
4078 | std::shared_ptr<AsyncRPCOperation> operation( | |
4079 | new AsyncRPCOperation_mergetoaddress(contextualTx, utxoInputs, noteInputs, recipient, nFee, contextInfo) ); | |
4080 | q->addOperation(operation); | |
4081 | AsyncRPCOperationId operationId = operation->getId(); | |
4082 | ||
4083 | // Return continuation information | |
4084 | UniValue o(UniValue::VOBJ); | |
4085 | o.push_back(Pair("remainingUTXOs", utxoCounter - numUtxos)); | |
4086 | o.push_back(Pair("remainingTransparentValue", ValueFromAmount(remainingUTXOValue))); | |
4087 | o.push_back(Pair("remainingNotes", noteCounter - numNotes)); | |
4088 | o.push_back(Pair("remainingShieldedValue", ValueFromAmount(remainingNoteValue))); | |
4089 | o.push_back(Pair("mergingUTXOs", numUtxos)); | |
4090 | o.push_back(Pair("mergingTransparentValue", ValueFromAmount(mergedUTXOValue))); | |
4091 | o.push_back(Pair("mergingNotes", numNotes)); | |
4092 | o.push_back(Pair("mergingShieldedValue", ValueFromAmount(mergedNoteValue))); | |
4093 | o.push_back(Pair("opid", operationId)); | |
4094 | return o; | |
4095 | } | |
4096 | ||
4097 | ||
0d37ae3a | 4098 | UniValue z_listoperationids(const UniValue& params, bool fHelp) |
34f0001c S |
4099 | { |
4100 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 4101 | return NullUniValue; |
34f0001c S |
4102 | |
4103 | if (fHelp || params.size() > 1) | |
4104 | throw runtime_error( | |
4105 | "z_listoperationids\n" | |
4106 | "\nReturns the list of operation ids currently known to the wallet.\n" | |
4107 | "\nArguments:\n" | |
4108 | "1. \"status\" (string, optional) Filter result by the operation's state state e.g. \"success\".\n" | |
4109 | "\nResult:\n" | |
4110 | "[ (json array of string)\n" | |
4111 | " \"operationid\" (string) an operation id belonging to the wallet\n" | |
4112 | " ,...\n" | |
4113 | "]\n" | |
4114 | "\nExamples:\n" | |
4115 | + HelpExampleCli("z_listoperationids", "") | |
4116 | + HelpExampleRpc("z_listoperationids", "") | |
4117 | ); | |
4118 | ||
4119 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
4120 | ||
4121 | std::string filter; | |
4122 | bool useFilter = false; | |
4123 | if (params.size()==1) { | |
4124 | filter = params[0].get_str(); | |
4125 | useFilter = true; | |
4126 | } | |
4127 | ||
0d37ae3a | 4128 | UniValue ret(UniValue::VARR); |
34f0001c S |
4129 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); |
4130 | std::vector<AsyncRPCOperationId> ids = q->getAllOperationIds(); | |
4131 | for (auto id : ids) { | |
4132 | std::shared_ptr<AsyncRPCOperation> operation = q->getOperationForId(id); | |
4133 | if (!operation) { | |
4134 | continue; | |
4135 | } | |
4136 | std::string state = operation->getStateAsString(); | |
4137 | if (useFilter && filter.compare(state)!=0) | |
4138 | continue; | |
4139 | ret.push_back(id); | |
4140 | } | |
4141 | ||
4142 | return ret; | |
4143 | } |