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