]>
Commit | Line | Data |
---|---|---|
e3bc5698 | 1 | // Copyright (c) 2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2014 The Bitcoin Core developers |
72fb3d29 | 3 | // Distributed under the MIT software license, see the accompanying |
e3bc5698 JG |
4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
5 | ||
eda37330 | 6 | #include "amount.h" |
51ed9ec9 | 7 | #include "base58.h" |
be126699 | 8 | #include "consensus/upgrades.h" |
ae775b5b | 9 | #include "core_io.h" |
e3bc5698 | 10 | #include "init.h" |
8a893c94 | 11 | #include "main.h" |
51ed9ec9 BD |
12 | #include "net.h" |
13 | #include "netbase.h" | |
8a893c94 | 14 | #include "rpcserver.h" |
14f888ca | 15 | #include "timedata.h" |
51ed9ec9 | 16 | #include "util.h" |
b93173de | 17 | #include "utilmoneystr.h" |
51ed9ec9 BD |
18 | #include "wallet.h" |
19 | #include "walletdb.h" | |
8cb25088 | 20 | #include "primitives/transaction.h" |
6962bb3d | 21 | #include "zcbenchmarks.h" |
6aae9d1a | 22 | #include "script/interpreter.h" |
51ed9ec9 | 23 | |
fc72c078 S |
24 | #include "utiltime.h" |
25 | #include "asyncrpcoperation.h" | |
ed21d5bd | 26 | #include "asyncrpcqueue.h" |
6e9c7629 | 27 | #include "wallet/asyncrpcoperation_mergetoaddress.h" |
fc72c078 | 28 | #include "wallet/asyncrpcoperation_sendmany.h" |
06c19063 | 29 | #include "wallet/asyncrpcoperation_shieldcoinbase.h" |
fc72c078 | 30 | |
320f2cc7 SB |
31 | #include "sodium.h" |
32 | ||
51ed9ec9 BD |
33 | #include <stdint.h> |
34 | ||
35 | #include <boost/assign/list_of.hpp> | |
25cf6f3d | 36 | |
a10a6e2a | 37 | #include <univalue.h> |
e3bc5698 | 38 | |
bcbde86a S |
39 | #include <numeric> |
40 | ||
8e0ff2b7 | 41 | |
e3bc5698 JG |
42 | using namespace std; |
43 | ||
2dc35992 SB |
44 | using namespace libzcash; |
45 | ||
1333d0d7 | 46 | extern char ASSETCHAINS_SYMBOL[KOMODO_ASSETCHAIN_MAXLEN]; |
0d37ae3a | 47 | extern UniValue TxJoinSplitToJSON(const CTransaction& tx); |
2f151c30 | 48 | extern uint8_t ASSETCHAINS_PRIVATE; |
890e708b | 49 | uint32_t komodo_segid32(char *coinaddr); |
f7cfb52d | 50 | |
51ed9ec9 | 51 | int64_t nWalletUnlockTime; |
e3bc5698 | 52 | static CCriticalSection cs_nWalletUnlockTime; |
8e0ff2b7 | 53 | std::string CCerror; |
e3bc5698 | 54 | |
c1eae280 | 55 | // Private method: |
0d37ae3a | 56 | UniValue z_getoperationstatus_IMPL(const UniValue&, bool); |
c1eae280 | 57 | |
82004353 JDL |
58 | #define PLAN_NAME_MAX 8 |
59 | #define VALID_PLAN_NAME(x) (strlen(x) <= PLAN_NAME_MAX) | |
60 | ||
bdab0cf5 | 61 | std::string HelpRequiringPassphrase() |
e3bc5698 | 62 | { |
b0730874 | 63 | return pwalletMain && pwalletMain->IsCrypted() |
a6099ef3 | 64 | ? "\nRequires wallet passphrase to be set with walletpassphrase call." |
e3bc5698 JG |
65 | : ""; |
66 | } | |
67 | ||
b9fb692d JS |
68 | bool EnsureWalletIsAvailable(bool avoidException) |
69 | { | |
70 | if (!pwalletMain) | |
71 | { | |
72 | if (!avoidException) | |
73 | throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Method not found (disabled)"); | |
74 | else | |
75 | return false; | |
76 | } | |
77 | return true; | |
78 | } | |
79 | ||
bdab0cf5 | 80 | void EnsureWalletIsUnlocked() |
e3bc5698 JG |
81 | { |
82 | if (pwalletMain->IsLocked()) | |
738835d7 | 83 | throw JSONRPCError(RPC_WALLET_UNLOCK_NEEDED, "Error: Please enter the wallet passphrase with walletpassphrase first."); |
e3bc5698 JG |
84 | } |
85 | ||
c60397dd | 86 | uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 hash,int32_t n,int32_t checkheight,uint64_t checkvalue,int32_t tipheight); |
15bcd69e | 87 | |
d014114d | 88 | void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry) |
e3bc5698 | 89 | { |
cbe3042f | 90 | //int32_t i,n,txheight; uint32_t locktime; uint64_t interest = 0; |
91 | int confirms = wtx.GetDepthInMainChain(); | |
e3bc5698 | 92 | entry.push_back(Pair("confirmations", confirms)); |
e07c8e91 LD |
93 | if (wtx.IsCoinBase()) |
94 | entry.push_back(Pair("generated", true)); | |
2b72d46f | 95 | if (confirms > 0) |
e3bc5698 JG |
96 | { |
97 | entry.push_back(Pair("blockhash", wtx.hashBlock.GetHex())); | |
98 | entry.push_back(Pair("blockindex", wtx.nIndex)); | |
209377a7 | 99 | entry.push_back(Pair("blocktime", mapBlockIndex[wtx.hashBlock]->GetBlockTime())); |
9bb37bf0 | 100 | entry.push_back(Pair("expiryheight", (int64_t)wtx.nExpiryHeight)); |
e3bc5698 | 101 | } |
805344dc | 102 | uint256 hash = wtx.GetHash(); |
731b89b8 | 103 | entry.push_back(Pair("txid", hash.GetHex())); |
38fc4b70 | 104 | UniValue conflicts(UniValue::VARR); |
731b89b8 GA |
105 | BOOST_FOREACH(const uint256& conflict, wtx.GetConflicts()) |
106 | conflicts.push_back(conflict.GetHex()); | |
107 | entry.push_back(Pair("walletconflicts", conflicts)); | |
d56e30ca | 108 | entry.push_back(Pair("time", wtx.GetTxTime())); |
4b61a6a4 | 109 | entry.push_back(Pair("timereceived", (int64_t)wtx.nTimeReceived)); |
e3bc5698 JG |
110 | BOOST_FOREACH(const PAIRTYPE(string,string)& item, wtx.mapValue) |
111 | entry.push_back(Pair(item.first, item.second)); | |
f7cfb52d S |
112 | |
113 | entry.push_back(Pair("vjoinsplit", TxJoinSplitToJSON(wtx))); | |
e3bc5698 JG |
114 | } |
115 | ||
d014114d | 116 | string AccountFromValue(const UniValue& value) |
e3bc5698 JG |
117 | { |
118 | string strAccount = value.get_str(); | |
8b6a0cb8 | 119 | //if (strAccount != "") |
120 | // throw JSONRPCError(RPC_WALLET_ACCOUNTS_UNSUPPORTED, "Accounts are unsupported"); | |
e3bc5698 JG |
121 | return strAccount; |
122 | } | |
123 | ||
fd2fd9a7 AL |
124 | char *komodo_chainname() |
125 | { | |
126 | return(ASSETCHAINS_SYMBOL[0] == 0 ? (char *)"KMD" : ASSETCHAINS_SYMBOL); | |
1333d0d7 | 127 | } |
128 | ||
d014114d | 129 | UniValue getnewaddress(const UniValue& params, bool fHelp) |
e3bc5698 | 130 | { |
b9fb692d | 131 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 132 | return NullUniValue; |
9d365796 | 133 | |
e3bc5698 JG |
134 | if (fHelp || params.size() > 1) |
135 | throw runtime_error( | |
a6099ef3 | 136 | "getnewaddress ( \"account\" )\n" |
1333d0d7 | 137 | "\nReturns a new " + strprintf("%s",komodo_chainname()) + " address for receiving payments.\n" |
a6099ef3 | 138 | "\nArguments:\n" |
3c31eb24 | 139 | "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 | 140 | "\nResult:\n" |
1333d0d7 | 141 | "\"" + strprintf("%s",komodo_chainname()) + "_address\" (string) The new " + strprintf("%s",komodo_chainname()) + " address\n" |
a6099ef3 | 142 | "\nExamples:\n" |
143 | + HelpExampleCli("getnewaddress", "") | |
7b782f5b | 144 | + HelpExampleRpc("getnewaddress", "") |
a6099ef3 | 145 | ); |
e3bc5698 | 146 | |
4401b2d7 EL |
147 | LOCK2(cs_main, pwalletMain->cs_wallet); |
148 | ||
e3bc5698 JG |
149 | // Parse the account first so we don't generate a key if there's an error |
150 | string strAccount; | |
151 | if (params.size() > 0) | |
152 | strAccount = AccountFromValue(params[0]); | |
153 | ||
154 | if (!pwalletMain->IsLocked()) | |
155 | pwalletMain->TopUpKeyPool(); | |
156 | ||
157 | // Generate a new key that is added to wallet | |
158 | CPubKey newKey; | |
71ac5052 | 159 | if (!pwalletMain->GetKeyFromPool(newKey)) |
738835d7 | 160 | throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); |
e3bc5698 JG |
161 | CKeyID keyID = newKey.GetID(); |
162 | ||
a41d5fe0 | 163 | pwalletMain->SetAddressBook(keyID, strAccount, "receive"); |
e3bc5698 JG |
164 | |
165 | return CBitcoinAddress(keyID).ToString(); | |
166 | } | |
167 | ||
168 | ||
169 | CBitcoinAddress GetAccountAddress(string strAccount, bool bForceNew=false) | |
170 | { | |
171 | CWalletDB walletdb(pwalletMain->strWalletFile); | |
172 | ||
173 | CAccount account; | |
174 | walletdb.ReadAccount(strAccount, account); | |
175 | ||
176 | bool bKeyUsed = false; | |
177 | ||
178 | // Check if the current key has been used | |
179 | if (account.vchPubKey.IsValid()) | |
180 | { | |
0be990ba | 181 | CScript scriptPubKey = GetScriptForDestination(account.vchPubKey.GetID()); |
e3bc5698 JG |
182 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); |
183 | it != pwalletMain->mapWallet.end() && account.vchPubKey.IsValid(); | |
184 | ++it) | |
185 | { | |
186 | const CWalletTx& wtx = (*it).second; | |
187 | BOOST_FOREACH(const CTxOut& txout, wtx.vout) | |
188 | if (txout.scriptPubKey == scriptPubKey) | |
189 | bKeyUsed = true; | |
190 | } | |
191 | } | |
192 | ||
193 | // Generate a new key | |
194 | if (!account.vchPubKey.IsValid() || bForceNew || bKeyUsed) | |
195 | { | |
71ac5052 | 196 | if (!pwalletMain->GetKeyFromPool(account.vchPubKey)) |
738835d7 | 197 | throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); |
e3bc5698 | 198 | |
a41d5fe0 | 199 | pwalletMain->SetAddressBook(account.vchPubKey.GetID(), strAccount, "receive"); |
e3bc5698 JG |
200 | walletdb.WriteAccount(strAccount, account); |
201 | } | |
202 | ||
203 | return CBitcoinAddress(account.vchPubKey.GetID()); | |
204 | } | |
205 | ||
d014114d | 206 | UniValue getaccountaddress(const UniValue& params, bool fHelp) |
e3bc5698 | 207 | { |
b9fb692d | 208 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 209 | return NullUniValue; |
9d365796 | 210 | |
e3bc5698 JG |
211 | if (fHelp || params.size() != 1) |
212 | throw runtime_error( | |
a6099ef3 | 213 | "getaccountaddress \"account\"\n" |
1333d0d7 | 214 | "\nDEPRECATED. Returns the current " + strprintf("%s",komodo_chainname()) + " address for receiving payments to this account.\n" |
a6099ef3 | 215 | "\nArguments:\n" |
3c31eb24 | 216 | "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 | 217 | "\nResult:\n" |
1333d0d7 | 218 | "\"" + strprintf("%s",komodo_chainname()) + "_address\" (string) The account " + strprintf("%s",komodo_chainname()) + " address\n" |
a6099ef3 | 219 | "\nExamples:\n" |
220 | + HelpExampleCli("getaccountaddress", "") | |
221 | + HelpExampleCli("getaccountaddress", "\"\"") | |
222 | + HelpExampleCli("getaccountaddress", "\"myaccount\"") | |
223 | + HelpExampleRpc("getaccountaddress", "\"myaccount\"") | |
224 | ); | |
e3bc5698 | 225 | |
4401b2d7 EL |
226 | LOCK2(cs_main, pwalletMain->cs_wallet); |
227 | ||
e3bc5698 JG |
228 | // Parse the account first so we don't generate a key if there's an error |
229 | string strAccount = AccountFromValue(params[0]); | |
230 | ||
d014114d | 231 | UniValue ret(UniValue::VSTR); |
e3bc5698 JG |
232 | |
233 | ret = GetAccountAddress(strAccount).ToString(); | |
e3bc5698 JG |
234 | return ret; |
235 | } | |
236 | ||
237 | ||
d014114d | 238 | UniValue getrawchangeaddress(const UniValue& params, bool fHelp) |
e5e9904c | 239 | { |
b9fb692d | 240 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 241 | return NullUniValue; |
9d365796 | 242 | |
e5e9904c JG |
243 | if (fHelp || params.size() > 1) |
244 | throw runtime_error( | |
245 | "getrawchangeaddress\n" | |
1333d0d7 | 246 | "\nReturns a new " + strprintf("%s",komodo_chainname()) + " address, for receiving change.\n" |
a6099ef3 | 247 | "This is for use with raw transactions, NOT normal use.\n" |
248 | "\nResult:\n" | |
249 | "\"address\" (string) The address\n" | |
250 | "\nExamples:\n" | |
251 | + HelpExampleCli("getrawchangeaddress", "") | |
252 | + HelpExampleRpc("getrawchangeaddress", "") | |
253 | ); | |
e5e9904c | 254 | |
4401b2d7 EL |
255 | LOCK2(cs_main, pwalletMain->cs_wallet); |
256 | ||
e5e9904c JG |
257 | if (!pwalletMain->IsLocked()) |
258 | pwalletMain->TopUpKeyPool(); | |
259 | ||
260 | CReserveKey reservekey(pwalletMain); | |
261 | CPubKey vchPubKey; | |
262 | if (!reservekey.GetReservedKey(vchPubKey)) | |
6c37f7fd | 263 | throw JSONRPCError(RPC_WALLET_KEYPOOL_RAN_OUT, "Error: Keypool ran out, please call keypoolrefill first"); |
e5e9904c JG |
264 | |
265 | reservekey.KeepKey(); | |
266 | ||
267 | CKeyID keyID = vchPubKey.GetID(); | |
268 | ||
269 | return CBitcoinAddress(keyID).ToString(); | |
270 | } | |
271 | ||
e3bc5698 | 272 | |
d014114d | 273 | UniValue setaccount(const UniValue& params, bool fHelp) |
e3bc5698 | 274 | { |
b9fb692d | 275 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 276 | return NullUniValue; |
9d365796 | 277 | |
e3bc5698 JG |
278 | if (fHelp || params.size() < 1 || params.size() > 2) |
279 | throw runtime_error( | |
1333d0d7 | 280 | "setaccount \"" + strprintf("%s",komodo_chainname()) + "_address\" \"account\"\n" |
7b782f5b | 281 | "\nDEPRECATED. Sets the account associated with the given address.\n" |
a6099ef3 | 282 | "\nArguments:\n" |
1333d0d7 | 283 | "1. \"" + strprintf("%s",komodo_chainname()) + "_address\" (string, required) The " + strprintf("%s",komodo_chainname()) + " address to be associated with an account.\n" |
3c31eb24 | 284 | "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 | 285 | "\nExamples:\n" |
1333d0d7 | 286 | + HelpExampleCli("setaccount", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" \"tabby\"") |
287 | + HelpExampleRpc("setaccount", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", \"tabby\"") | |
a6099ef3 | 288 | ); |
e3bc5698 | 289 | |
4401b2d7 EL |
290 | LOCK2(cs_main, pwalletMain->cs_wallet); |
291 | ||
e3bc5698 JG |
292 | CBitcoinAddress address(params[0].get_str()); |
293 | if (!address.IsValid()) | |
1333d0d7 | 294 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid " + strprintf("%s",komodo_chainname()) + " address"); |
e3bc5698 | 295 | |
e3bc5698 JG |
296 | string strAccount; |
297 | if (params.size() > 1) | |
298 | strAccount = AccountFromValue(params[1]); | |
299 | ||
31d6390f ES |
300 | // Only add the account if the address is yours. |
301 | if (IsMine(*pwalletMain, address.Get())) | |
e3bc5698 | 302 | { |
31d6390f ES |
303 | // Detect when changing the account of an address that is the 'unused current key' of another account: |
304 | if (pwalletMain->mapAddressBook.count(address.Get())) | |
305 | { | |
306 | string strOldAccount = pwalletMain->mapAddressBook[address.Get()].name; | |
307 | if (address == GetAccountAddress(strOldAccount)) | |
308 | GetAccountAddress(strOldAccount, true); | |
309 | } | |
310 | pwalletMain->SetAddressBook(address.Get(), strAccount, "receive"); | |
e3bc5698 | 311 | } |
31d6390f ES |
312 | else |
313 | throw JSONRPCError(RPC_MISC_ERROR, "setaccount can only be used with own address"); | |
e3bc5698 | 314 | |
ed21d5bd | 315 | return NullUniValue; |
e3bc5698 JG |
316 | } |
317 | ||
318 | ||
d014114d | 319 | UniValue getaccount(const UniValue& params, bool fHelp) |
e3bc5698 | 320 | { |
b9fb692d | 321 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 322 | return NullUniValue; |
9d365796 | 323 | |
e3bc5698 JG |
324 | if (fHelp || params.size() != 1) |
325 | throw runtime_error( | |
1333d0d7 | 326 | "getaccount \"" + strprintf("%s",komodo_chainname()) + "_address\"\n" |
7b782f5b | 327 | "\nDEPRECATED. Returns the account associated with the given address.\n" |
a6099ef3 | 328 | "\nArguments:\n" |
1333d0d7 | 329 | "1. \"" + strprintf("%s",komodo_chainname()) + "_address\" (string, required) The " + strprintf("%s",komodo_chainname()) + " address for account lookup.\n" |
a6099ef3 | 330 | "\nResult:\n" |
331 | "\"accountname\" (string) the account address\n" | |
332 | "\nExamples:\n" | |
1333d0d7 | 333 | + HelpExampleCli("getaccount", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"") |
334 | + HelpExampleRpc("getaccount", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"") | |
a6099ef3 | 335 | ); |
e3bc5698 | 336 | |
4401b2d7 EL |
337 | LOCK2(cs_main, pwalletMain->cs_wallet); |
338 | ||
e3bc5698 JG |
339 | CBitcoinAddress address(params[0].get_str()); |
340 | if (!address.IsValid()) | |
1333d0d7 | 341 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid " + strprintf("%s",komodo_chainname()) + " address"); |
e3bc5698 JG |
342 | |
343 | string strAccount; | |
61885513 GA |
344 | map<CTxDestination, CAddressBookData>::iterator mi = pwalletMain->mapAddressBook.find(address.Get()); |
345 | if (mi != pwalletMain->mapAddressBook.end() && !(*mi).second.name.empty()) | |
346 | strAccount = (*mi).second.name; | |
e3bc5698 JG |
347 | return strAccount; |
348 | } | |
349 | ||
350 | ||
d014114d | 351 | UniValue getaddressesbyaccount(const UniValue& params, bool fHelp) |
e3bc5698 | 352 | { |
b9fb692d | 353 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 354 | return NullUniValue; |
9d365796 | 355 | |
e3bc5698 JG |
356 | if (fHelp || params.size() != 1) |
357 | throw runtime_error( | |
a6099ef3 | 358 | "getaddressesbyaccount \"account\"\n" |
7b782f5b | 359 | "\nDEPRECATED. Returns the list of addresses for the given account.\n" |
a6099ef3 | 360 | "\nArguments:\n" |
3c31eb24 | 361 | "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 | 362 | "\nResult:\n" |
363 | "[ (json array of string)\n" | |
1333d0d7 | 364 | " \"" + strprintf("%s",komodo_chainname()) + "_address\" (string) a " + strprintf("%s",komodo_chainname()) + " address associated with the given account\n" |
a6099ef3 | 365 | " ,...\n" |
366 | "]\n" | |
367 | "\nExamples:\n" | |
368 | + HelpExampleCli("getaddressesbyaccount", "\"tabby\"") | |
369 | + HelpExampleRpc("getaddressesbyaccount", "\"tabby\"") | |
370 | ); | |
e3bc5698 | 371 | |
4401b2d7 EL |
372 | LOCK2(cs_main, pwalletMain->cs_wallet); |
373 | ||
e3bc5698 JG |
374 | string strAccount = AccountFromValue(params[0]); |
375 | ||
376 | // Find all addresses that have the given account | |
38fc4b70 | 377 | UniValue ret(UniValue::VARR); |
61885513 | 378 | BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) |
e3bc5698 JG |
379 | { |
380 | const CBitcoinAddress& address = item.first; | |
61885513 | 381 | const string& strName = item.second.name; |
e3bc5698 JG |
382 | if (strName == strAccount) |
383 | ret.push_back(address.ToString()); | |
384 | } | |
385 | return ret; | |
386 | } | |
387 | ||
9415da7f | 388 | static void SendMoney(const CTxDestination &address, CAmount nValue, bool fSubtractFeeFromAmount, CWalletTx& wtxNew,uint8_t *opretbuf,int32_t opretlen,long int opretValue) |
b93173de | 389 | { |
25cf6f3d PK |
390 | CAmount curBalance = pwalletMain->GetBalance(); |
391 | ||
b93173de PJ |
392 | // Check amount |
393 | if (nValue <= 0) | |
4be639ea | 394 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid amount"); |
b93173de | 395 | |
25cf6f3d | 396 | if (nValue > curBalance) |
b93173de PJ |
397 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds"); |
398 | ||
58c4c0bb | 399 | // Parse Zcash address |
b93173de PJ |
400 | CScript scriptPubKey = GetScriptForDestination(address); |
401 | ||
402 | // Create and send the transaction | |
403 | CReserveKey reservekey(pwalletMain); | |
404 | CAmount nFeeRequired; | |
25cf6f3d | 405 | std::string strError; |
292623ad CL |
406 | vector<CRecipient> vecSend; |
407 | int nChangePosRet = -1; | |
408 | CRecipient recipient = {scriptPubKey, nValue, fSubtractFeeFromAmount}; | |
409 | vecSend.push_back(recipient); | |
b62d7030 | 410 | if ( opretlen > 0 && opretbuf != 0 ) |
411 | { | |
412 | CScript opretpubkey; int32_t i; uint8_t *ptr; | |
413 | opretpubkey.resize(opretlen); | |
414 | ptr = (uint8_t *)opretpubkey.data(); | |
415 | for (i=0; i<opretlen; i++) | |
4aa2c64e | 416 | { |
353edf5d | 417 | ptr[i] = opretbuf[i]; |
1919e9de | 418 | //printf("%02x",ptr[i]); |
4aa2c64e | 419 | } |
1919e9de | 420 | //printf(" opretbuf[%d]\n",opretlen); |
b62d7030 | 421 | CRecipient opret = { opretpubkey, opretValue, false }; |
422 | vecSend.push_back(opret); | |
423 | } | |
292623ad CL |
424 | if (!pwalletMain->CreateTransaction(vecSend, wtxNew, reservekey, nFeeRequired, nChangePosRet, strError)) { |
425 | if (!fSubtractFeeFromAmount && nValue + nFeeRequired > pwalletMain->GetBalance()) | |
426 | 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)); | |
427 | throw JSONRPCError(RPC_WALLET_ERROR, strError); | |
b93173de PJ |
428 | } |
429 | if (!pwalletMain->CommitTransaction(wtxNew, reservekey)) | |
430 | 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 |
431 | } |
432 | ||
d014114d | 433 | UniValue sendtoaddress(const UniValue& params, bool fHelp) |
e3bc5698 | 434 | { |
b9fb692d | 435 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 436 | return NullUniValue; |
9d365796 | 437 | |
292623ad | 438 | if (fHelp || params.size() < 2 || params.size() > 5) |
e3bc5698 | 439 | throw runtime_error( |
1333d0d7 | 440 | "sendtoaddress \"" + strprintf("%s",komodo_chainname()) + "_address\" amount ( \"comment\" \"comment-to\" subtractfeefromamount )\n" |
e743678d | 441 | "\nSend an amount to a given address. The amount is a real and is rounded to the nearest 0.00000001\n" |
a6099ef3 | 442 | + HelpRequiringPassphrase() + |
443 | "\nArguments:\n" | |
1333d0d7 | 444 | "1. \"" + strprintf("%s",komodo_chainname()) + "_address\" (string, required) The " + strprintf("%s",komodo_chainname()) + " address to send to.\n" |
445 | "2. \"amount\" (numeric, required) The amount in " + strprintf("%s",komodo_chainname()) + " to send. eg 0.1\n" | |
a6099ef3 | 446 | "3. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" |
447 | " This is not part of the transaction, just kept in your wallet.\n" | |
448 | "4. \"comment-to\" (string, optional) A comment to store the name of the person or organization \n" | |
449 | " to which you're sending the transaction. This is not part of the \n" | |
450 | " transaction, just kept in your wallet.\n" | |
292623ad | 451 | "5. subtractfeefromamount (boolean, optional, default=false) The fee will be deducted from the amount being sent.\n" |
1333d0d7 | 452 | " The recipient will receive less " + strprintf("%s",komodo_chainname()) + " than you enter in the amount field.\n" |
a6099ef3 | 453 | "\nResult:\n" |
b5ef85c7 | 454 | "\"transactionid\" (string) The transaction id.\n" |
a6099ef3 | 455 | "\nExamples:\n" |
1333d0d7 | 456 | + HelpExampleCli("sendtoaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" 0.1") |
457 | + HelpExampleCli("sendtoaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" 0.1 \"donation\" \"seans outpost\"") | |
458 | + HelpExampleCli("sendtoaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" 0.1 \"\" \"\" true") | |
459 | + HelpExampleRpc("sendtoaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", 0.1, \"donation\", \"seans outpost\"") | |
a6099ef3 | 460 | ); |
e3bc5698 | 461 | |
2f151c30 | 462 | if ( ASSETCHAINS_PRIVATE != 0 && AmountFromValue(params[1]) > 0 ) |
463 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid " + strprintf("%s",komodo_chainname()) + " address"); | |
464 | ||
4401b2d7 EL |
465 | LOCK2(cs_main, pwalletMain->cs_wallet); |
466 | ||
e3bc5698 JG |
467 | CBitcoinAddress address(params[0].get_str()); |
468 | if (!address.IsValid()) | |
1333d0d7 | 469 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid " + strprintf("%s",komodo_chainname()) + " address"); |
e3bc5698 JG |
470 | |
471 | // Amount | |
a372168e | 472 | CAmount nAmount = AmountFromValue(params[1]); |
e76a3849 WL |
473 | if (nAmount <= 0) |
474 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); | |
e3bc5698 JG |
475 | |
476 | // Wallet comments | |
477 | CWalletTx wtx; | |
ed21d5bd | 478 | if (params.size() > 2 && !params[2].isNull() && !params[2].get_str().empty()) |
e3bc5698 | 479 | wtx.mapValue["comment"] = params[2].get_str(); |
ed21d5bd | 480 | if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) |
e3bc5698 JG |
481 | wtx.mapValue["to"] = params[3].get_str(); |
482 | ||
292623ad CL |
483 | bool fSubtractFeeFromAmount = false; |
484 | if (params.size() > 4) | |
485 | fSubtractFeeFromAmount = params[4].get_bool(); | |
486 | ||
f914c7a1 | 487 | EnsureWalletIsUnlocked(); |
e3bc5698 | 488 | |
b62d7030 | 489 | SendMoney(address.Get(), nAmount, fSubtractFeeFromAmount, wtx,0,0,0); |
e3bc5698 | 490 | |
805344dc | 491 | return wtx.GetHash().GetHex(); |
e3bc5698 | 492 | } |
bdc72415 | 493 | #include "komodo_defs.h" |
e3bc5698 | 494 | |
64449ba8 | 495 | #define KOMODO_KVPROTECTED 1 |
638ac3ba | 496 | #define KOMODO_KVBINARY 2 |
497 | #define KOMODO_KVDURATION 1440 | |
ab6e81ec | 498 | #define IGUANA_MAXSCRIPTSIZE 10001 |
0bda6249 | 499 | uint64_t PAX_fiatdest(uint64_t *seedp,int32_t tokomodo,char *destaddr,uint8_t pubkey37[37],char *coinaddr,int32_t height,char *base,int64_t fiatoshis); |
353edf5d | 500 | int32_t komodo_opreturnscript(uint8_t *script,uint8_t type,uint8_t *opret,int32_t opretlen); |
9415da7f | 501 | #define CRYPTO777_KMDADDR "RXL3YXG2ceaB6C5hfJcN4fvmLH2C34knhA" |
598ee06a | 502 | extern int32_t KOMODO_PAX; |
94d3d09d | 503 | extern uint64_t KOMODO_INTERESTSUM,KOMODO_WALLETBALANCE; |
beb9352f | 504 | int32_t komodo_is_issuer(); |
19cb746d | 505 | int32_t iguana_rwnum(int32_t rwflag,uint8_t *serialized,int32_t len,void *endianedp); |
48a3cd18 | 506 | int32_t komodo_isrealtime(int32_t *kmdheightp); |
aa114a60 | 507 | int32_t pax_fiatstatus(uint64_t *available,uint64_t *deposited,uint64_t *issued,uint64_t *withdrawn,uint64_t *approved,uint64_t *redeemed,char *base); |
6b5cfbb4 | 508 | int32_t komodo_kvsearch(uint256 *refpubkeyp,int32_t current_height,uint32_t *flagsp,int32_t *heightp,uint8_t value[IGUANA_MAXSCRIPTSIZE],uint8_t *key,int32_t keylen); |
51ff33a4 | 509 | int32_t komodo_kvcmp(uint8_t *refvalue,uint16_t refvaluesize,uint8_t *value,uint16_t valuesize); |
6b5cfbb4 | 510 | uint64_t komodo_kvfee(uint32_t flags,int32_t opretlen,int32_t keylen); |
511 | uint256 komodo_kvsig(uint8_t *buf,int32_t len,uint256 privkey); | |
512 | int32_t komodo_kvduration(uint32_t flags); | |
513 | uint256 komodo_kvprivkey(uint256 *pubkeyp,char *passphrase); | |
7112e5a7 | 514 | int32_t komodo_kvsigverify(uint8_t *buf,int32_t len,uint256 _pubkey,uint256 sig); |
d7d27bb3 | 515 | |
14f3daa6 | 516 | UniValue kvupdate(const UniValue& params, bool fHelp) |
ab6e81ec | 517 | { |
684d42a2 | 518 | static uint256 zeroes; |
14f3daa6 | 519 | CWalletTx wtx; UniValue ret(UniValue::VOBJ); |
01801559 | 520 | uint8_t keyvalue[IGUANA_MAXSCRIPTSIZE*8],opretbuf[IGUANA_MAXSCRIPTSIZE*8]; int32_t i,coresize,haveprivkey,duration,opretlen,height; uint16_t keylen=0,valuesize=0,refvaluesize=0; uint8_t *key,*value=0; uint32_t flags,tmpflags,n; struct komodo_kv *ptr; uint64_t fee; uint256 privkey,pubkey,refpubkey,sig; |
2987e650 | 521 | if (fHelp || params.size() < 3 ) |
38e9a59c A |
522 | throw runtime_error( |
523 | "kvupdate key \"value\" days passphrase\n" | |
524 | "\nStore a key value. This feature is only available for asset chains.\n" | |
525 | "\nArguments:\n" | |
526 | "1. key (string, required) key\n" | |
527 | "2. \"value\" (string, required) value\n" | |
528 | "3. days (numeric, required) amount of days(1440 blocks/day) before the key expires. Minimum 1 day\n" | |
529 | "4. passphrase (string, optional) passphrase required to update this key\n" | |
530 | "\nResult:\n" | |
531 | "{\n" | |
532 | " \"coin\": \"xxxxx\", (string) chain the key is stored on\n" | |
533 | " \"height\": xxxxx, (numeric) height the key was stored at\n" | |
6f3db929 A |
534 | " \"expiration\": xxxxx, (numeric) height the key will expire\n" |
535 | " \"flags\": x, (string) amount of days the key will be stored \n" | |
536 | " \"key\": \"xxxxx\", (numeric) stored key\n" | |
38e9a59c | 537 | " \"keylen\": xxxxx, (numeric) length of the key\n" |
6f3db929 A |
538 | " \"value\": \"xxxxx\" (numeric) stored value\n" |
539 | " \"valuesize\": xxxxx, (string) length of the stored value\n" | |
38e9a59c | 540 | " \"fee\": xxxxx (string) transaction fee paid to store the key\n" |
6f3db929 | 541 | " \"txid\": \"xxxxx\" (string) transaction id\n" |
38e9a59c A |
542 | "}\n" |
543 | "\nExamples:\n" | |
544 | + HelpExampleCli("kvupdate", "examplekey \"examplevalue\" 2 examplepassphrase") | |
545 | + HelpExampleRpc("kvupdate", "examplekey \"examplevalue\" 2 examplepassphrase") | |
546 | ); | |
ab6e81ec | 547 | if (!EnsureWalletIsAvailable(fHelp)) |
548 | return 0; | |
3e71f585 | 549 | if ( ASSETCHAINS_SYMBOL[0] == 0 ) |
550 | return(0); | |
ad2c025a | 551 | haveprivkey = 0; |
552 | memset(&sig,0,sizeof(sig)); | |
553 | memset(&privkey,0,sizeof(privkey)); | |
3bd13500 | 554 | memset(&refpubkey,0,sizeof(refpubkey)); |
ad2c025a | 555 | memset(&pubkey,0,sizeof(pubkey)); |
c929bc3e | 556 | if ( (n= (int32_t)params.size()) >= 3 ) |
3b4652ca | 557 | { |
1919e9de | 558 | flags = atoi(params[2].get_str().c_str()); |
ebeae1eb | 559 | //printf("flags.%d (%s) n.%d\n",flags,params[2].get_str().c_str(),n); |
3b4652ca | 560 | } else flags = 0; |
c929bc3e | 561 | if ( n >= 4 ) |
66dc0c54 | 562 | privkey = komodo_kvprivkey(&pubkey,(char *)(n >= 4 ? params[3].get_str().c_str() : "password")); |
563 | haveprivkey = 1; | |
564 | flags |= 1; | |
ebeae1eb | 565 | /*for (i=0; i<32; i++) |
66dc0c54 | 566 | printf("%02x",((uint8_t *)&privkey)[i]); |
567 | printf(" priv, "); | |
568 | for (i=0; i<32; i++) | |
569 | printf("%02x",((uint8_t *)&pubkey)[i]); | |
570 | printf(" pubkey, privkey derived from (%s)\n",(char *)params[3].get_str().c_str()); | |
ebeae1eb | 571 | */ |
ab6e81ec | 572 | LOCK2(cs_main, pwalletMain->cs_wallet); |
573 | if ( (keylen= (int32_t)strlen(params[0].get_str().c_str())) > 0 ) | |
574 | { | |
575 | key = (uint8_t *)params[0].get_str().c_str(); | |
c929bc3e | 576 | if ( n >= 2 && params[1].get_str().c_str() != 0 ) |
51ff33a4 | 577 | { |
578 | value = (uint8_t *)params[1].get_str().c_str(); | |
579 | valuesize = (int32_t)strlen(params[1].get_str().c_str()); | |
580 | } | |
6b5cfbb4 | 581 | memcpy(keyvalue,key,keylen); |
86131275 | 582 | if ( (refvaluesize= komodo_kvsearch(&refpubkey,chainActive.LastTip()->nHeight,&tmpflags,&height,&keyvalue[keylen],key,keylen)) >= 0 ) |
05cbbacf | 583 | { |
6b5cfbb4 | 584 | if ( (tmpflags & KOMODO_KVPROTECTED) != 0 ) |
585 | { | |
ebeae1eb | 586 | if ( memcmp(&refpubkey,&pubkey,sizeof(refpubkey)) != 0 ) |
6b5cfbb4 | 587 | { |
588 | ret.push_back(Pair("error",(char *)"cant modify write once key without passphrase")); | |
589 | return ret; | |
590 | } | |
6b5cfbb4 | 591 | } |
55791792 | 592 | if ( keylen+refvaluesize <= sizeof(keyvalue) ) |
e2843ac1 | 593 | { |
55791792 | 594 | sig = komodo_kvsig(keyvalue,keylen+refvaluesize,privkey); |
e2843ac1 | 595 | if ( komodo_kvsigverify(keyvalue,keylen+refvaluesize,refpubkey,sig) < 0 ) |
ebeae1eb | 596 | { |
597 | ret.push_back(Pair("error",(char *)"error verifying sig, passphrase is probably wrong")); | |
e2843ac1 | 598 | printf("VERIFY ERROR\n"); |
ebeae1eb | 599 | return ret; |
600 | } // else printf("verified immediately\n"); | |
e2843ac1 | 601 | } |
05cbbacf | 602 | } |
ebeae1eb | 603 | //for (i=0; i<32; i++) |
604 | // printf("%02x",((uint8_t *)&sig)[i]); | |
605 | //printf(" sig for keylen.%d + valuesize.%d\n",keylen,refvaluesize); | |
ab6e81ec | 606 | ret.push_back(Pair("coin",(char *)(ASSETCHAINS_SYMBOL[0] == 0 ? "KMD" : ASSETCHAINS_SYMBOL))); |
86131275 | 607 | height = chainActive.LastTip()->nHeight; |
6b5cfbb4 | 608 | if ( memcmp(&zeroes,&refpubkey,sizeof(refpubkey)) != 0 ) |
609 | ret.push_back(Pair("owner",refpubkey.GetHex())); | |
368da363 | 610 | ret.push_back(Pair("height", (int64_t)height)); |
6b5cfbb4 | 611 | duration = komodo_kvduration(flags); //((flags >> 2) + 1) * KOMODO_KVDURATION; |
a6b182bd | 612 | ret.push_back(Pair("expiration", (int64_t)(height+duration))); |
05cbbacf | 613 | ret.push_back(Pair("flags",(int64_t)flags)); |
ab6e81ec | 614 | ret.push_back(Pair("key",params[0].get_str())); |
05cbbacf | 615 | ret.push_back(Pair("keylen",(int64_t)keylen)); |
c929bc3e | 616 | if ( n >= 2 && params[1].get_str().c_str() != 0 ) |
ab6e81ec | 617 | { |
b9ecd81f | 618 | ret.push_back(Pair("value",params[1].get_str())); |
619 | ret.push_back(Pair("valuesize",valuesize)); | |
ab6e81ec | 620 | } |
621 | iguana_rwnum(1,&keyvalue[0],sizeof(keylen),&keylen); | |
622 | iguana_rwnum(1,&keyvalue[2],sizeof(valuesize),&valuesize); | |
368da363 | 623 | iguana_rwnum(1,&keyvalue[4],sizeof(height),&height); |
1919e9de | 624 | iguana_rwnum(1,&keyvalue[8],sizeof(flags),&flags); |
625 | memcpy(&keyvalue[12],key,keylen); | |
ab6e81ec | 626 | if ( value != 0 ) |
1919e9de | 627 | memcpy(&keyvalue[12 + keylen],value,valuesize); |
6b5cfbb4 | 628 | coresize = (int32_t)(sizeof(flags)+sizeof(height)+sizeof(uint16_t)*2+keylen+valuesize); |
629 | if ( haveprivkey != 0 ) | |
630 | { | |
631 | for (i=0; i<32; i++) | |
632 | keyvalue[12 + keylen + valuesize + i] = ((uint8_t *)&pubkey)[i]; | |
633 | coresize += 32; | |
634 | if ( refvaluesize >=0 ) | |
635 | { | |
636 | for (i=0; i<32; i++) | |
637 | keyvalue[12 + keylen + valuesize + 32 + i] = ((uint8_t *)&sig)[i]; | |
638 | coresize += 32; | |
639 | } | |
640 | } | |
641 | if ( (opretlen= komodo_opreturnscript(opretbuf,'K',keyvalue,coresize)) == 40 ) | |
0b748e97 | 642 | opretlen++; |
1919e9de | 643 | //for (i=0; i<opretlen; i++) |
644 | // printf("%02x",opretbuf[i]); | |
645 | //printf(" opretbuf keylen.%d valuesize.%d height.%d (%02x %02x %02x)\n",*(uint16_t *)&keyvalue[0],*(uint16_t *)&keyvalue[2],*(uint32_t *)&keyvalue[4],keyvalue[8],keyvalue[9],keyvalue[10]); | |
ab6e81ec | 646 | EnsureWalletIsUnlocked(); |
6b5cfbb4 | 647 | fee = komodo_kvfee(flags,opretlen,keylen); |
a6b182bd | 648 | ret.push_back(Pair("fee",(double)fee/COIN)); |
ab6e81ec | 649 | CBitcoinAddress destaddress(CRYPTO777_KMDADDR); |
650 | if (!destaddress.IsValid()) | |
651 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid dest Bitcoin address"); | |
6685bab2 | 652 | SendMoney(destaddress.Get(),10000,false,wtx,opretbuf,opretlen,fee); |
a42fa6cf | 653 | ret.push_back(Pair("txid",wtx.GetHash().GetHex())); |
1919e9de | 654 | } else ret.push_back(Pair("error",(char *)"null key")); |
ab6e81ec | 655 | return ret; |
656 | } | |
657 | ||
14f3daa6 | 658 | UniValue paxdeposit(const UniValue& params, bool fHelp) |
c929bc3e | 659 | { |
660 | uint64_t available,deposited,issued,withdrawn,approved,redeemed,seed,komodoshis = 0; int32_t height; char destaddr[64]; uint8_t i,pubkey37[33]; | |
661 | bool fSubtractFeeFromAmount = false; | |
43430608 | 662 | if ( KOMODO_PAX == 0 ) |
e9d73912 | 663 | { |
b92d9db1 | 664 | throw runtime_error("paxdeposit disabled without -pax"); |
e9d73912 | 665 | } |
c929bc3e | 666 | if ( komodo_is_issuer() != 0 ) |
667 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "paxdeposit only from KMD"); | |
668 | if (!EnsureWalletIsAvailable(fHelp)) | |
4e5be259 | 669 | throw runtime_error("paxdeposit needs wallet"); //return Value::null; |
c929bc3e | 670 | if (fHelp || params.size() != 3) |
671 | throw runtime_error("paxdeposit address fiatoshis base"); | |
672 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
673 | CBitcoinAddress address(params[0].get_str()); | |
674 | if (!address.IsValid()) | |
675 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); | |
82fd1939 | 676 | int64_t fiatoshis = atof(params[1].get_str().c_str()) * COIN; |
c929bc3e | 677 | std::string base = params[2].get_str(); |
678 | std::string dest; | |
86131275 | 679 | height = chainActive.LastTip()->nHeight; |
82fd1939 | 680 | if ( pax_fiatstatus(&available,&deposited,&issued,&withdrawn,&approved,&redeemed,(char *)base.c_str()) != 0 || available < fiatoshis ) |
ea73ef00 | 681 | { |
f79f75be | 682 | fprintf(stderr,"available %llu vs fiatoshis %llu\n",(long long)available,(long long)fiatoshis); |
82fd1939 | 683 | throw runtime_error("paxdeposit not enough available inventory"); |
ea73ef00 | 684 | } |
c929bc3e | 685 | komodoshis = PAX_fiatdest(&seed,0,destaddr,pubkey37,(char *)params[0].get_str().c_str(),height,(char *)base.c_str(),fiatoshis); |
686 | dest.append(destaddr); | |
687 | CBitcoinAddress destaddress(CRYPTO777_KMDADDR); | |
688 | if (!destaddress.IsValid()) | |
689 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid dest Bitcoin address"); | |
690 | for (i=0; i<33; i++) | |
691 | fprintf(stderr,"%02x",pubkey37[i]); | |
692 | fprintf(stderr," ht.%d srcaddr.(%s) %s fiatoshis.%lld -> dest.(%s) komodoshis.%llu seed.%llx\n",height,(char *)params[0].get_str().c_str(),(char *)base.c_str(),(long long)fiatoshis,destaddr,(long long)komodoshis,(long long)seed); | |
693 | EnsureWalletIsUnlocked(); | |
694 | CWalletTx wtx; | |
695 | uint8_t opretbuf[64]; int32_t opretlen; uint64_t fee = komodoshis / 1000; | |
696 | if ( fee < 10000 ) | |
697 | fee = 10000; | |
698 | iguana_rwnum(1,&pubkey37[33],sizeof(height),&height); | |
699 | opretlen = komodo_opreturnscript(opretbuf,'D',pubkey37,37); | |
700 | SendMoney(address.Get(),fee,fSubtractFeeFromAmount,wtx,opretbuf,opretlen,komodoshis); | |
701 | return wtx.GetHash().GetHex(); | |
702 | } | |
703 | ||
14f3daa6 | 704 | UniValue paxwithdraw(const UniValue& params, bool fHelp) |
c929bc3e | 705 | { |
706 | CWalletTx wtx; std::string dest; int32_t kmdheight; uint64_t seed,komodoshis = 0; char destaddr[64]; uint8_t i,pubkey37[37]; bool fSubtractFeeFromAmount = false; | |
707 | if ( ASSETCHAINS_SYMBOL[0] == 0 ) | |
708 | return(0); | |
709 | if (!EnsureWalletIsAvailable(fHelp)) | |
710 | return 0; | |
16c7bf6b | 711 | throw runtime_error("paxwithdraw deprecated"); |
c929bc3e | 712 | if (fHelp || params.size() != 2) |
713 | throw runtime_error("paxwithdraw address fiatamount"); | |
714 | if ( komodo_isrealtime(&kmdheight) == 0 ) | |
715 | return(0); | |
716 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
717 | CBitcoinAddress address(params[0].get_str()); | |
718 | if (!address.IsValid()) | |
719 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address"); | |
720 | int64_t fiatoshis = atof(params[1].get_str().c_str()) * COIN; | |
721 | komodoshis = PAX_fiatdest(&seed,1,destaddr,pubkey37,(char *)params[0].get_str().c_str(),kmdheight,ASSETCHAINS_SYMBOL,fiatoshis); | |
722 | dest.append(destaddr); | |
723 | CBitcoinAddress destaddress(CRYPTO777_KMDADDR); | |
724 | if (!destaddress.IsValid()) | |
725 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid dest Bitcoin address"); | |
726 | for (i=0; i<33; i++) | |
727 | printf("%02x",pubkey37[i]); | |
728 | printf(" kmdheight.%d srcaddr.(%s) %s fiatoshis.%lld -> dest.(%s) komodoshis.%llu seed.%llx\n",kmdheight,(char *)params[0].get_str().c_str(),ASSETCHAINS_SYMBOL,(long long)fiatoshis,destaddr,(long long)komodoshis,(long long)seed); | |
729 | EnsureWalletIsUnlocked(); | |
730 | uint8_t opretbuf[64]; int32_t opretlen; uint64_t fee = fiatoshis / 1000; | |
731 | if ( fee < 10000 ) | |
732 | fee = 10000; | |
733 | iguana_rwnum(1,&pubkey37[33],sizeof(kmdheight),&kmdheight); | |
734 | opretlen = komodo_opreturnscript(opretbuf,'W',pubkey37,37); | |
735 | SendMoney(destaddress.Get(),fee,fSubtractFeeFromAmount,wtx,opretbuf,opretlen,fiatoshis); | |
736 | return wtx.GetHash().GetHex(); | |
737 | } | |
738 | ||
d014114d | 739 | UniValue listaddressgroupings(const UniValue& params, bool fHelp) |
22dfd735 | 740 | { |
b9fb692d | 741 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 742 | return NullUniValue; |
9d365796 | 743 | |
22dfd735 | 744 | if (fHelp) |
b1093efa GM |
745 | throw runtime_error( |
746 | "listaddressgroupings\n" | |
a6099ef3 | 747 | "\nLists groups of addresses which have had their common ownership\n" |
b1093efa | 748 | "made public by common use as inputs or as the resulting change\n" |
a6099ef3 | 749 | "in past transactions\n" |
750 | "\nResult:\n" | |
751 | "[\n" | |
752 | " [\n" | |
753 | " [\n" | |
1333d0d7 | 754 | " \"" + strprintf("%s",komodo_chainname()) + " address\", (string) The " + strprintf("%s",komodo_chainname()) + " address\n" |
755 | " amount, (numeric) The amount in " + strprintf("%s",komodo_chainname()) + "\n" | |
7b782f5b | 756 | " \"account\" (string, optional) The account (DEPRECATED)\n" |
a6099ef3 | 757 | " ]\n" |
758 | " ,...\n" | |
759 | " ]\n" | |
760 | " ,...\n" | |
761 | "]\n" | |
762 | "\nExamples:\n" | |
763 | + HelpExampleCli("listaddressgroupings", "") | |
764 | + HelpExampleRpc("listaddressgroupings", "") | |
765 | ); | |
22dfd735 | 766 | |
4401b2d7 EL |
767 | LOCK2(cs_main, pwalletMain->cs_wallet); |
768 | ||
38fc4b70 | 769 | UniValue jsonGroupings(UniValue::VARR); |
a372168e | 770 | map<CTxDestination, CAmount> balances = pwalletMain->GetAddressBalances(); |
b1093efa | 771 | BOOST_FOREACH(set<CTxDestination> grouping, pwalletMain->GetAddressGroupings()) |
22dfd735 | 772 | { |
38fc4b70 | 773 | UniValue jsonGrouping(UniValue::VARR); |
b1093efa | 774 | BOOST_FOREACH(CTxDestination address, grouping) |
22dfd735 | 775 | { |
38fc4b70 | 776 | UniValue addressInfo(UniValue::VARR); |
b1093efa | 777 | addressInfo.push_back(CBitcoinAddress(address).ToString()); |
22dfd735 | 778 | addressInfo.push_back(ValueFromAmount(balances[address])); |
779 | { | |
22dfd735 | 780 | if (pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get()) != pwalletMain->mapAddressBook.end()) |
61885513 | 781 | addressInfo.push_back(pwalletMain->mapAddressBook.find(CBitcoinAddress(address).Get())->second.name); |
22dfd735 | 782 | } |
783 | jsonGrouping.push_back(addressInfo); | |
784 | } | |
785 | jsonGroupings.push_back(jsonGrouping); | |
786 | } | |
787 | return jsonGroupings; | |
788 | } | |
789 | ||
d014114d | 790 | UniValue signmessage(const UniValue& params, bool fHelp) |
e3bc5698 | 791 | { |
b9fb692d | 792 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 793 | return NullUniValue; |
9d365796 | 794 | |
e3bc5698 JG |
795 | if (fHelp || params.size() != 2) |
796 | throw runtime_error( | |
1333d0d7 | 797 | "signmessage \"" + strprintf("%s",komodo_chainname()) + " address\" \"message\"\n" |
a6099ef3 | 798 | "\nSign a message with the private key of an address" |
799 | + HelpRequiringPassphrase() + "\n" | |
800 | "\nArguments:\n" | |
1333d0d7 | 801 | "1. \"" + strprintf("%s",komodo_chainname()) + " address\" (string, required) The " + strprintf("%s",komodo_chainname()) + " address to use for the private key.\n" |
a6099ef3 | 802 | "2. \"message\" (string, required) The message to create a signature of.\n" |
803 | "\nResult:\n" | |
804 | "\"signature\" (string) The signature of the message encoded in base 64\n" | |
805 | "\nExamples:\n" | |
806 | "\nUnlock the wallet for 30 seconds\n" | |
807 | + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + | |
808 | "\nCreate the signature\n" | |
1333d0d7 | 809 | + HelpExampleCli("signmessage", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" \"my message\"") + |
a6099ef3 | 810 | "\nVerify the signature\n" |
1333d0d7 | 811 | + HelpExampleCli("verifymessage", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" \"signature\" \"my message\"") + |
a6099ef3 | 812 | "\nAs json rpc\n" |
1333d0d7 | 813 | + HelpExampleRpc("signmessage", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", \"my message\"") |
a6099ef3 | 814 | ); |
e3bc5698 | 815 | |
4401b2d7 EL |
816 | LOCK2(cs_main, pwalletMain->cs_wallet); |
817 | ||
e3bc5698 JG |
818 | EnsureWalletIsUnlocked(); |
819 | ||
820 | string strAddress = params[0].get_str(); | |
821 | string strMessage = params[1].get_str(); | |
822 | ||
823 | CBitcoinAddress addr(strAddress); | |
824 | if (!addr.IsValid()) | |
738835d7 | 825 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); |
e3bc5698 JG |
826 | |
827 | CKeyID keyID; | |
828 | if (!addr.GetKeyID(keyID)) | |
738835d7 | 829 | throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); |
e3bc5698 JG |
830 | |
831 | CKey key; | |
832 | if (!pwalletMain->GetKey(keyID, key)) | |
738835d7 | 833 | throw JSONRPCError(RPC_WALLET_ERROR, "Private key not available"); |
e3bc5698 | 834 | |
8980a509 | 835 | CHashWriter ss(SER_GETHASH, 0); |
e3bc5698 JG |
836 | ss << strMessageMagic; |
837 | ss << strMessage; | |
838 | ||
839 | vector<unsigned char> vchSig; | |
8980a509 | 840 | if (!key.SignCompact(ss.GetHash(), vchSig)) |
738835d7 | 841 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Sign failed"); |
e3bc5698 JG |
842 | |
843 | return EncodeBase64(&vchSig[0], vchSig.size()); | |
844 | } | |
845 | ||
d014114d | 846 | UniValue getreceivedbyaddress(const UniValue& params, bool fHelp) |
e3bc5698 | 847 | { |
b9fb692d | 848 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 849 | return NullUniValue; |
9d365796 | 850 | |
e3bc5698 JG |
851 | if (fHelp || params.size() < 1 || params.size() > 2) |
852 | throw runtime_error( | |
1333d0d7 | 853 | "getreceivedbyaddress \"" + strprintf("%s",komodo_chainname()) + "_address\" ( minconf )\n" |
854 | "\nReturns the total amount received by the given " + strprintf("%s",komodo_chainname()) + " address in transactions with at least minconf confirmations.\n" | |
a6099ef3 | 855 | "\nArguments:\n" |
1333d0d7 | 856 | "1. \"" + strprintf("%s",komodo_chainname()) + "_address\" (string, required) The " + strprintf("%s",komodo_chainname()) + " address for transactions.\n" |
a6099ef3 | 857 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" |
858 | "\nResult:\n" | |
1333d0d7 | 859 | "amount (numeric) The total amount in " + strprintf("%s",komodo_chainname()) + " received at this address.\n" |
a6099ef3 | 860 | "\nExamples:\n" |
861 | "\nThe amount from transactions with at least 1 confirmation\n" | |
1333d0d7 | 862 | + HelpExampleCli("getreceivedbyaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"") + |
a6099ef3 | 863 | "\nThe amount including unconfirmed transactions, zero confirmations\n" |
1333d0d7 | 864 | + HelpExampleCli("getreceivedbyaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" 0") + |
c938fb1f | 865 | "\nThe amount with at least 6 confirmations, very safe\n" |
1333d0d7 | 866 | + HelpExampleCli("getreceivedbyaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" 6") + |
a6099ef3 | 867 | "\nAs a json rpc call\n" |
1333d0d7 | 868 | + HelpExampleRpc("getreceivedbyaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", 6") |
a6099ef3 | 869 | ); |
e3bc5698 | 870 | |
4401b2d7 EL |
871 | LOCK2(cs_main, pwalletMain->cs_wallet); |
872 | ||
e3bc5698 JG |
873 | // Bitcoin address |
874 | CBitcoinAddress address = CBitcoinAddress(params[0].get_str()); | |
e3bc5698 | 875 | if (!address.IsValid()) |
1333d0d7 | 876 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid " + strprintf("%s",komodo_chainname()) + " address"); |
0be990ba | 877 | CScript scriptPubKey = GetScriptForDestination(address.Get()); |
e3bc5698 JG |
878 | if (!IsMine(*pwalletMain,scriptPubKey)) |
879 | return (double)0.0; | |
880 | ||
881 | // Minimum confirmations | |
882 | int nMinDepth = 1; | |
883 | if (params.size() > 1) | |
884 | nMinDepth = params[1].get_int(); | |
885 | ||
886 | // Tally | |
a372168e | 887 | CAmount nAmount = 0; |
e3bc5698 JG |
888 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) |
889 | { | |
890 | const CWalletTx& wtx = (*it).second; | |
75a4d512 | 891 | if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) |
e3bc5698 JG |
892 | continue; |
893 | ||
894 | BOOST_FOREACH(const CTxOut& txout, wtx.vout) | |
895 | if (txout.scriptPubKey == scriptPubKey) | |
896 | if (wtx.GetDepthInMainChain() >= nMinDepth) | |
47658758 | 897 | nAmount += txout.nValue; // komodo_interest? |
e3bc5698 JG |
898 | } |
899 | ||
900 | return ValueFromAmount(nAmount); | |
901 | } | |
902 | ||
903 | ||
d014114d | 904 | UniValue getreceivedbyaccount(const UniValue& params, bool fHelp) |
e3bc5698 | 905 | { |
b9fb692d | 906 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 907 | return NullUniValue; |
9d365796 | 908 | |
e3bc5698 JG |
909 | if (fHelp || params.size() < 1 || params.size() > 2) |
910 | throw runtime_error( | |
a6099ef3 | 911 | "getreceivedbyaccount \"account\" ( minconf )\n" |
7b782f5b | 912 | "\nDEPRECATED. Returns the total amount received by addresses with <account> in transactions with at least [minconf] confirmations.\n" |
a6099ef3 | 913 | "\nArguments:\n" |
3c31eb24 | 914 | "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 | 915 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" |
916 | "\nResult:\n" | |
1333d0d7 | 917 | "amount (numeric) The total amount in " + strprintf("%s",komodo_chainname()) + " received for this account.\n" |
a6099ef3 | 918 | "\nExamples:\n" |
919 | "\nAmount received by the default account with at least 1 confirmation\n" | |
920 | + HelpExampleCli("getreceivedbyaccount", "\"\"") + | |
921 | "\nAmount received at the tabby account including unconfirmed amounts with zero confirmations\n" | |
922 | + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 0") + | |
923 | "\nThe amount with at least 6 confirmation, very safe\n" | |
924 | + HelpExampleCli("getreceivedbyaccount", "\"tabby\" 6") + | |
925 | "\nAs a json rpc call\n" | |
926 | + HelpExampleRpc("getreceivedbyaccount", "\"tabby\", 6") | |
927 | ); | |
e3bc5698 | 928 | |
4401b2d7 EL |
929 | LOCK2(cs_main, pwalletMain->cs_wallet); |
930 | ||
e3bc5698 JG |
931 | // Minimum confirmations |
932 | int nMinDepth = 1; | |
933 | if (params.size() > 1) | |
934 | nMinDepth = params[1].get_int(); | |
935 | ||
936 | // Get the set of pub keys assigned to account | |
937 | string strAccount = AccountFromValue(params[0]); | |
3624356e | 938 | set<CTxDestination> setAddress = pwalletMain->GetAccountAddresses(strAccount); |
e3bc5698 JG |
939 | |
940 | // Tally | |
a372168e | 941 | CAmount nAmount = 0; |
e3bc5698 JG |
942 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) |
943 | { | |
944 | const CWalletTx& wtx = (*it).second; | |
75a4d512 | 945 | if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) |
e3bc5698 JG |
946 | continue; |
947 | ||
948 | BOOST_FOREACH(const CTxOut& txout, wtx.vout) | |
949 | { | |
950 | CTxDestination address; | |
951 | if (ExtractDestination(txout.scriptPubKey, address) && IsMine(*pwalletMain, address) && setAddress.count(address)) | |
952 | if (wtx.GetDepthInMainChain() >= nMinDepth) | |
47658758 | 953 | nAmount += txout.nValue; // komodo_interest? |
e3bc5698 JG |
954 | } |
955 | } | |
956 | ||
957 | return (double)nAmount / (double)COIN; | |
958 | } | |
959 | ||
960 | ||
a372168e | 961 | CAmount GetAccountBalance(CWalletDB& walletdb, const string& strAccount, int nMinDepth, const isminefilter& filter) |
e3bc5698 | 962 | { |
a372168e | 963 | CAmount nBalance = 0; |
e3bc5698 JG |
964 | |
965 | // Tally wallet transactions | |
966 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) | |
967 | { | |
968 | const CWalletTx& wtx = (*it).second; | |
75a4d512 | 969 | if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) |
e3bc5698 JG |
970 | continue; |
971 | ||
a372168e | 972 | CAmount nReceived, nSent, nFee; |
d4640d7d | 973 | wtx.GetAccountAmounts(strAccount, nReceived, nSent, nFee, filter); |
e3bc5698 JG |
974 | |
975 | if (nReceived != 0 && wtx.GetDepthInMainChain() >= nMinDepth) | |
976 | nBalance += nReceived; | |
e07c8e91 | 977 | nBalance -= nSent + nFee; |
e3bc5698 JG |
978 | } |
979 | ||
980 | // Tally internal accounting entries | |
981 | nBalance += walletdb.GetAccountCreditDebit(strAccount); | |
982 | ||
983 | return nBalance; | |
984 | } | |
985 | ||
a372168e | 986 | CAmount GetAccountBalance(const string& strAccount, int nMinDepth, const isminefilter& filter) |
e3bc5698 JG |
987 | { |
988 | CWalletDB walletdb(pwalletMain->strWalletFile); | |
d4640d7d | 989 | return GetAccountBalance(walletdb, strAccount, nMinDepth, filter); |
e3bc5698 JG |
990 | } |
991 | ||
992 | ||
d014114d | 993 | UniValue getbalance(const UniValue& params, bool fHelp) |
e3bc5698 | 994 | { |
b9fb692d | 995 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 996 | return NullUniValue; |
9d365796 | 997 | |
d4640d7d | 998 | if (fHelp || params.size() > 3) |
e3bc5698 | 999 | throw runtime_error( |
d4640d7d | 1000 | "getbalance ( \"account\" minconf includeWatchonly )\n" |
3c31eb24 | 1001 | "\nReturns the server's total available balance.\n" |
a6099ef3 | 1002 | "\nArguments:\n" |
715e5bbe | 1003 | "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 | 1004 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" |
d4640d7d | 1005 | "3. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress')\n" |
a6099ef3 | 1006 | "\nResult:\n" |
1333d0d7 | 1007 | "amount (numeric) The total amount in " + strprintf("%s",komodo_chainname()) + " received for this account.\n" |
a6099ef3 | 1008 | "\nExamples:\n" |
7b782f5b | 1009 | "\nThe total amount in the wallet\n" |
a6099ef3 | 1010 | + HelpExampleCli("getbalance", "") + |
7b782f5b | 1011 | "\nThe total amount in the wallet at least 5 blocks confirmed\n" |
3cf1f436 | 1012 | + HelpExampleCli("getbalance", "\"*\" 6") + |
a6099ef3 | 1013 | "\nAs a json rpc call\n" |
7b782f5b | 1014 | + HelpExampleRpc("getbalance", "\"*\", 6") |
a6099ef3 | 1015 | ); |
e3bc5698 | 1016 | |
4401b2d7 EL |
1017 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1018 | ||
e3bc5698 JG |
1019 | if (params.size() == 0) |
1020 | return ValueFromAmount(pwalletMain->GetBalance()); | |
1021 | ||
1022 | int nMinDepth = 1; | |
1023 | if (params.size() > 1) | |
1024 | nMinDepth = params[1].get_int(); | |
a3e192a3 | 1025 | isminefilter filter = ISMINE_SPENDABLE; |
a5c6c5d6 J |
1026 | if(params.size() > 2) |
1027 | if(params[2].get_bool()) | |
a3e192a3 | 1028 | filter = filter | ISMINE_WATCH_ONLY; |
e3bc5698 JG |
1029 | |
1030 | if (params[0].get_str() == "*") { | |
1031 | // Calculate total balance a different way from GetBalance() | |
1032 | // (GetBalance() sums up all unspent TxOuts) | |
c9fd9078 | 1033 | // getbalance and "getbalance * 1 true" should return the same number |
a372168e | 1034 | CAmount nBalance = 0; |
e3bc5698 JG |
1035 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) |
1036 | { | |
1037 | const CWalletTx& wtx = (*it).second; | |
c9fd9078 | 1038 | if (!CheckFinalTx(wtx) || wtx.GetBlocksToMaturity() > 0 || wtx.GetDepthInMainChain() < 0) |
e3bc5698 JG |
1039 | continue; |
1040 | ||
a372168e | 1041 | CAmount allFee; |
e3bc5698 | 1042 | string strSentAccount; |
1b4568cb CL |
1043 | list<COutputEntry> listReceived; |
1044 | list<COutputEntry> listSent; | |
d4640d7d | 1045 | wtx.GetAmounts(listReceived, listSent, allFee, strSentAccount, filter); |
e3bc5698 JG |
1046 | if (wtx.GetDepthInMainChain() >= nMinDepth) |
1047 | { | |
1b4568cb CL |
1048 | BOOST_FOREACH(const COutputEntry& r, listReceived) |
1049 | nBalance += r.amount; | |
e3bc5698 | 1050 | } |
1b4568cb CL |
1051 | BOOST_FOREACH(const COutputEntry& s, listSent) |
1052 | nBalance -= s.amount; | |
e3bc5698 | 1053 | nBalance -= allFee; |
e3bc5698 JG |
1054 | } |
1055 | return ValueFromAmount(nBalance); | |
1056 | } | |
1057 | ||
1058 | string strAccount = AccountFromValue(params[0]); | |
1059 | ||
a372168e | 1060 | CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, filter); |
e3bc5698 JG |
1061 | |
1062 | return ValueFromAmount(nBalance); | |
1063 | } | |
1064 | ||
d014114d | 1065 | UniValue getunconfirmedbalance(const UniValue ¶ms, bool fHelp) |
6027b460 | 1066 | { |
b9fb692d | 1067 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1068 | return NullUniValue; |
9d365796 | 1069 | |
6027b460 MB |
1070 | if (fHelp || params.size() > 0) |
1071 | throw runtime_error( | |
1072 | "getunconfirmedbalance\n" | |
1073 | "Returns the server's total unconfirmed balance\n"); | |
4401b2d7 EL |
1074 | |
1075 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
1076 | ||
6027b460 MB |
1077 | return ValueFromAmount(pwalletMain->GetUnconfirmedBalance()); |
1078 | } | |
1079 | ||
e3bc5698 | 1080 | |
d014114d | 1081 | UniValue movecmd(const UniValue& params, bool fHelp) |
e3bc5698 | 1082 | { |
b9fb692d | 1083 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1084 | return NullUniValue; |
9d365796 | 1085 | |
e3bc5698 JG |
1086 | if (fHelp || params.size() < 3 || params.size() > 5) |
1087 | throw runtime_error( | |
a6099ef3 | 1088 | "move \"fromaccount\" \"toaccount\" amount ( minconf \"comment\" )\n" |
7b782f5b | 1089 | "\nDEPRECATED. Move a specified amount from one account in your wallet to another.\n" |
a6099ef3 | 1090 | "\nArguments:\n" |
3c31eb24 JG |
1091 | "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" |
1092 | "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" | |
1333d0d7 | 1093 | "3. amount (numeric) Quantity of " + strprintf("%s",komodo_chainname()) + " to move between accounts.\n" |
091b2116 RN |
1094 | "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" |
1095 | "5. \"comment\" (string, optional) An optional comment, stored in the wallet only.\n" | |
a6099ef3 | 1096 | "\nResult:\n" |
45bfa137 | 1097 | "true|false (boolean) true if successful.\n" |
a6099ef3 | 1098 | "\nExamples:\n" |
1333d0d7 | 1099 | "\nMove 0.01 " + strprintf("%s",komodo_chainname()) + " from the default account to the account named tabby\n" |
a6099ef3 | 1100 | + HelpExampleCli("move", "\"\" \"tabby\" 0.01") + |
1333d0d7 | 1101 | "\nMove 0.01 " + strprintf("%s",komodo_chainname()) + " timotei to akiko with a comment and funds have 6 confirmations\n" |
a6099ef3 | 1102 | + HelpExampleCli("move", "\"timotei\" \"akiko\" 0.01 6 \"happy birthday!\"") + |
1103 | "\nAs a json rpc call\n" | |
1104 | + HelpExampleRpc("move", "\"timotei\", \"akiko\", 0.01, 6, \"happy birthday!\"") | |
1105 | ); | |
b310ffff | 1106 | if ( ASSETCHAINS_PRIVATE != 0 ) |
1107 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "cant use transparent addresses in private chain"); | |
e3bc5698 | 1108 | |
4401b2d7 EL |
1109 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1110 | ||
e3bc5698 JG |
1111 | string strFrom = AccountFromValue(params[0]); |
1112 | string strTo = AccountFromValue(params[1]); | |
a372168e | 1113 | CAmount nAmount = AmountFromValue(params[2]); |
e76a3849 WL |
1114 | if (nAmount <= 0) |
1115 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); | |
e3bc5698 JG |
1116 | if (params.size() > 3) |
1117 | // unused parameter, used to be nMinDepth, keep type-checking it though | |
1118 | (void)params[3].get_int(); | |
1119 | string strComment; | |
1120 | if (params.size() > 4) | |
1121 | strComment = params[4].get_str(); | |
1122 | ||
1123 | CWalletDB walletdb(pwalletMain->strWalletFile); | |
1124 | if (!walletdb.TxnBegin()) | |
738835d7 | 1125 | throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); |
e3bc5698 | 1126 | |
51ed9ec9 | 1127 | int64_t nNow = GetAdjustedTime(); |
e3bc5698 JG |
1128 | |
1129 | // Debit | |
1130 | CAccountingEntry debit; | |
4291e8fe | 1131 | debit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); |
e3bc5698 JG |
1132 | debit.strAccount = strFrom; |
1133 | debit.nCreditDebit = -nAmount; | |
1134 | debit.nTime = nNow; | |
1135 | debit.strOtherAccount = strTo; | |
1136 | debit.strComment = strComment; | |
1137 | walletdb.WriteAccountingEntry(debit); | |
1138 | ||
1139 | // Credit | |
1140 | CAccountingEntry credit; | |
4291e8fe | 1141 | credit.nOrderPos = pwalletMain->IncOrderPosNext(&walletdb); |
e3bc5698 JG |
1142 | credit.strAccount = strTo; |
1143 | credit.nCreditDebit = nAmount; | |
1144 | credit.nTime = nNow; | |
1145 | credit.strOtherAccount = strFrom; | |
1146 | credit.strComment = strComment; | |
1147 | walletdb.WriteAccountingEntry(credit); | |
1148 | ||
1149 | if (!walletdb.TxnCommit()) | |
738835d7 | 1150 | throw JSONRPCError(RPC_DATABASE_ERROR, "database error"); |
e3bc5698 JG |
1151 | |
1152 | return true; | |
1153 | } | |
1154 | ||
1155 | ||
d014114d | 1156 | UniValue sendfrom(const UniValue& params, bool fHelp) |
e3bc5698 | 1157 | { |
b9fb692d | 1158 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1159 | return NullUniValue; |
9d365796 | 1160 | |
e3bc5698 JG |
1161 | if (fHelp || params.size() < 3 || params.size() > 6) |
1162 | throw runtime_error( | |
1333d0d7 | 1163 | "sendfrom \"fromaccount\" \"to" + strprintf("%s",komodo_chainname()) + "address\" amount ( minconf \"comment\" \"comment-to\" )\n" |
1164 | "\nDEPRECATED (use sendtoaddress). Sent an amount from an account to a " + strprintf("%s",komodo_chainname()) + " address.\n" | |
a6099ef3 | 1165 | "The amount is a real and is rounded to the nearest 0.00000001." |
1166 | + HelpRequiringPassphrase() + "\n" | |
1167 | "\nArguments:\n" | |
3c31eb24 | 1168 | "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" |
1333d0d7 | 1169 | "2. \"to" + strprintf("%s",komodo_chainname()) + "address\" (string, required) The " + strprintf("%s",komodo_chainname()) + " address to send funds to.\n" |
1170 | "3. amount (numeric, required) The amount in " + strprintf("%s",komodo_chainname()) + " (transaction fee is added on top).\n" | |
a6099ef3 | 1171 | "4. minconf (numeric, optional, default=1) Only use funds with at least this many confirmations.\n" |
1172 | "5. \"comment\" (string, optional) A comment used to store what the transaction is for. \n" | |
1173 | " This is not part of the transaction, just kept in your wallet.\n" | |
1174 | "6. \"comment-to\" (string, optional) An optional comment to store the name of the person or organization \n" | |
1175 | " to which you're sending the transaction. This is not part of the transaction, \n" | |
1176 | " it is just kept in your wallet.\n" | |
1177 | "\nResult:\n" | |
b5ef85c7 | 1178 | "\"transactionid\" (string) The transaction id.\n" |
a6099ef3 | 1179 | "\nExamples:\n" |
1333d0d7 | 1180 | "\nSend 0.01 " + strprintf("%s",komodo_chainname()) + " from the default account to the address, must have at least 1 confirmation\n" |
1181 | + HelpExampleCli("sendfrom", "\"\" \"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" 0.01") + | |
a6099ef3 | 1182 | "\nSend 0.01 from the tabby account to the given address, funds must have at least 6 confirmations\n" |
1333d0d7 | 1183 | + HelpExampleCli("sendfrom", "\"tabby\" \"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" 0.01 6 \"donation\" \"seans outpost\"") + |
a6099ef3 | 1184 | "\nAs a json rpc call\n" |
1333d0d7 | 1185 | + HelpExampleRpc("sendfrom", "\"tabby\", \"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", 0.01, 6, \"donation\", \"seans outpost\"") |
a6099ef3 | 1186 | ); |
b310ffff | 1187 | if ( ASSETCHAINS_PRIVATE != 0 ) |
1188 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "cant use transparent addresses in private chain"); | |
e3bc5698 | 1189 | |
4401b2d7 EL |
1190 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1191 | ||
e3bc5698 JG |
1192 | string strAccount = AccountFromValue(params[0]); |
1193 | CBitcoinAddress address(params[1].get_str()); | |
1194 | if (!address.IsValid()) | |
1333d0d7 | 1195 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid " + strprintf("%s",komodo_chainname()) + " address"); |
a372168e | 1196 | CAmount nAmount = AmountFromValue(params[2]); |
e76a3849 WL |
1197 | if (nAmount <= 0) |
1198 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); | |
e3bc5698 JG |
1199 | int nMinDepth = 1; |
1200 | if (params.size() > 3) | |
1201 | nMinDepth = params[3].get_int(); | |
1202 | ||
1203 | CWalletTx wtx; | |
1204 | wtx.strFromAccount = strAccount; | |
ed21d5bd | 1205 | if (params.size() > 4 && !params[4].isNull() && !params[4].get_str().empty()) |
e3bc5698 | 1206 | wtx.mapValue["comment"] = params[4].get_str(); |
ed21d5bd | 1207 | if (params.size() > 5 && !params[5].isNull() && !params[5].get_str().empty()) |
e3bc5698 JG |
1208 | wtx.mapValue["to"] = params[5].get_str(); |
1209 | ||
1210 | EnsureWalletIsUnlocked(); | |
1211 | ||
1212 | // Check funds | |
a372168e | 1213 | CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); |
e3bc5698 | 1214 | if (nAmount > nBalance) |
738835d7 | 1215 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); |
e3bc5698 | 1216 | |
b62d7030 | 1217 | SendMoney(address.Get(), nAmount, false, wtx,0,0,0); |
e3bc5698 | 1218 | |
805344dc | 1219 | return wtx.GetHash().GetHex(); |
e3bc5698 JG |
1220 | } |
1221 | ||
1222 | ||
d014114d | 1223 | UniValue sendmany(const UniValue& params, bool fHelp) |
e3bc5698 | 1224 | { |
b9fb692d | 1225 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1226 | return NullUniValue; |
9d365796 | 1227 | |
292623ad | 1228 | if (fHelp || params.size() < 2 || params.size() > 5) |
e3bc5698 | 1229 | throw runtime_error( |
40a75733 | 1230 | "sendmany \"fromaccount\" {\"address\":amount,...} ( minconf \"comment\" [\"address\",...] )\n" |
a6099ef3 | 1231 | "\nSend multiple times. Amounts are double-precision floating point numbers." |
1232 | + HelpRequiringPassphrase() + "\n" | |
1233 | "\nArguments:\n" | |
3c31eb24 | 1234 | "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 | 1235 | "2. \"amounts\" (string, required) A json object with addresses and amounts\n" |
1236 | " {\n" | |
1333d0d7 | 1237 | " \"address\":amount (numeric) The " + strprintf("%s",komodo_chainname()) + " address is the key, the numeric amount in " + strprintf("%s",komodo_chainname()) + " is the value\n" |
a6099ef3 | 1238 | " ,...\n" |
1239 | " }\n" | |
1240 | "3. minconf (numeric, optional, default=1) Only use the balance confirmed at least this many times.\n" | |
1241 | "4. \"comment\" (string, optional) A comment\n" | |
40a75733 | 1242 | "5. subtractfeefromamount (string, optional) A json array with addresses.\n" |
292623ad | 1243 | " The fee will be equally deducted from the amount of each selected address.\n" |
1333d0d7 | 1244 | " Those recipients will receive less " + strprintf("%s",komodo_chainname()) + " than you enter in their corresponding amount field.\n" |
40a75733 LD |
1245 | " If no addresses are specified here, the sender pays the fee.\n" |
1246 | " [\n" | |
1247 | " \"address\" (string) Subtract fee from this address\n" | |
292623ad | 1248 | " ,...\n" |
40a75733 | 1249 | " ]\n" |
a6099ef3 | 1250 | "\nResult:\n" |
1251 | "\"transactionid\" (string) The transaction id for the send. Only 1 transaction is created regardless of \n" | |
b5ef85c7 | 1252 | " the number of addresses.\n" |
a6099ef3 | 1253 | "\nExamples:\n" |
1254 | "\nSend two amounts to two different addresses:\n" | |
1333d0d7 | 1255 | + HelpExampleCli("sendmany", "\"\" \"{\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.01,\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.02}\"") + |
a6099ef3 | 1256 | "\nSend two amounts to two different addresses setting the confirmation and comment:\n" |
1333d0d7 | 1257 | + HelpExampleCli("sendmany", "\"\" \"{\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.01,\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.02}\" 6 \"testing\"") + |
292623ad | 1258 | "\nSend two amounts to two different addresses, subtract fee from amount:\n" |
1333d0d7 | 1259 | + HelpExampleCli("sendmany", "\"\" \"{\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.01,\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.02}\" 1 \"\" \"[\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\",\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\"]\"") + |
a6099ef3 | 1260 | "\nAs a json rpc call\n" |
1333d0d7 | 1261 | + HelpExampleRpc("sendmany", "\"\", \"{\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.01,\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\":0.02}\", 6, \"testing\"") |
a6099ef3 | 1262 | ); |
b310ffff | 1263 | if ( ASSETCHAINS_PRIVATE != 0 ) |
1264 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "cant use transparent addresses in private chain"); | |
e3bc5698 | 1265 | |
4401b2d7 EL |
1266 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1267 | ||
e3bc5698 | 1268 | string strAccount = AccountFromValue(params[0]); |
851f58f9 | 1269 | UniValue sendTo = params[1].get_obj(); |
e3bc5698 JG |
1270 | int nMinDepth = 1; |
1271 | if (params.size() > 2) | |
1272 | nMinDepth = params[2].get_int(); | |
1273 | ||
1274 | CWalletTx wtx; | |
1275 | wtx.strFromAccount = strAccount; | |
ed21d5bd | 1276 | if (params.size() > 3 && !params[3].isNull() && !params[3].get_str().empty()) |
e3bc5698 JG |
1277 | wtx.mapValue["comment"] = params[3].get_str(); |
1278 | ||
38fc4b70 | 1279 | UniValue subtractFeeFromAmount(UniValue::VARR); |
292623ad | 1280 | if (params.size() > 4) |
40a75733 | 1281 | subtractFeeFromAmount = params[4].get_array(); |
292623ad | 1282 | |
e3bc5698 | 1283 | set<CBitcoinAddress> setAddress; |
292623ad | 1284 | vector<CRecipient> vecSend; |
e3bc5698 | 1285 | |
a372168e | 1286 | CAmount totalAmount = 0; |
ed21d5bd JG |
1287 | vector<string> keys = sendTo.getKeys(); |
1288 | BOOST_FOREACH(const string& name_, keys) | |
e3bc5698 | 1289 | { |
ed21d5bd | 1290 | CBitcoinAddress address(name_); |
e3bc5698 | 1291 | if (!address.IsValid()) |
1333d0d7 | 1292 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid " + strprintf("%s",komodo_chainname()) + " address: ")+name_); |
e3bc5698 | 1293 | |
9d83c1ad | 1294 | //if (setAddress.count(address)) |
1295 | // throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); | |
e3bc5698 JG |
1296 | setAddress.insert(address); |
1297 | ||
0be990ba | 1298 | CScript scriptPubKey = GetScriptForDestination(address.Get()); |
ed21d5bd | 1299 | CAmount nAmount = AmountFromValue(sendTo[name_]); |
e76a3849 WL |
1300 | if (nAmount <= 0) |
1301 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid amount for send"); | |
e3bc5698 JG |
1302 | totalAmount += nAmount; |
1303 | ||
292623ad | 1304 | bool fSubtractFeeFromAmount = false; |
cc71666a | 1305 | for (size_t idx = 0; idx < subtractFeeFromAmount.size(); idx++) { |
d014114d | 1306 | const UniValue& addr = subtractFeeFromAmount[idx]; |
9756b7bd | 1307 | if (addr.get_str() == name_) |
292623ad | 1308 | fSubtractFeeFromAmount = true; |
9756b7bd | 1309 | } |
292623ad CL |
1310 | |
1311 | CRecipient recipient = {scriptPubKey, nAmount, fSubtractFeeFromAmount}; | |
1312 | vecSend.push_back(recipient); | |
e3bc5698 JG |
1313 | } |
1314 | ||
1315 | EnsureWalletIsUnlocked(); | |
1316 | ||
1317 | // Check funds | |
170d7b7a | 1318 | CAmount nBalance = pwalletMain->GetBalance(); |
c83c59f9 | 1319 | //CAmount nBalance = GetAccountBalance(strAccount, nMinDepth, ISMINE_SPENDABLE); |
e3bc5698 | 1320 | if (totalAmount > nBalance) |
738835d7 | 1321 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Account has insufficient funds"); |
e3bc5698 JG |
1322 | |
1323 | // Send | |
1324 | CReserveKey keyChange(pwalletMain); | |
a372168e | 1325 | CAmount nFeeRequired = 0; |
292623ad | 1326 | int nChangePosRet = -1; |
1f00f4e9 | 1327 | string strFailReason; |
292623ad | 1328 | bool fCreated = pwalletMain->CreateTransaction(vecSend, wtx, keyChange, nFeeRequired, nChangePosRet, strFailReason); |
e3bc5698 | 1329 | if (!fCreated) |
1f00f4e9 | 1330 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason); |
e3bc5698 | 1331 | if (!pwalletMain->CommitTransaction(wtx, keyChange)) |
738835d7 | 1332 | throw JSONRPCError(RPC_WALLET_ERROR, "Transaction commit failed"); |
e3bc5698 | 1333 | |
805344dc | 1334 | return wtx.GetHash().GetHex(); |
e3bc5698 JG |
1335 | } |
1336 | ||
723a03d2 | 1337 | // Defined in rpcmisc.cpp |
d014114d | 1338 | extern CScript _createmultisig_redeemScript(const UniValue& params); |
34226be7 | 1339 | |
d014114d | 1340 | UniValue addmultisigaddress(const UniValue& params, bool fHelp) |
34226be7 | 1341 | { |
b9fb692d | 1342 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1343 | return NullUniValue; |
9d365796 | 1344 | |
34226be7 GA |
1345 | if (fHelp || params.size() < 2 || params.size() > 3) |
1346 | { | |
a6099ef3 | 1347 | string msg = "addmultisigaddress nrequired [\"key\",...] ( \"account\" )\n" |
1348 | "\nAdd a nrequired-to-sign multisignature address to the wallet.\n" | |
1333d0d7 | 1349 | "Each key is a " + strprintf("%s",komodo_chainname()) + " address or hex-encoded public key.\n" |
7b782f5b | 1350 | "If 'account' is specified (DEPRECATED), assign address to that account.\n" |
a6099ef3 | 1351 | |
1352 | "\nArguments:\n" | |
1353 | "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" | |
1333d0d7 | 1354 | "2. \"keysobject\" (string, required) A json array of " + strprintf("%s",komodo_chainname()) + " addresses or hex-encoded public keys\n" |
a6099ef3 | 1355 | " [\n" |
1333d0d7 | 1356 | " \"address\" (string) " + strprintf("%s",komodo_chainname()) + " address or hex-encoded public key\n" |
a6099ef3 | 1357 | " ...,\n" |
1358 | " ]\n" | |
3c31eb24 | 1359 | "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 | 1360 | |
1361 | "\nResult:\n" | |
1333d0d7 | 1362 | "\"" + strprintf("%s",komodo_chainname()) + "_address\" (string) A " + strprintf("%s",komodo_chainname()) + " address associated with the keys.\n" |
a6099ef3 | 1363 | |
1364 | "\nExamples:\n" | |
1365 | "\nAdd a multisig address from 2 addresses\n" | |
1333d0d7 | 1366 | + HelpExampleCli("addmultisigaddress", "2 \"[\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\",\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\"]\"") + |
a6099ef3 | 1367 | "\nAs json rpc call\n" |
1333d0d7 | 1368 | + HelpExampleRpc("addmultisigaddress", "2, \"[\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\",\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\"]\"") |
a6099ef3 | 1369 | ; |
34226be7 GA |
1370 | throw runtime_error(msg); |
1371 | } | |
1372 | ||
4401b2d7 EL |
1373 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1374 | ||
34226be7 GA |
1375 | string strAccount; |
1376 | if (params.size() > 2) | |
1377 | strAccount = AccountFromValue(params[2]); | |
e3bc5698 JG |
1378 | |
1379 | // Construct using pay-to-script-hash: | |
787ee0c9 | 1380 | CScript inner = _createmultisig_redeemScript(params); |
066e2a14 | 1381 | CScriptID innerID(inner); |
e3bc5698 JG |
1382 | pwalletMain->AddCScript(inner); |
1383 | ||
a41d5fe0 | 1384 | pwalletMain->SetAddressBook(innerID, strAccount, "send"); |
e3bc5698 JG |
1385 | return CBitcoinAddress(innerID).ToString(); |
1386 | } | |
1387 | ||
1388 | ||
1389 | struct tallyitem | |
1390 | { | |
a372168e | 1391 | CAmount nAmount; |
e3bc5698 | 1392 | int nConf; |
62c9b115 | 1393 | vector<uint256> txids; |
0fa2f889 | 1394 | bool fIsWatchonly; |
e3bc5698 JG |
1395 | tallyitem() |
1396 | { | |
1397 | nAmount = 0; | |
1398 | nConf = std::numeric_limits<int>::max(); | |
0fa2f889 | 1399 | fIsWatchonly = false; |
e3bc5698 JG |
1400 | } |
1401 | }; | |
1402 | ||
d014114d | 1403 | UniValue ListReceived(const UniValue& params, bool fByAccounts) |
e3bc5698 JG |
1404 | { |
1405 | // Minimum confirmations | |
1406 | int nMinDepth = 1; | |
1407 | if (params.size() > 0) | |
1408 | nMinDepth = params[0].get_int(); | |
1409 | ||
1410 | // Whether to include empty accounts | |
1411 | bool fIncludeEmpty = false; | |
1412 | if (params.size() > 1) | |
1413 | fIncludeEmpty = params[1].get_bool(); | |
1414 | ||
a3e192a3 | 1415 | isminefilter filter = ISMINE_SPENDABLE; |
0fa2f889 J |
1416 | if(params.size() > 2) |
1417 | if(params[2].get_bool()) | |
a3e192a3 | 1418 | filter = filter | ISMINE_WATCH_ONLY; |
0fa2f889 | 1419 | |
e3bc5698 JG |
1420 | // Tally |
1421 | map<CBitcoinAddress, tallyitem> mapTally; | |
1422 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) | |
1423 | { | |
1424 | const CWalletTx& wtx = (*it).second; | |
1425 | ||
75a4d512 | 1426 | if (wtx.IsCoinBase() || !CheckFinalTx(wtx)) |
e3bc5698 JG |
1427 | continue; |
1428 | ||
1429 | int nDepth = wtx.GetDepthInMainChain(); | |
1430 | if (nDepth < nMinDepth) | |
1431 | continue; | |
1432 | ||
1433 | BOOST_FOREACH(const CTxOut& txout, wtx.vout) | |
1434 | { | |
1435 | CTxDestination address; | |
0fa2f889 J |
1436 | if (!ExtractDestination(txout.scriptPubKey, address)) |
1437 | continue; | |
1438 | ||
1439 | isminefilter mine = IsMine(*pwalletMain, address); | |
f28707a8 | 1440 | if(!(mine & filter)) |
e3bc5698 JG |
1441 | continue; |
1442 | ||
1443 | tallyitem& item = mapTally[address]; | |
47658758 | 1444 | item.nAmount += txout.nValue; // komodo_interest? |
e3bc5698 | 1445 | item.nConf = min(item.nConf, nDepth); |
805344dc | 1446 | item.txids.push_back(wtx.GetHash()); |
a3e192a3 | 1447 | if (mine & ISMINE_WATCH_ONLY) |
0fa2f889 | 1448 | item.fIsWatchonly = true; |
e3bc5698 JG |
1449 | } |
1450 | } | |
1451 | ||
1452 | // Reply | |
38fc4b70 | 1453 | UniValue ret(UniValue::VARR); |
e3bc5698 | 1454 | map<string, tallyitem> mapAccountTally; |
61885513 | 1455 | BOOST_FOREACH(const PAIRTYPE(CBitcoinAddress, CAddressBookData)& item, pwalletMain->mapAddressBook) |
e3bc5698 JG |
1456 | { |
1457 | const CBitcoinAddress& address = item.first; | |
61885513 | 1458 | const string& strAccount = item.second.name; |
e3bc5698 JG |
1459 | map<CBitcoinAddress, tallyitem>::iterator it = mapTally.find(address); |
1460 | if (it == mapTally.end() && !fIncludeEmpty) | |
1461 | continue; | |
1462 | ||
a372168e | 1463 | CAmount nAmount = 0; |
e3bc5698 | 1464 | int nConf = std::numeric_limits<int>::max(); |
0fa2f889 | 1465 | bool fIsWatchonly = false; |
e3bc5698 JG |
1466 | if (it != mapTally.end()) |
1467 | { | |
1468 | nAmount = (*it).second.nAmount; | |
1469 | nConf = (*it).second.nConf; | |
0fa2f889 | 1470 | fIsWatchonly = (*it).second.fIsWatchonly; |
e3bc5698 JG |
1471 | } |
1472 | ||
1473 | if (fByAccounts) | |
1474 | { | |
1475 | tallyitem& item = mapAccountTally[strAccount]; | |
1476 | item.nAmount += nAmount; | |
1477 | item.nConf = min(item.nConf, nConf); | |
0fa2f889 | 1478 | item.fIsWatchonly = fIsWatchonly; |
e3bc5698 JG |
1479 | } |
1480 | else | |
1481 | { | |
38fc4b70 | 1482 | UniValue obj(UniValue::VOBJ); |
0fa2f889 J |
1483 | if(fIsWatchonly) |
1484 | obj.push_back(Pair("involvesWatchonly", true)); | |
e3bc5698 JG |
1485 | obj.push_back(Pair("address", address.ToString())); |
1486 | obj.push_back(Pair("account", strAccount)); | |
1487 | obj.push_back(Pair("amount", ValueFromAmount(nAmount))); | |
1488 | obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); | |
38fc4b70 | 1489 | UniValue transactions(UniValue::VARR); |
1a204694 | 1490 | if (it != mapTally.end()) |
62c9b115 | 1491 | { |
1a204694 A |
1492 | BOOST_FOREACH(const uint256& item, (*it).second.txids) |
1493 | { | |
1494 | transactions.push_back(item.GetHex()); | |
1495 | } | |
62c9b115 A |
1496 | } |
1497 | obj.push_back(Pair("txids", transactions)); | |
e3bc5698 JG |
1498 | ret.push_back(obj); |
1499 | } | |
1500 | } | |
1501 | ||
1502 | if (fByAccounts) | |
1503 | { | |
1504 | for (map<string, tallyitem>::iterator it = mapAccountTally.begin(); it != mapAccountTally.end(); ++it) | |
1505 | { | |
a372168e | 1506 | CAmount nAmount = (*it).second.nAmount; |
e3bc5698 | 1507 | int nConf = (*it).second.nConf; |
38fc4b70 | 1508 | UniValue obj(UniValue::VOBJ); |
0fa2f889 J |
1509 | if((*it).second.fIsWatchonly) |
1510 | obj.push_back(Pair("involvesWatchonly", true)); | |
e3bc5698 JG |
1511 | obj.push_back(Pair("account", (*it).first)); |
1512 | obj.push_back(Pair("amount", ValueFromAmount(nAmount))); | |
1513 | obj.push_back(Pair("confirmations", (nConf == std::numeric_limits<int>::max() ? 0 : nConf))); | |
1514 | ret.push_back(obj); | |
1515 | } | |
1516 | } | |
1517 | ||
1518 | return ret; | |
1519 | } | |
1520 | ||
d014114d | 1521 | UniValue listreceivedbyaddress(const UniValue& params, bool fHelp) |
e3bc5698 | 1522 | { |
b9fb692d | 1523 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1524 | return NullUniValue; |
9d365796 | 1525 | |
0fa2f889 | 1526 | if (fHelp || params.size() > 3) |
e3bc5698 | 1527 | throw runtime_error( |
0fa2f889 | 1528 | "listreceivedbyaddress ( minconf includeempty includeWatchonly)\n" |
a6099ef3 | 1529 | "\nList balances by receiving address.\n" |
1530 | "\nArguments:\n" | |
1531 | "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" | |
5617267c | 1532 | "2. includeempty (numeric, optional, default=false) Whether to include addresses that haven't received any payments.\n" |
0fa2f889 | 1533 | "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" |
a6099ef3 | 1534 | |
1535 | "\nResult:\n" | |
1536 | "[\n" | |
1537 | " {\n" | |
8f6860a0 | 1538 | " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" |
a6099ef3 | 1539 | " \"address\" : \"receivingaddress\", (string) The receiving address\n" |
7b782f5b | 1540 | " \"account\" : \"accountname\", (string) DEPRECATED. The account of the receiving address. The default account is \"\".\n" |
1333d0d7 | 1541 | " \"amount\" : x.xxx, (numeric) The total amount in " + strprintf("%s",komodo_chainname()) + " received by the address\n" |
a6099ef3 | 1542 | " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n" |
1543 | " }\n" | |
1544 | " ,...\n" | |
1545 | "]\n" | |
1546 | ||
1547 | "\nExamples:\n" | |
1548 | + HelpExampleCli("listreceivedbyaddress", "") | |
1549 | + HelpExampleCli("listreceivedbyaddress", "6 true") | |
0fa2f889 | 1550 | + HelpExampleRpc("listreceivedbyaddress", "6, true, true") |
a6099ef3 | 1551 | ); |
e3bc5698 | 1552 | |
4401b2d7 EL |
1553 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1554 | ||
e3bc5698 JG |
1555 | return ListReceived(params, false); |
1556 | } | |
1557 | ||
d014114d | 1558 | UniValue listreceivedbyaccount(const UniValue& params, bool fHelp) |
e3bc5698 | 1559 | { |
b9fb692d | 1560 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1561 | return NullUniValue; |
9d365796 | 1562 | |
0fa2f889 | 1563 | if (fHelp || params.size() > 3) |
e3bc5698 | 1564 | throw runtime_error( |
0fa2f889 | 1565 | "listreceivedbyaccount ( minconf includeempty includeWatchonly)\n" |
7b782f5b | 1566 | "\nDEPRECATED. List balances by account.\n" |
a6099ef3 | 1567 | "\nArguments:\n" |
1568 | "1. minconf (numeric, optional, default=1) The minimum number of confirmations before payments are included.\n" | |
1569 | "2. includeempty (boolean, optional, default=false) Whether to include accounts that haven't received any payments.\n" | |
0fa2f889 | 1570 | "3. includeWatchonly (bool, optional, default=false) Whether to include watchonly addresses (see 'importaddress').\n" |
a6099ef3 | 1571 | |
1572 | "\nResult:\n" | |
1573 | "[\n" | |
1574 | " {\n" | |
8f6860a0 | 1575 | " \"involvesWatchonly\" : true, (bool) Only returned if imported addresses were involved in transaction\n" |
a6099ef3 | 1576 | " \"account\" : \"accountname\", (string) The account name of the receiving account\n" |
1577 | " \"amount\" : x.xxx, (numeric) The total amount received by addresses with this account\n" | |
1578 | " \"confirmations\" : n (numeric) The number of confirmations of the most recent transaction included\n" | |
1579 | " }\n" | |
1580 | " ,...\n" | |
1581 | "]\n" | |
1582 | ||
1583 | "\nExamples:\n" | |
1584 | + HelpExampleCli("listreceivedbyaccount", "") | |
1585 | + HelpExampleCli("listreceivedbyaccount", "6 true") | |
0fa2f889 | 1586 | + HelpExampleRpc("listreceivedbyaccount", "6, true, true") |
a6099ef3 | 1587 | ); |
e3bc5698 | 1588 | |
4401b2d7 EL |
1589 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1590 | ||
e3bc5698 JG |
1591 | return ListReceived(params, true); |
1592 | } | |
1593 | ||
851f58f9 | 1594 | static void MaybePushAddress(UniValue & entry, const CTxDestination &dest) |
cc6cfab3 LD |
1595 | { |
1596 | CBitcoinAddress addr; | |
1597 | if (addr.Set(dest)) | |
1598 | entry.push_back(Pair("address", addr.ToString())); | |
1599 | } | |
1600 | ||
d014114d | 1601 | void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDepth, bool fLong, UniValue& ret, const isminefilter& filter) |
e3bc5698 | 1602 | { |
a372168e | 1603 | CAmount nFee; |
e3bc5698 | 1604 | string strSentAccount; |
1b4568cb CL |
1605 | list<COutputEntry> listReceived; |
1606 | list<COutputEntry> listSent; | |
e3bc5698 | 1607 | |
d7d5d23b | 1608 | wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, filter); |
e3bc5698 JG |
1609 | |
1610 | bool fAllAccounts = (strAccount == string("*")); | |
a3e192a3 | 1611 | bool involvesWatchonly = wtx.IsFromMe(ISMINE_WATCH_ONLY); |
e3bc5698 | 1612 | |
e3bc5698 JG |
1613 | // Sent |
1614 | if ((!listSent.empty() || nFee != 0) && (fAllAccounts || strAccount == strSentAccount)) | |
1615 | { | |
1b4568cb | 1616 | BOOST_FOREACH(const COutputEntry& s, listSent) |
e3bc5698 | 1617 | { |
38fc4b70 | 1618 | UniValue entry(UniValue::VOBJ); |
1b4568cb | 1619 | if(involvesWatchonly || (::IsMine(*pwalletMain, s.destination) & ISMINE_WATCH_ONLY)) |
952877e0 | 1620 | entry.push_back(Pair("involvesWatchonly", true)); |
e3bc5698 | 1621 | entry.push_back(Pair("account", strSentAccount)); |
1b4568cb | 1622 | MaybePushAddress(entry, s.destination); |
b96f6a77 | 1623 | entry.push_back(Pair("category", "send")); |
1b4568cb CL |
1624 | entry.push_back(Pair("amount", ValueFromAmount(-s.amount))); |
1625 | entry.push_back(Pair("vout", s.vout)); | |
e3bc5698 JG |
1626 | entry.push_back(Pair("fee", ValueFromAmount(-nFee))); |
1627 | if (fLong) | |
1628 | WalletTxToJSON(wtx, entry); | |
9d365796 | 1629 | #ifdef __APPLE__ |
1630 | entry.push_back(Pair("size", (uint64_t)static_cast<CTransaction>(wtx).GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION))); | |
1631 | #else | |
f32cade8 | 1632 | entry.push_back(Pair("size", static_cast<CTransaction>(wtx).GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION))); |
9d365796 | 1633 | #endif |
e3bc5698 JG |
1634 | ret.push_back(entry); |
1635 | } | |
1636 | } | |
1637 | ||
1638 | // Received | |
1639 | if (listReceived.size() > 0 && wtx.GetDepthInMainChain() >= nMinDepth) | |
1640 | { | |
1b4568cb | 1641 | BOOST_FOREACH(const COutputEntry& r, listReceived) |
e3bc5698 JG |
1642 | { |
1643 | string account; | |
1b4568cb CL |
1644 | if (pwalletMain->mapAddressBook.count(r.destination)) |
1645 | account = pwalletMain->mapAddressBook[r.destination].name; | |
e3bc5698 JG |
1646 | if (fAllAccounts || (account == strAccount)) |
1647 | { | |
38fc4b70 | 1648 | UniValue entry(UniValue::VOBJ); |
1b4568cb | 1649 | if(involvesWatchonly || (::IsMine(*pwalletMain, r.destination) & ISMINE_WATCH_ONLY)) |
952877e0 | 1650 | entry.push_back(Pair("involvesWatchonly", true)); |
e3bc5698 | 1651 | entry.push_back(Pair("account", account)); |
1b4568cb | 1652 | MaybePushAddress(entry, r.destination); |
e07c8e91 LD |
1653 | if (wtx.IsCoinBase()) |
1654 | { | |
1655 | if (wtx.GetDepthInMainChain() < 1) | |
1656 | entry.push_back(Pair("category", "orphan")); | |
1657 | else if (wtx.GetBlocksToMaturity() > 0) | |
1658 | entry.push_back(Pair("category", "immature")); | |
1659 | else | |
1660 | entry.push_back(Pair("category", "generate")); | |
1661 | } | |
1662 | else | |
2b72d46f | 1663 | { |
b96f6a77 | 1664 | entry.push_back(Pair("category", "receive")); |
2b72d46f | 1665 | } |
1b4568cb CL |
1666 | entry.push_back(Pair("amount", ValueFromAmount(r.amount))); |
1667 | entry.push_back(Pair("vout", r.vout)); | |
e3bc5698 JG |
1668 | if (fLong) |
1669 | WalletTxToJSON(wtx, entry); | |
9d365796 | 1670 | #ifdef __APPLE__ |
1671 | entry.push_back(Pair("size", (uint64_t)static_cast<CTransaction>(wtx).GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION))); | |
1672 | #else | |
1673 | entry.push_back(Pair("size", static_cast<CTransaction>(wtx).GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION))); | |
1674 | #endif | |
e3bc5698 JG |
1675 | ret.push_back(entry); |
1676 | } | |
1677 | } | |
1678 | } | |
1679 | } | |
1680 | ||
d014114d | 1681 | void AcentryToJSON(const CAccountingEntry& acentry, const string& strAccount, UniValue& ret) |
e3bc5698 JG |
1682 | { |
1683 | bool fAllAccounts = (strAccount == string("*")); | |
1684 | ||
1685 | if (fAllAccounts || acentry.strAccount == strAccount) | |
1686 | { | |
38fc4b70 | 1687 | UniValue entry(UniValue::VOBJ); |
e3bc5698 JG |
1688 | entry.push_back(Pair("account", acentry.strAccount)); |
1689 | entry.push_back(Pair("category", "move")); | |
d56e30ca | 1690 | entry.push_back(Pair("time", acentry.nTime)); |
e3bc5698 JG |
1691 | entry.push_back(Pair("amount", ValueFromAmount(acentry.nCreditDebit))); |
1692 | entry.push_back(Pair("otheraccount", acentry.strOtherAccount)); | |
1693 | entry.push_back(Pair("comment", acentry.strComment)); | |
1694 | ret.push_back(entry); | |
1695 | } | |
1696 | } | |
1697 | ||
d014114d | 1698 | UniValue listtransactions(const UniValue& params, bool fHelp) |
e3bc5698 | 1699 | { |
b9fb692d | 1700 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1701 | return NullUniValue; |
9d365796 | 1702 | |
d7d5d23b | 1703 | if (fHelp || params.size() > 4) |
e3bc5698 | 1704 | throw runtime_error( |
d7d5d23b | 1705 | "listtransactions ( \"account\" count from includeWatchonly)\n" |
a6099ef3 | 1706 | "\nReturns up to 'count' most recent transactions skipping the first 'from' transactions for account 'account'.\n" |
1707 | "\nArguments:\n" | |
7b782f5b | 1708 | "1. \"account\" (string, optional) DEPRECATED. The account name. Should be \"*\".\n" |
a6099ef3 | 1709 | "2. count (numeric, optional, default=10) The number of transactions to return\n" |
1710 | "3. from (numeric, optional, default=0) The number of transactions to skip\n" | |
d7d5d23b | 1711 | "4. includeWatchonly (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')\n" |
a6099ef3 | 1712 | "\nResult:\n" |
1713 | "[\n" | |
1714 | " {\n" | |
7b782f5b | 1715 | " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. \n" |
a6099ef3 | 1716 | " It will be \"\" for the default account.\n" |
1333d0d7 | 1717 | " \"address\":\"" + strprintf("%s",komodo_chainname()) + "_address\", (string) The " + strprintf("%s",komodo_chainname()) + " address of the transaction. Not present for \n" |
a6099ef3 | 1718 | " move transactions (category = move).\n" |
1719 | " \"category\":\"send|receive|move\", (string) The transaction category. 'move' is a local (off blockchain)\n" | |
1720 | " transaction between accounts, and not associated with an address,\n" | |
1721 | " transaction id or block. 'send' and 'receive' transactions are \n" | |
1722 | " associated with an address, transaction id and block details\n" | |
1333d0d7 | 1723 | " \"amount\": x.xxx, (numeric) The amount in " + strprintf("%s",komodo_chainname()) + ". This is negative for the 'send' category, and for the\n" |
a6099ef3 | 1724 | " 'move' category for moves outbound. It is positive for the 'receive' category,\n" |
1725 | " and for the 'move' category for inbound funds.\n" | |
1b4568cb | 1726 | " \"vout\" : n, (numeric) the vout value\n" |
1333d0d7 | 1727 | " \"fee\": x.xxx, (numeric) The amount of the fee in " + strprintf("%s",komodo_chainname()) + ". This is negative and only available for the \n" |
a6099ef3 | 1728 | " 'send' category of transactions.\n" |
1729 | " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and \n" | |
1730 | " 'receive' category of transactions.\n" | |
1731 | " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive'\n" | |
1732 | " category of transactions.\n" | |
1733 | " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive'\n" | |
1734 | " category of transactions.\n" | |
b5ef85c7 | 1735 | " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" |
a6099ef3 | 1736 | " \"time\": xxx, (numeric) The transaction time in seconds since epoch (midnight Jan 1 1970 GMT).\n" |
1737 | " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (midnight Jan 1 1970 GMT). Available \n" | |
1738 | " for 'send' and 'receive' category of transactions.\n" | |
1739 | " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" | |
1740 | " \"otheraccount\": \"accountname\", (string) For the 'move' category of transactions, the account the funds came \n" | |
1741 | " from (for receiving funds, positive amounts), or went to (for sending funds,\n" | |
1742 | " negative amounts).\n" | |
f32cade8 | 1743 | " \"size\": n, (numeric) Transaction size in bytes\n" |
a6099ef3 | 1744 | " }\n" |
1745 | "]\n" | |
1746 | ||
1747 | "\nExamples:\n" | |
1748 | "\nList the most recent 10 transactions in the systems\n" | |
1749 | + HelpExampleCli("listtransactions", "") + | |
7b782f5b LD |
1750 | "\nList transactions 100 to 120\n" |
1751 | + HelpExampleCli("listtransactions", "\"*\" 20 100") + | |
a6099ef3 | 1752 | "\nAs a json rpc call\n" |
7b782f5b | 1753 | + HelpExampleRpc("listtransactions", "\"*\", 20, 100") |
a6099ef3 | 1754 | ); |
e3bc5698 | 1755 | |
4401b2d7 EL |
1756 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1757 | ||
e3bc5698 JG |
1758 | string strAccount = "*"; |
1759 | if (params.size() > 0) | |
1760 | strAccount = params[0].get_str(); | |
1761 | int nCount = 10; | |
1762 | if (params.size() > 1) | |
1763 | nCount = params[1].get_int(); | |
1764 | int nFrom = 0; | |
1765 | if (params.size() > 2) | |
1766 | nFrom = params[2].get_int(); | |
a3e192a3 | 1767 | isminefilter filter = ISMINE_SPENDABLE; |
a5c6c5d6 J |
1768 | if(params.size() > 3) |
1769 | if(params[3].get_bool()) | |
a3e192a3 | 1770 | filter = filter | ISMINE_WATCH_ONLY; |
e3bc5698 JG |
1771 | |
1772 | if (nCount < 0) | |
738835d7 | 1773 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative count"); |
e3bc5698 | 1774 | if (nFrom < 0) |
738835d7 | 1775 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative from"); |
e3bc5698 | 1776 | |
38fc4b70 | 1777 | UniValue ret(UniValue::VARR); |
e3bc5698 | 1778 | |
ddb709e9 LD |
1779 | std::list<CAccountingEntry> acentries; |
1780 | CWallet::TxItems txOrdered = pwalletMain->OrderedTxItems(acentries, strAccount); | |
e3bc5698 JG |
1781 | |
1782 | // iterate backwards until we have nCount items to return: | |
c3f95ef1 | 1783 | for (CWallet::TxItems::reverse_iterator it = txOrdered.rbegin(); it != txOrdered.rend(); ++it) |
e3bc5698 JG |
1784 | { |
1785 | CWalletTx *const pwtx = (*it).second.first; | |
1786 | if (pwtx != 0) | |
d7d5d23b | 1787 | ListTransactions(*pwtx, strAccount, 0, true, ret, filter); |
e3bc5698 JG |
1788 | CAccountingEntry *const pacentry = (*it).second.second; |
1789 | if (pacentry != 0) | |
1790 | AcentryToJSON(*pacentry, strAccount, ret); | |
1791 | ||
1792 | if ((int)ret.size() >= (nCount+nFrom)) break; | |
1793 | } | |
1794 | // ret is newest to oldest | |
1795 | ||
1796 | if (nFrom > (int)ret.size()) | |
1797 | nFrom = ret.size(); | |
1798 | if ((nFrom + nCount) > (int)ret.size()) | |
1799 | nCount = ret.size() - nFrom; | |
ed21d5bd JG |
1800 | |
1801 | vector<UniValue> arrTmp = ret.getValues(); | |
1802 | ||
1803 | vector<UniValue>::iterator first = arrTmp.begin(); | |
e3bc5698 | 1804 | std::advance(first, nFrom); |
ed21d5bd | 1805 | vector<UniValue>::iterator last = arrTmp.begin(); |
e3bc5698 JG |
1806 | std::advance(last, nFrom+nCount); |
1807 | ||
ed21d5bd JG |
1808 | if (last != arrTmp.end()) arrTmp.erase(last, arrTmp.end()); |
1809 | if (first != arrTmp.begin()) arrTmp.erase(arrTmp.begin(), first); | |
e3bc5698 | 1810 | |
ed21d5bd | 1811 | std::reverse(arrTmp.begin(), arrTmp.end()); // Return oldest to newest |
e3bc5698 | 1812 | |
ed21d5bd | 1813 | ret.clear(); |
38fc4b70 | 1814 | ret.setArray(); |
ed21d5bd | 1815 | ret.push_backV(arrTmp); |
e3bc5698 JG |
1816 | |
1817 | return ret; | |
1818 | } | |
1819 | ||
d014114d | 1820 | UniValue listaccounts(const UniValue& params, bool fHelp) |
e3bc5698 | 1821 | { |
b9fb692d | 1822 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1823 | return NullUniValue; |
9d365796 | 1824 | |
83f3543f | 1825 | if (fHelp || params.size() > 2) |
e3bc5698 | 1826 | throw runtime_error( |
83f3543f | 1827 | "listaccounts ( minconf includeWatchonly)\n" |
7b782f5b | 1828 | "\nDEPRECATED. Returns Object that has account names as keys, account balances as values.\n" |
a6099ef3 | 1829 | "\nArguments:\n" |
5617267c | 1830 | "1. minconf (numeric, optional, default=1) Only include transactions with at least this many confirmations\n" |
83f3543f | 1831 | "2. includeWatchonly (bool, optional, default=false) Include balances in watchonly addresses (see 'importaddress')\n" |
a6099ef3 | 1832 | "\nResult:\n" |
1833 | "{ (json object where keys are account names, and values are numeric balances\n" | |
1834 | " \"account\": x.xxx, (numeric) The property name is the account name, and the value is the total balance for the account.\n" | |
1835 | " ...\n" | |
1836 | "}\n" | |
1837 | "\nExamples:\n" | |
1838 | "\nList account balances where there at least 1 confirmation\n" | |
1839 | + HelpExampleCli("listaccounts", "") + | |
1840 | "\nList account balances including zero confirmation transactions\n" | |
1841 | + HelpExampleCli("listaccounts", "0") + | |
1842 | "\nList account balances for 6 or more confirmations\n" | |
1843 | + HelpExampleCli("listaccounts", "6") + | |
1844 | "\nAs json rpc call\n" | |
1845 | + HelpExampleRpc("listaccounts", "6") | |
1846 | ); | |
e3bc5698 | 1847 | |
4401b2d7 EL |
1848 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1849 | ||
e3bc5698 JG |
1850 | int nMinDepth = 1; |
1851 | if (params.size() > 0) | |
1852 | nMinDepth = params[0].get_int(); | |
a3e192a3 | 1853 | isminefilter includeWatchonly = ISMINE_SPENDABLE; |
a5c6c5d6 J |
1854 | if(params.size() > 1) |
1855 | if(params[1].get_bool()) | |
a3e192a3 | 1856 | includeWatchonly = includeWatchonly | ISMINE_WATCH_ONLY; |
e3bc5698 | 1857 | |
a372168e | 1858 | map<string, CAmount> mapAccountBalances; |
61885513 | 1859 | BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& entry, pwalletMain->mapAddressBook) { |
83f3543f | 1860 | if (IsMine(*pwalletMain, entry.first) & includeWatchonly) // This address belongs to me |
61885513 | 1861 | mapAccountBalances[entry.second.name] = 0; |
e3bc5698 JG |
1862 | } |
1863 | ||
1864 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); ++it) | |
1865 | { | |
1866 | const CWalletTx& wtx = (*it).second; | |
a372168e | 1867 | CAmount nFee; |
e3bc5698 | 1868 | string strSentAccount; |
1b4568cb CL |
1869 | list<COutputEntry> listReceived; |
1870 | list<COutputEntry> listSent; | |
93a18a36 GA |
1871 | int nDepth = wtx.GetDepthInMainChain(); |
1872 | if (wtx.GetBlocksToMaturity() > 0 || nDepth < 0) | |
731b89b8 | 1873 | continue; |
83f3543f | 1874 | wtx.GetAmounts(listReceived, listSent, nFee, strSentAccount, includeWatchonly); |
e3bc5698 | 1875 | mapAccountBalances[strSentAccount] -= nFee; |
1b4568cb CL |
1876 | BOOST_FOREACH(const COutputEntry& s, listSent) |
1877 | mapAccountBalances[strSentAccount] -= s.amount; | |
93a18a36 | 1878 | if (nDepth >= nMinDepth) |
e3bc5698 | 1879 | { |
1b4568cb CL |
1880 | BOOST_FOREACH(const COutputEntry& r, listReceived) |
1881 | if (pwalletMain->mapAddressBook.count(r.destination)) | |
1882 | mapAccountBalances[pwalletMain->mapAddressBook[r.destination].name] += r.amount; | |
e3bc5698 | 1883 | else |
1b4568cb | 1884 | mapAccountBalances[""] += r.amount; |
e3bc5698 JG |
1885 | } |
1886 | } | |
1887 | ||
1888 | list<CAccountingEntry> acentries; | |
1889 | CWalletDB(pwalletMain->strWalletFile).ListAccountCreditDebit("*", acentries); | |
1890 | BOOST_FOREACH(const CAccountingEntry& entry, acentries) | |
1891 | mapAccountBalances[entry.strAccount] += entry.nCreditDebit; | |
1892 | ||
38fc4b70 | 1893 | UniValue ret(UniValue::VOBJ); |
a372168e | 1894 | BOOST_FOREACH(const PAIRTYPE(string, CAmount)& accountBalance, mapAccountBalances) { |
e3bc5698 JG |
1895 | ret.push_back(Pair(accountBalance.first, ValueFromAmount(accountBalance.second))); |
1896 | } | |
1897 | return ret; | |
1898 | } | |
1899 | ||
d014114d | 1900 | UniValue listsinceblock(const UniValue& params, bool fHelp) |
e3bc5698 | 1901 | { |
b9fb692d | 1902 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1903 | return NullUniValue; |
9d365796 | 1904 | |
e3bc5698 JG |
1905 | if (fHelp) |
1906 | throw runtime_error( | |
d7d5d23b | 1907 | "listsinceblock ( \"blockhash\" target-confirmations includeWatchonly)\n" |
a6099ef3 | 1908 | "\nGet all transactions in blocks since block [blockhash], or all transactions if omitted\n" |
1909 | "\nArguments:\n" | |
1910 | "1. \"blockhash\" (string, optional) The block hash to list transactions since\n" | |
1911 | "2. target-confirmations: (numeric, optional) The confirmations required, must be 1 or more\n" | |
d7d5d23b | 1912 | "3. includeWatchonly: (bool, optional, default=false) Include transactions to watchonly addresses (see 'importaddress')" |
a6099ef3 | 1913 | "\nResult:\n" |
1914 | "{\n" | |
1915 | " \"transactions\": [\n" | |
7b782f5b | 1916 | " \"account\":\"accountname\", (string) DEPRECATED. The account name associated with the transaction. Will be \"\" for the default account.\n" |
1333d0d7 | 1917 | " \"address\":\"" + strprintf("%s",komodo_chainname()) + "_address\", (string) The " + strprintf("%s",komodo_chainname()) + " address of the transaction. Not present for move transactions (category = move).\n" |
a6099ef3 | 1918 | " \"category\":\"send|receive\", (string) The transaction category. 'send' has negative amounts, 'receive' has positive amounts.\n" |
1333d0d7 | 1919 | " \"amount\": x.xxx, (numeric) The amount in " + strprintf("%s",komodo_chainname()) + ". This is negative for the 'send' category, and for the 'move' category for moves \n" |
a6099ef3 | 1920 | " outbound. It is positive for the 'receive' category, and for the 'move' category for inbound funds.\n" |
1b4568cb | 1921 | " \"vout\" : n, (numeric) the vout value\n" |
1333d0d7 | 1922 | " \"fee\": x.xxx, (numeric) The amount of the fee in " + strprintf("%s",komodo_chainname()) + ". This is negative and only available for the 'send' category of transactions.\n" |
a6099ef3 | 1923 | " \"confirmations\": n, (numeric) The number of confirmations for the transaction. Available for 'send' and 'receive' category of transactions.\n" |
1924 | " \"blockhash\": \"hashvalue\", (string) The block hash containing the transaction. Available for 'send' and 'receive' category of transactions.\n" | |
1925 | " \"blockindex\": n, (numeric) The block index containing the transaction. Available for 'send' and 'receive' category of transactions.\n" | |
1926 | " \"blocktime\": xxx, (numeric) The block time in seconds since epoch (1 Jan 1970 GMT).\n" | |
b5ef85c7 | 1927 | " \"txid\": \"transactionid\", (string) The transaction id. Available for 'send' and 'receive' category of transactions.\n" |
a6099ef3 | 1928 | " \"time\": xxx, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT).\n" |
1929 | " \"timereceived\": xxx, (numeric) The time received in seconds since epoch (Jan 1 1970 GMT). Available for 'send' and 'receive' category of transactions.\n" | |
1930 | " \"comment\": \"...\", (string) If a comment is associated with the transaction.\n" | |
1931 | " \"to\": \"...\", (string) If a comment to is associated with the transaction.\n" | |
1932 | " ],\n" | |
1933 | " \"lastblock\": \"lastblockhash\" (string) The hash of the last block\n" | |
1934 | "}\n" | |
1935 | "\nExamples:\n" | |
1936 | + HelpExampleCli("listsinceblock", "") | |
1937 | + HelpExampleCli("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\" 6") | |
1938 | + HelpExampleRpc("listsinceblock", "\"000000000000000bacf66f7497b7dc45ef753ee9a7d38571037cdb1a57f663ad\", 6") | |
1939 | ); | |
e3bc5698 | 1940 | |
4401b2d7 EL |
1941 | LOCK2(cs_main, pwalletMain->cs_wallet); |
1942 | ||
e3bc5698 JG |
1943 | CBlockIndex *pindex = NULL; |
1944 | int target_confirms = 1; | |
a3e192a3 | 1945 | isminefilter filter = ISMINE_SPENDABLE; |
e3bc5698 JG |
1946 | |
1947 | if (params.size() > 0) | |
1948 | { | |
4f152496 | 1949 | uint256 blockId; |
e3bc5698 JG |
1950 | |
1951 | blockId.SetHex(params[0].get_str()); | |
145d5be8 | 1952 | BlockMap::iterator it = mapBlockIndex.find(blockId); |
e4daecda PW |
1953 | if (it != mapBlockIndex.end()) |
1954 | pindex = it->second; | |
e3bc5698 JG |
1955 | } |
1956 | ||
1957 | if (params.size() > 1) | |
1958 | { | |
1959 | target_confirms = params[1].get_int(); | |
1960 | ||
1961 | if (target_confirms < 1) | |
738835d7 | 1962 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter"); |
e3bc5698 JG |
1963 | } |
1964 | ||
a5c6c5d6 J |
1965 | if(params.size() > 2) |
1966 | if(params[2].get_bool()) | |
a3e192a3 | 1967 | filter = filter | ISMINE_WATCH_ONLY; |
a5c6c5d6 | 1968 | |
4c6d41b8 | 1969 | int depth = pindex ? (1 + chainActive.Height() - pindex->nHeight) : -1; |
e3bc5698 | 1970 | |
38fc4b70 | 1971 | UniValue transactions(UniValue::VARR); |
e3bc5698 JG |
1972 | |
1973 | for (map<uint256, CWalletTx>::iterator it = pwalletMain->mapWallet.begin(); it != pwalletMain->mapWallet.end(); it++) | |
1974 | { | |
1975 | CWalletTx tx = (*it).second; | |
1976 | ||
1977 | if (depth == -1 || tx.GetDepthInMainChain() < depth) | |
d7d5d23b | 1978 | ListTransactions(tx, "*", 0, true, transactions, filter); |
e3bc5698 JG |
1979 | } |
1980 | ||
4c6d41b8 | 1981 | CBlockIndex *pblockLast = chainActive[chainActive.Height() + 1 - target_confirms]; |
4f152496 | 1982 | uint256 lastblock = pblockLast ? pblockLast->GetBlockHash() : uint256(); |
e3bc5698 | 1983 | |
38fc4b70 | 1984 | UniValue ret(UniValue::VOBJ); |
e3bc5698 JG |
1985 | ret.push_back(Pair("transactions", transactions)); |
1986 | ret.push_back(Pair("lastblock", lastblock.GetHex())); | |
1987 | ||
1988 | return ret; | |
1989 | } | |
1990 | ||
d014114d | 1991 | UniValue gettransaction(const UniValue& params, bool fHelp) |
e3bc5698 | 1992 | { |
b9fb692d | 1993 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 1994 | return NullUniValue; |
9d365796 | 1995 | |
f87ba3df | 1996 | if (fHelp || params.size() < 1 || params.size() > 2) |
e3bc5698 | 1997 | throw runtime_error( |
57e1716d | 1998 | "gettransaction \"txid\" ( includeWatchonly )\n" |
a6099ef3 | 1999 | "\nGet detailed information about in-wallet transaction <txid>\n" |
2000 | "\nArguments:\n" | |
2001 | "1. \"txid\" (string, required) The transaction id\n" | |
f87ba3df | 2002 | "2. \"includeWatchonly\" (bool, optional, default=false) Whether to include watchonly addresses in balance calculation and details[]\n" |
a6099ef3 | 2003 | "\nResult:\n" |
2004 | "{\n" | |
1333d0d7 | 2005 | " \"amount\" : x.xxx, (numeric) The transaction amount in " + strprintf("%s",komodo_chainname()) + "\n" |
a6099ef3 | 2006 | " \"confirmations\" : n, (numeric) The number of confirmations\n" |
2007 | " \"blockhash\" : \"hash\", (string) The block hash\n" | |
2008 | " \"blockindex\" : xx, (numeric) The block index\n" | |
2009 | " \"blocktime\" : ttt, (numeric) The time in seconds since epoch (1 Jan 1970 GMT)\n" | |
b5ef85c7 | 2010 | " \"txid\" : \"transactionid\", (string) The transaction id.\n" |
a6099ef3 | 2011 | " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (1 Jan 1970 GMT)\n" |
2012 | " \"timereceived\" : ttt, (numeric) The time received in seconds since epoch (1 Jan 1970 GMT)\n" | |
2013 | " \"details\" : [\n" | |
2014 | " {\n" | |
7b782f5b | 2015 | " \"account\" : \"accountname\", (string) DEPRECATED. The account name involved in the transaction, can be \"\" for the default account.\n" |
1333d0d7 | 2016 | " \"address\" : \"" + strprintf("%s",komodo_chainname()) + "_address\", (string) The " + strprintf("%s",komodo_chainname()) + " address involved in the transaction\n" |
a6099ef3 | 2017 | " \"category\" : \"send|receive\", (string) The category, either 'send' or 'receive'\n" |
1333d0d7 | 2018 | " \"amount\" : x.xxx (numeric) The amount in " + strprintf("%s",komodo_chainname()) + "\n" |
1b4568cb | 2019 | " \"vout\" : n, (numeric) the vout value\n" |
a6099ef3 | 2020 | " }\n" |
2021 | " ,...\n" | |
3a1c20b7 | 2022 | " ],\n" |
f7cfb52d S |
2023 | " \"vjoinsplit\" : [\n" |
2024 | " {\n" | |
2025 | " \"anchor\" : \"treestateref\", (string) Merkle root of note commitment tree\n" | |
2026 | " \"nullifiers\" : [ string, ... ] (string) Nullifiers of input notes\n" | |
2027 | " \"commitments\" : [ string, ... ] (string) Note commitments for note outputs\n" | |
2028 | " \"macs\" : [ string, ... ] (string) Message authentication tags\n" | |
2029 | " \"vpub_old\" : x.xxx (numeric) The amount removed from the transparent value pool\n" | |
2030 | " \"vpub_new\" : x.xxx, (numeric) The amount added to the transparent value pool\n" | |
2031 | " }\n" | |
2032 | " ,...\n" | |
2033 | " ],\n" | |
3a1c20b7 | 2034 | " \"hex\" : \"data\" (string) Raw data for transaction\n" |
a6099ef3 | 2035 | "}\n" |
2036 | ||
ab45ddb5 | 2037 | "\nExamples:\n" |
a6099ef3 | 2038 | + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") |
57e1716d | 2039 | + HelpExampleCli("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\" true") |
a6099ef3 | 2040 | + HelpExampleRpc("gettransaction", "\"1075db55d416d3ca199f55b6084e2115b9345e16c5cf302fc80e9d5fbf5d48d\"") |
2041 | ); | |
e3bc5698 | 2042 | |
4401b2d7 EL |
2043 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2044 | ||
e3bc5698 JG |
2045 | uint256 hash; |
2046 | hash.SetHex(params[0].get_str()); | |
2047 | ||
a3e192a3 | 2048 | isminefilter filter = ISMINE_SPENDABLE; |
f87ba3df J |
2049 | if(params.size() > 1) |
2050 | if(params[1].get_bool()) | |
a3e192a3 | 2051 | filter = filter | ISMINE_WATCH_ONLY; |
f87ba3df | 2052 | |
38fc4b70 | 2053 | UniValue entry(UniValue::VOBJ); |
e3bc5698 | 2054 | if (!pwalletMain->mapWallet.count(hash)) |
738835d7 | 2055 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid or non-wallet transaction id"); |
e3bc5698 JG |
2056 | const CWalletTx& wtx = pwalletMain->mapWallet[hash]; |
2057 | ||
ccca27a7 | 2058 | CAmount nCredit = wtx.GetCredit(filter); |
a372168e MF |
2059 | CAmount nDebit = wtx.GetDebit(filter); |
2060 | CAmount nNet = nCredit - nDebit; | |
2061 | CAmount nFee = (wtx.IsFromMe(filter) ? wtx.GetValueOut() - nDebit : 0); | |
e3bc5698 JG |
2062 | |
2063 | entry.push_back(Pair("amount", ValueFromAmount(nNet - nFee))); | |
80dda36a | 2064 | if (wtx.IsFromMe(filter)) |
e3bc5698 JG |
2065 | entry.push_back(Pair("fee", ValueFromAmount(nFee))); |
2066 | ||
2067 | WalletTxToJSON(wtx, entry); | |
2068 | ||
38fc4b70 | 2069 | UniValue details(UniValue::VARR); |
f87ba3df | 2070 | ListTransactions(wtx, "*", 0, false, details, filter); |
e3bc5698 JG |
2071 | entry.push_back(Pair("details", details)); |
2072 | ||
ae775b5b | 2073 | string strHex = EncodeHexTx(static_cast<CTransaction>(wtx)); |
3a1c20b7 WL |
2074 | entry.push_back(Pair("hex", strHex)); |
2075 | ||
e3bc5698 JG |
2076 | return entry; |
2077 | } | |
2078 | ||
2079 | ||
d014114d | 2080 | UniValue backupwallet(const UniValue& params, bool fHelp) |
e3bc5698 | 2081 | { |
b9fb692d | 2082 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2083 | return NullUniValue; |
9d365796 | 2084 | |
e3bc5698 JG |
2085 | if (fHelp || params.size() != 1) |
2086 | throw runtime_error( | |
a6099ef3 | 2087 | "backupwallet \"destination\"\n" |
9064d73b | 2088 | "\nSafely copies wallet.dat to destination filename\n" |
a6099ef3 | 2089 | "\nArguments:\n" |
9064d73b S |
2090 | "1. \"destination\" (string, required) The destination filename, saved in the directory set by -exportdir option.\n" |
2091 | "\nResult:\n" | |
2092 | "\"path\" (string) The full path of the destination file\n" | |
a6099ef3 | 2093 | "\nExamples:\n" |
9064d73b S |
2094 | + HelpExampleCli("backupwallet", "\"backupdata\"") |
2095 | + HelpExampleRpc("backupwallet", "\"backupdata\"") | |
a6099ef3 | 2096 | ); |
e3bc5698 | 2097 | |
4401b2d7 EL |
2098 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2099 | ||
9064d73b S |
2100 | boost::filesystem::path exportdir; |
2101 | try { | |
2102 | exportdir = GetExportDir(); | |
2103 | } catch (const std::runtime_error& e) { | |
2104 | throw JSONRPCError(RPC_INTERNAL_ERROR, e.what()); | |
2105 | } | |
2106 | if (exportdir.empty()) { | |
2107 | throw JSONRPCError(RPC_WALLET_ERROR, "Cannot backup wallet until the -exportdir option has been set"); | |
2108 | } | |
2109 | std::string unclean = params[0].get_str(); | |
2110 | std::string clean = SanitizeFilename(unclean); | |
2111 | if (clean.compare(unclean) != 0) { | |
2112 | throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Filename is invalid as only alphanumeric characters are allowed. Try '%s' instead.", clean)); | |
2113 | } | |
2114 | boost::filesystem::path exportfilepath = exportdir / clean; | |
2115 | ||
2116 | if (!BackupWallet(*pwalletMain, exportfilepath.string())) | |
ad525e9c | 2117 | throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet backup failed!"); |
e3bc5698 | 2118 | |
9064d73b | 2119 | return exportfilepath.string(); |
e3bc5698 JG |
2120 | } |
2121 | ||
2122 | ||
d014114d | 2123 | UniValue keypoolrefill(const UniValue& params, bool fHelp) |
e3bc5698 | 2124 | { |
b9fb692d | 2125 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2126 | return NullUniValue; |
9d365796 | 2127 | |
36bd46f1 | 2128 | if (fHelp || params.size() > 1) |
e3bc5698 | 2129 | throw runtime_error( |
a6099ef3 | 2130 | "keypoolrefill ( newsize )\n" |
2131 | "\nFills the keypool." | |
2132 | + HelpRequiringPassphrase() + "\n" | |
2133 | "\nArguments\n" | |
2134 | "1. newsize (numeric, optional, default=100) The new keypool size\n" | |
2135 | "\nExamples:\n" | |
2136 | + HelpExampleCli("keypoolrefill", "") | |
2137 | + HelpExampleRpc("keypoolrefill", "") | |
2138 | ); | |
e3bc5698 | 2139 | |
4401b2d7 EL |
2140 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2141 | ||
f914c7a1 PK |
2142 | // 0 is interpreted by TopUpKeyPool() as the default keypool size given by -keypool |
2143 | unsigned int kpSize = 0; | |
36bd46f1 JG |
2144 | if (params.size() > 0) { |
2145 | if (params[0].get_int() < 0) | |
f914c7a1 PK |
2146 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected valid size."); |
2147 | kpSize = (unsigned int)params[0].get_int(); | |
36bd46f1 JG |
2148 | } |
2149 | ||
e3bc5698 | 2150 | EnsureWalletIsUnlocked(); |
36bd46f1 | 2151 | pwalletMain->TopUpKeyPool(kpSize); |
e3bc5698 | 2152 | |
36bd46f1 | 2153 | if (pwalletMain->GetKeyPoolSize() < kpSize) |
738835d7 | 2154 | throw JSONRPCError(RPC_WALLET_ERROR, "Error refreshing keypool."); |
e3bc5698 | 2155 | |
ed21d5bd | 2156 | return NullUniValue; |
e3bc5698 JG |
2157 | } |
2158 | ||
2159 | ||
92f2c1fe | 2160 | static void LockWallet(CWallet* pWallet) |
e3bc5698 | 2161 | { |
92f2c1fe GA |
2162 | LOCK(cs_nWalletUnlockTime); |
2163 | nWalletUnlockTime = 0; | |
2164 | pWallet->Lock(); | |
e3bc5698 JG |
2165 | } |
2166 | ||
d014114d | 2167 | UniValue walletpassphrase(const UniValue& params, bool fHelp) |
e3bc5698 | 2168 | { |
b9fb692d | 2169 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2170 | return NullUniValue; |
9d365796 | 2171 | |
e3bc5698 JG |
2172 | if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) |
2173 | throw runtime_error( | |
a6099ef3 | 2174 | "walletpassphrase \"passphrase\" timeout\n" |
2175 | "\nStores the wallet decryption key in memory for 'timeout' seconds.\n" | |
1333d0d7 | 2176 | "This is needed prior to performing transactions related to private keys such as sending " + strprintf("%s",komodo_chainname()) + "\n" |
a6099ef3 | 2177 | "\nArguments:\n" |
2178 | "1. \"passphrase\" (string, required) The wallet passphrase\n" | |
2179 | "2. timeout (numeric, required) The time to keep the decryption key in seconds.\n" | |
6c0db81c WL |
2180 | "\nNote:\n" |
2181 | "Issuing the walletpassphrase command while the wallet is already unlocked will set a new unlock\n" | |
2182 | "time that overrides the old one.\n" | |
a6099ef3 | 2183 | "\nExamples:\n" |
2184 | "\nunlock the wallet for 60 seconds\n" | |
2185 | + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 60") + | |
2186 | "\nLock the wallet again (before 60 seconds)\n" | |
2187 | + HelpExampleCli("walletlock", "") + | |
2188 | "\nAs json rpc call\n" | |
2189 | + HelpExampleRpc("walletpassphrase", "\"my pass phrase\", 60") | |
2190 | ); | |
2191 | ||
4401b2d7 EL |
2192 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2193 | ||
e3bc5698 JG |
2194 | if (fHelp) |
2195 | return true; | |
2196 | if (!pwalletMain->IsCrypted()) | |
738835d7 | 2197 | throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrase was called."); |
e3bc5698 | 2198 | |
e3bc5698 JG |
2199 | // Note that the walletpassphrase is stored in params[0] which is not mlock()ed |
2200 | SecureString strWalletPass; | |
2201 | strWalletPass.reserve(100); | |
2202 | // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) | |
2203 | // Alternately, find a way to make params[0] mlock()'d to begin with. | |
2204 | strWalletPass = params[0].get_str().c_str(); | |
2205 | ||
2206 | if (strWalletPass.length() > 0) | |
2207 | { | |
2208 | if (!pwalletMain->Unlock(strWalletPass)) | |
738835d7 | 2209 | throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); |
e3bc5698 JG |
2210 | } |
2211 | else | |
2212 | throw runtime_error( | |
2213 | "walletpassphrase <passphrase> <timeout>\n" | |
2214 | "Stores the wallet decryption key in memory for <timeout> seconds."); | |
2215 | ||
6e263a5f | 2216 | // No need to check return values, because the wallet was unlocked above |
1a62587e | 2217 | pwalletMain->UpdateNullifierNoteMap(); |
92f2c1fe GA |
2218 | pwalletMain->TopUpKeyPool(); |
2219 | ||
51ed9ec9 | 2220 | int64_t nSleepTime = params[1].get_int64(); |
92f2c1fe GA |
2221 | LOCK(cs_nWalletUnlockTime); |
2222 | nWalletUnlockTime = GetTime() + nSleepTime; | |
2223 | RPCRunLater("lockwallet", boost::bind(LockWallet, pwalletMain), nSleepTime); | |
e3bc5698 | 2224 | |
ed21d5bd | 2225 | return NullUniValue; |
e3bc5698 JG |
2226 | } |
2227 | ||
2228 | ||
d014114d | 2229 | UniValue walletpassphrasechange(const UniValue& params, bool fHelp) |
e3bc5698 | 2230 | { |
b9fb692d | 2231 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2232 | return NullUniValue; |
9d365796 | 2233 | |
e3bc5698 JG |
2234 | if (pwalletMain->IsCrypted() && (fHelp || params.size() != 2)) |
2235 | throw runtime_error( | |
a6099ef3 | 2236 | "walletpassphrasechange \"oldpassphrase\" \"newpassphrase\"\n" |
2237 | "\nChanges the wallet passphrase from 'oldpassphrase' to 'newpassphrase'.\n" | |
2238 | "\nArguments:\n" | |
2239 | "1. \"oldpassphrase\" (string) The current passphrase\n" | |
2240 | "2. \"newpassphrase\" (string) The new passphrase\n" | |
2241 | "\nExamples:\n" | |
2242 | + HelpExampleCli("walletpassphrasechange", "\"old one\" \"new one\"") | |
2243 | + HelpExampleRpc("walletpassphrasechange", "\"old one\", \"new one\"") | |
2244 | ); | |
2245 | ||
4401b2d7 EL |
2246 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2247 | ||
e3bc5698 JG |
2248 | if (fHelp) |
2249 | return true; | |
2250 | if (!pwalletMain->IsCrypted()) | |
738835d7 | 2251 | throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletpassphrasechange was called."); |
e3bc5698 JG |
2252 | |
2253 | // TODO: get rid of these .c_str() calls by implementing SecureString::operator=(std::string) | |
2254 | // Alternately, find a way to make params[0] mlock()'d to begin with. | |
2255 | SecureString strOldWalletPass; | |
2256 | strOldWalletPass.reserve(100); | |
2257 | strOldWalletPass = params[0].get_str().c_str(); | |
2258 | ||
2259 | SecureString strNewWalletPass; | |
2260 | strNewWalletPass.reserve(100); | |
2261 | strNewWalletPass = params[1].get_str().c_str(); | |
2262 | ||
2263 | if (strOldWalletPass.length() < 1 || strNewWalletPass.length() < 1) | |
2264 | throw runtime_error( | |
2265 | "walletpassphrasechange <oldpassphrase> <newpassphrase>\n" | |
2266 | "Changes the wallet passphrase from <oldpassphrase> to <newpassphrase>."); | |
2267 | ||
2268 | if (!pwalletMain->ChangeWalletPassphrase(strOldWalletPass, strNewWalletPass)) | |
738835d7 | 2269 | throw JSONRPCError(RPC_WALLET_PASSPHRASE_INCORRECT, "Error: The wallet passphrase entered was incorrect."); |
e3bc5698 | 2270 | |
ed21d5bd | 2271 | return NullUniValue; |
e3bc5698 JG |
2272 | } |
2273 | ||
2274 | ||
d014114d | 2275 | UniValue walletlock(const UniValue& params, bool fHelp) |
e3bc5698 | 2276 | { |
b9fb692d | 2277 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2278 | return NullUniValue; |
9d365796 | 2279 | |
e3bc5698 JG |
2280 | if (pwalletMain->IsCrypted() && (fHelp || params.size() != 0)) |
2281 | throw runtime_error( | |
2282 | "walletlock\n" | |
a6099ef3 | 2283 | "\nRemoves the wallet encryption key from memory, locking the wallet.\n" |
e3bc5698 | 2284 | "After calling this method, you will need to call walletpassphrase again\n" |
a6099ef3 | 2285 | "before being able to call any methods which require the wallet to be unlocked.\n" |
2286 | "\nExamples:\n" | |
2287 | "\nSet the passphrase for 2 minutes to perform a transaction\n" | |
2288 | + HelpExampleCli("walletpassphrase", "\"my pass phrase\" 120") + | |
2289 | "\nPerform a send (requires passphrase set)\n" | |
1333d0d7 | 2290 | + HelpExampleCli("sendtoaddress", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" 1.0") + |
a6099ef3 | 2291 | "\nClear the passphrase since we are done before 2 minutes is up\n" |
2292 | + HelpExampleCli("walletlock", "") + | |
2293 | "\nAs json rpc call\n" | |
2294 | + HelpExampleRpc("walletlock", "") | |
2295 | ); | |
2296 | ||
4401b2d7 EL |
2297 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2298 | ||
e3bc5698 JG |
2299 | if (fHelp) |
2300 | return true; | |
2301 | if (!pwalletMain->IsCrypted()) | |
738835d7 | 2302 | throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an unencrypted wallet, but walletlock was called."); |
e3bc5698 JG |
2303 | |
2304 | { | |
2305 | LOCK(cs_nWalletUnlockTime); | |
2306 | pwalletMain->Lock(); | |
2307 | nWalletUnlockTime = 0; | |
2308 | } | |
2309 | ||
ed21d5bd | 2310 | return NullUniValue; |
e3bc5698 JG |
2311 | } |
2312 | ||
2313 | ||
d014114d | 2314 | UniValue encryptwallet(const UniValue& params, bool fHelp) |
e3bc5698 | 2315 | { |
b9fb692d | 2316 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2317 | return NullUniValue; |
62c0aa9e | 2318 | |
b8eb3775 | 2319 | auto fEnableWalletEncryption = fExperimentalMode && GetBoolArg("-developerencryptwallet", false); |
62c0aa9e JG |
2320 | |
2321 | std::string strWalletEncryptionDisabledMsg = ""; | |
2322 | if (!fEnableWalletEncryption) { | |
1532cb75 | 2323 | strWalletEncryptionDisabledMsg = "\nWARNING: Wallet encryption is DISABLED. This call always fails.\n"; |
62c0aa9e JG |
2324 | } |
2325 | ||
e3bc5698 JG |
2326 | if (!pwalletMain->IsCrypted() && (fHelp || params.size() != 1)) |
2327 | throw runtime_error( | |
a6099ef3 | 2328 | "encryptwallet \"passphrase\"\n" |
62c0aa9e | 2329 | + strWalletEncryptionDisabledMsg + |
a6099ef3 | 2330 | "\nEncrypts the wallet with 'passphrase'. This is for first time encryption.\n" |
2331 | "After this, any calls that interact with private keys such as sending or signing \n" | |
2332 | "will require the passphrase to be set prior the making these calls.\n" | |
2333 | "Use the walletpassphrase call for this, and then walletlock call.\n" | |
2334 | "If the wallet is already encrypted, use the walletpassphrasechange call.\n" | |
2335 | "Note that this will shutdown the server.\n" | |
2336 | "\nArguments:\n" | |
2337 | "1. \"passphrase\" (string) The pass phrase to encrypt the wallet with. It must be at least 1 character, but should be long.\n" | |
2338 | "\nExamples:\n" | |
2339 | "\nEncrypt you wallet\n" | |
2340 | + HelpExampleCli("encryptwallet", "\"my pass phrase\"") + | |
1333d0d7 | 2341 | "\nNow set the passphrase to use the wallet, such as for signing or sending " + strprintf("%s",komodo_chainname()) + "\n" |
a6099ef3 | 2342 | + HelpExampleCli("walletpassphrase", "\"my pass phrase\"") + |
2343 | "\nNow we can so something like sign\n" | |
1333d0d7 | 2344 | + HelpExampleCli("signmessage", "\"" + strprintf("%s",komodo_chainname()) + "_address\" \"test message\"") + |
a6099ef3 | 2345 | "\nNow lock the wallet again by removing the passphrase\n" |
2346 | + HelpExampleCli("walletlock", "") + | |
2347 | "\nAs a json rpc call\n" | |
2348 | + HelpExampleRpc("encryptwallet", "\"my pass phrase\"") | |
2349 | ); | |
2350 | ||
4401b2d7 EL |
2351 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2352 | ||
e3bc5698 JG |
2353 | if (fHelp) |
2354 | return true; | |
62c0aa9e | 2355 | if (!fEnableWalletEncryption) { |
1532cb75 | 2356 | throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: wallet encryption is disabled."); |
62c0aa9e | 2357 | } |
e3bc5698 | 2358 | if (pwalletMain->IsCrypted()) |
738835d7 | 2359 | throw JSONRPCError(RPC_WALLET_WRONG_ENC_STATE, "Error: running with an encrypted wallet, but encryptwallet was called."); |
e3bc5698 JG |
2360 | |
2361 | // TODO: get rid of this .c_str() by implementing SecureString::operator=(std::string) | |
2362 | // Alternately, find a way to make params[0] mlock()'d to begin with. | |
2363 | SecureString strWalletPass; | |
2364 | strWalletPass.reserve(100); | |
2365 | strWalletPass = params[0].get_str().c_str(); | |
2366 | ||
2367 | if (strWalletPass.length() < 1) | |
2368 | throw runtime_error( | |
2369 | "encryptwallet <passphrase>\n" | |
2370 | "Encrypts the wallet with <passphrase>."); | |
2371 | ||
2372 | if (!pwalletMain->EncryptWallet(strWalletPass)) | |
738835d7 | 2373 | throw JSONRPCError(RPC_WALLET_ENCRYPTION_FAILED, "Error: Failed to encrypt the wallet."); |
e3bc5698 JG |
2374 | |
2375 | // BDB seems to have a bad habit of writing old data into | |
2376 | // slack space in .dat files; that is bad if the old data is | |
331544bc | 2377 | // unencrypted private keys. So: |
e3bc5698 | 2378 | StartShutdown(); |
2e500f50 | 2379 | return "wallet encrypted; Komodo server stopping, restart to run with encrypted wallet. The keypool has been flushed, you need to make a new backup."; |
e3bc5698 JG |
2380 | } |
2381 | ||
d014114d | 2382 | UniValue lockunspent(const UniValue& params, bool fHelp) |
fdbb537d | 2383 | { |
b9fb692d | 2384 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2385 | return NullUniValue; |
9d365796 | 2386 | |
fdbb537d JG |
2387 | if (fHelp || params.size() < 1 || params.size() > 2) |
2388 | throw runtime_error( | |
a6099ef3 | 2389 | "lockunspent unlock [{\"txid\":\"txid\",\"vout\":n},...]\n" |
2390 | "\nUpdates list of temporarily unspendable outputs.\n" | |
90fd8737 | 2391 | "Temporarily lock (unlock=false) or unlock (unlock=true) specified transaction outputs.\n" |
1333d0d7 | 2392 | "A locked transaction output will not be chosen by automatic coin selection, when spending " + strprintf("%s",komodo_chainname()) + ".\n" |
a6099ef3 | 2393 | "Locks are stored in memory only. Nodes start with zero locked outputs, and the locked output list\n" |
2394 | "is always cleared (by virtue of process exit) when a node stops or fails.\n" | |
2395 | "Also see the listunspent call\n" | |
2396 | "\nArguments:\n" | |
2397 | "1. unlock (boolean, required) Whether to unlock (true) or lock (false) the specified transactions\n" | |
2398 | "2. \"transactions\" (string, required) A json array of objects. Each object the txid (string) vout (numeric)\n" | |
2399 | " [ (json array of json objects)\n" | |
2400 | " {\n" | |
2401 | " \"txid\":\"id\", (string) The transaction id\n" | |
2402 | " \"vout\": n (numeric) The output number\n" | |
2403 | " }\n" | |
2404 | " ,...\n" | |
2405 | " ]\n" | |
2406 | ||
2407 | "\nResult:\n" | |
2408 | "true|false (boolean) Whether the command was successful or not\n" | |
2409 | ||
2410 | "\nExamples:\n" | |
2411 | "\nList the unspent transactions\n" | |
2412 | + HelpExampleCli("listunspent", "") + | |
2413 | "\nLock an unspent transaction\n" | |
2414 | + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + | |
2415 | "\nList the locked transactions\n" | |
2416 | + HelpExampleCli("listlockunspent", "") + | |
2417 | "\nUnlock the transaction again\n" | |
2418 | + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + | |
2419 | "\nAs a json rpc call\n" | |
2420 | + HelpExampleRpc("lockunspent", "false, \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") | |
2421 | ); | |
fdbb537d | 2422 | |
4401b2d7 EL |
2423 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2424 | ||
fdbb537d | 2425 | if (params.size() == 1) |
ed21d5bd | 2426 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)); |
fdbb537d | 2427 | else |
ed21d5bd | 2428 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VBOOL)(UniValue::VARR)); |
fdbb537d JG |
2429 | |
2430 | bool fUnlock = params[0].get_bool(); | |
2431 | ||
2432 | if (params.size() == 1) { | |
2433 | if (fUnlock) | |
2434 | pwalletMain->UnlockAllCoins(); | |
2435 | return true; | |
2436 | } | |
2437 | ||
851f58f9 | 2438 | UniValue outputs = params[1].get_array(); |
cc71666a | 2439 | for (size_t idx = 0; idx < outputs.size(); idx++) { |
ed21d5bd JG |
2440 | const UniValue& output = outputs[idx]; |
2441 | if (!output.isObject()) | |
15117692 | 2442 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); |
d014114d | 2443 | const UniValue& o = output.get_obj(); |
fdbb537d | 2444 | |
ed21d5bd | 2445 | RPCTypeCheckObj(o, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)); |
fdbb537d JG |
2446 | |
2447 | string txid = find_value(o, "txid").get_str(); | |
2448 | if (!IsHex(txid)) | |
15117692 | 2449 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid"); |
fdbb537d JG |
2450 | |
2451 | int nOutput = find_value(o, "vout").get_int(); | |
2452 | if (nOutput < 0) | |
15117692 | 2453 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); |
fdbb537d | 2454 | |
34cdc411 | 2455 | COutPoint outpt(uint256S(txid), nOutput); |
fdbb537d JG |
2456 | |
2457 | if (fUnlock) | |
2458 | pwalletMain->UnlockCoin(outpt); | |
2459 | else | |
2460 | pwalletMain->LockCoin(outpt); | |
2461 | } | |
2462 | ||
2463 | return true; | |
2464 | } | |
2465 | ||
d014114d | 2466 | UniValue listlockunspent(const UniValue& params, bool fHelp) |
fdbb537d | 2467 | { |
b9fb692d | 2468 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2469 | return NullUniValue; |
9d365796 | 2470 | |
fdbb537d JG |
2471 | if (fHelp || params.size() > 0) |
2472 | throw runtime_error( | |
2473 | "listlockunspent\n" | |
a6099ef3 | 2474 | "\nReturns list of temporarily unspendable outputs.\n" |
2475 | "See the lockunspent call to lock and unlock transactions for spending.\n" | |
2476 | "\nResult:\n" | |
2477 | "[\n" | |
2478 | " {\n" | |
2479 | " \"txid\" : \"transactionid\", (string) The transaction id locked\n" | |
2480 | " \"vout\" : n (numeric) The vout value\n" | |
2481 | " }\n" | |
2482 | " ,...\n" | |
2483 | "]\n" | |
2484 | "\nExamples:\n" | |
2485 | "\nList the unspent transactions\n" | |
2486 | + HelpExampleCli("listunspent", "") + | |
2487 | "\nLock an unspent transaction\n" | |
2488 | + HelpExampleCli("lockunspent", "false \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + | |
2489 | "\nList the locked transactions\n" | |
2490 | + HelpExampleCli("listlockunspent", "") + | |
2491 | "\nUnlock the transaction again\n" | |
2492 | + HelpExampleCli("lockunspent", "true \"[{\\\"txid\\\":\\\"a08e6907dbbd3d809776dbfc5d82e371b764ed838b5655e72f463568df1aadf0\\\",\\\"vout\\\":1}]\"") + | |
2493 | "\nAs a json rpc call\n" | |
2494 | + HelpExampleRpc("listlockunspent", "") | |
2495 | ); | |
fdbb537d | 2496 | |
4401b2d7 EL |
2497 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2498 | ||
fdbb537d JG |
2499 | vector<COutPoint> vOutpts; |
2500 | pwalletMain->ListLockedCoins(vOutpts); | |
2501 | ||
38fc4b70 | 2502 | UniValue ret(UniValue::VARR); |
fdbb537d JG |
2503 | |
2504 | BOOST_FOREACH(COutPoint &outpt, vOutpts) { | |
38fc4b70 | 2505 | UniValue o(UniValue::VOBJ); |
fdbb537d JG |
2506 | |
2507 | o.push_back(Pair("txid", outpt.hash.GetHex())); | |
2508 | o.push_back(Pair("vout", (int)outpt.n)); | |
2509 | ret.push_back(o); | |
2510 | } | |
2511 | ||
2512 | return ret; | |
2513 | } | |
2514 | ||
d014114d | 2515 | UniValue settxfee(const UniValue& params, bool fHelp) |
a943bde6 | 2516 | { |
b9fb692d | 2517 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2518 | return NullUniValue; |
9d365796 | 2519 | |
a943bde6 WL |
2520 | if (fHelp || params.size() < 1 || params.size() > 1) |
2521 | throw runtime_error( | |
2522 | "settxfee amount\n" | |
6943cb9b | 2523 | "\nSet the transaction fee per kB.\n" |
a943bde6 | 2524 | "\nArguments:\n" |
1333d0d7 | 2525 | "1. amount (numeric, required) The transaction fee in " + strprintf("%s",komodo_chainname()) + "/kB rounded to the nearest 0.00000001\n" |
a943bde6 WL |
2526 | "\nResult\n" |
2527 | "true|false (boolean) Returns true if successful\n" | |
2528 | "\nExamples:\n" | |
2529 | + HelpExampleCli("settxfee", "0.00001") | |
2530 | + HelpExampleRpc("settxfee", "0.00001") | |
2531 | ); | |
2532 | ||
4401b2d7 EL |
2533 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2534 | ||
a943bde6 | 2535 | // Amount |
e76a3849 | 2536 | CAmount nAmount = AmountFromValue(params[0]); |
a943bde6 | 2537 | |
c6cb21d1 | 2538 | payTxFee = CFeeRate(nAmount, 1000); |
a943bde6 WL |
2539 | return true; |
2540 | } | |
2541 | ||
d014114d | 2542 | UniValue getwalletinfo(const UniValue& params, bool fHelp) |
a00ebb51 | 2543 | { |
b9fb692d | 2544 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2545 | return NullUniValue; |
9d365796 | 2546 | |
a00ebb51 DN |
2547 | if (fHelp || params.size() != 0) |
2548 | throw runtime_error( | |
2549 | "getwalletinfo\n" | |
2550 | "Returns an object containing various wallet state info.\n" | |
2551 | "\nResult:\n" | |
2552 | "{\n" | |
2553 | " \"walletversion\": xxxxx, (numeric) the wallet version\n" | |
1333d0d7 | 2554 | " \"balance\": xxxxxxx, (numeric) the total confirmed balance of the wallet in " + strprintf("%s",komodo_chainname()) + "\n" |
2555 | " \"unconfirmed_balance\": xxx, (numeric) the total unconfirmed balance of the wallet in " + strprintf("%s",komodo_chainname()) + "\n" | |
2556 | " \"immature_balance\": xxxxxx, (numeric) the total immature balance of the wallet in " + strprintf("%s",komodo_chainname()) + "\n" | |
a00ebb51 DN |
2557 | " \"txcount\": xxxxxxx, (numeric) the total number of transactions in the wallet\n" |
2558 | " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" | |
2559 | " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" | |
2560 | " \"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" | |
f37f614e | 2561 | " \"paytxfee\": x.xxxx, (numeric) the transaction fee configuration, set in KMD/KB\n" |
a00ebb51 DN |
2562 | "}\n" |
2563 | "\nExamples:\n" | |
2564 | + HelpExampleCli("getwalletinfo", "") | |
2565 | + HelpExampleRpc("getwalletinfo", "") | |
2566 | ); | |
2567 | ||
4401b2d7 EL |
2568 | LOCK2(cs_main, pwalletMain->cs_wallet); |
2569 | ||
38fc4b70 | 2570 | UniValue obj(UniValue::VOBJ); |
a00ebb51 DN |
2571 | obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); |
2572 | obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); | |
8024d67d GM |
2573 | obj.push_back(Pair("unconfirmed_balance", ValueFromAmount(pwalletMain->GetUnconfirmedBalance()))); |
2574 | obj.push_back(Pair("immature_balance", ValueFromAmount(pwalletMain->GetImmatureBalance()))); | |
a00ebb51 | 2575 | obj.push_back(Pair("txcount", (int)pwalletMain->mapWallet.size())); |
d56e30ca | 2576 | obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); |
a00ebb51 DN |
2577 | obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); |
2578 | if (pwalletMain->IsCrypted()) | |
d56e30ca | 2579 | obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); |
6699b425 | 2580 | obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); |
a00ebb51 DN |
2581 | return obj; |
2582 | } | |
0f5954c4 | 2583 | |
d014114d | 2584 | UniValue resendwallettransactions(const UniValue& params, bool fHelp) |
0f5954c4 | 2585 | { |
b9fb692d | 2586 | if (!EnsureWalletIsAvailable(fHelp)) |
9756b7bd | 2587 | return NullUniValue; |
9d365796 | 2588 | |
0f5954c4 GA |
2589 | if (fHelp || params.size() != 0) |
2590 | throw runtime_error( | |
2591 | "resendwallettransactions\n" | |
2592 | "Immediately re-broadcast unconfirmed wallet transactions to all peers.\n" | |
2593 | "Intended only for testing; the wallet code periodically re-broadcasts\n" | |
2594 | "automatically.\n" | |
2595 | "Returns array of transaction ids that were re-broadcast.\n" | |
2596 | ); | |
2597 | ||
2598 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
2599 | ||
2600 | std::vector<uint256> txids = pwalletMain->ResendWalletTransactionsBefore(GetTime()); | |
38fc4b70 | 2601 | UniValue result(UniValue::VARR); |
0f5954c4 GA |
2602 | BOOST_FOREACH(const uint256& txid, txids) |
2603 | { | |
2604 | result.push_back(txid.ToString()); | |
2605 | } | |
2606 | return result; | |
2607 | } | |
0b9dc9c8 | 2608 | |
d014114d | 2609 | UniValue listunspent(const UniValue& params, bool fHelp) |
0b9dc9c8 JS |
2610 | { |
2611 | if (!EnsureWalletIsAvailable(fHelp)) | |
9756b7bd | 2612 | return NullUniValue; |
9d365796 | 2613 | |
0b9dc9c8 | 2614 | if (fHelp || params.size() > 3) |
ea9e82df JS |
2615 | throw runtime_error( |
2616 | "listunspent ( minconf maxconf [\"address\",...] )\n" | |
2617 | "\nReturns array of unspent transaction outputs\n" | |
2618 | "with between minconf and maxconf (inclusive) confirmations.\n" | |
2619 | "Optionally filter to only include txouts paid to specified addresses.\n" | |
2620 | "Results are an array of Objects, each of which has:\n" | |
2621 | "{txid, vout, scriptPubKey, amount, confirmations}\n" | |
2622 | "\nArguments:\n" | |
2623 | "1. minconf (numeric, optional, default=1) The minimum confirmations to filter\n" | |
2624 | "2. maxconf (numeric, optional, default=9999999) The maximum confirmations to filter\n" | |
1333d0d7 | 2625 | "3. \"addresses\" (string) A json array of " + strprintf("%s",komodo_chainname()) + " addresses to filter\n" |
ea9e82df | 2626 | " [\n" |
1333d0d7 | 2627 | " \"address\" (string) " + strprintf("%s",komodo_chainname()) + " address\n" |
ea9e82df JS |
2628 | " ,...\n" |
2629 | " ]\n" | |
2630 | "\nResult\n" | |
2631 | "[ (array of json object)\n" | |
2632 | " {\n" | |
2633 | " \"txid\" : \"txid\", (string) the transaction id \n" | |
2634 | " \"vout\" : n, (numeric) the vout value\n" | |
d77a0ac4 | 2635 | " \"generated\" : true|false (boolean) true if txout is a coinbase transaction output\n" |
1333d0d7 | 2636 | " \"address\" : \"address\", (string) the " + strprintf("%s",komodo_chainname()) + " address\n" |
ea9e82df JS |
2637 | " \"account\" : \"account\", (string) DEPRECATED. The associated account, or \"\" for the default account\n" |
2638 | " \"scriptPubKey\" : \"key\", (string) the script key\n" | |
1333d0d7 | 2639 | " \"amount\" : x.xxx, (numeric) the transaction amount in " + strprintf("%s",komodo_chainname()) + "\n" |
ea9e82df JS |
2640 | " \"confirmations\" : n (numeric) The number of confirmations\n" |
2641 | " }\n" | |
2642 | " ,...\n" | |
2643 | "]\n" | |
2644 | ||
2645 | "\nExamples\n" | |
2646 | + HelpExampleCli("listunspent", "") | |
1333d0d7 | 2647 | + HelpExampleCli("listunspent", "6 9999999 \"[\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\",\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\"]\"") |
2648 | + HelpExampleRpc("listunspent", "6, 9999999 \"[\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\",\\\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\\\"]\"") | |
ea9e82df JS |
2649 | ); |
2650 | ||
9756b7bd | 2651 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)(UniValue::VNUM)(UniValue::VARR)); |
ea9e82df | 2652 | |
0b9dc9c8 JS |
2653 | int nMinDepth = 1; |
2654 | if (params.size() > 0) | |
ea9e82df JS |
2655 | nMinDepth = params[0].get_int(); |
2656 | ||
0b9dc9c8 JS |
2657 | int nMaxDepth = 9999999; |
2658 | if (params.size() > 1) | |
ea9e82df JS |
2659 | nMaxDepth = params[1].get_int(); |
2660 | ||
0b9dc9c8 JS |
2661 | set<CBitcoinAddress> setAddress; |
2662 | if (params.size() > 2) { | |
851f58f9 | 2663 | UniValue inputs = params[2].get_array(); |
cc71666a | 2664 | for (size_t idx = 0; idx < inputs.size(); idx++) { |
d014114d | 2665 | const UniValue& input = inputs[idx]; |
0b9dc9c8 JS |
2666 | CBitcoinAddress address(input.get_str()); |
2667 | if (!address.IsValid()) | |
1333d0d7 | 2668 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid " + strprintf("%s",komodo_chainname()) + " address: ")+input.get_str()); |
0b9dc9c8 | 2669 | if (setAddress.count(address)) |
ea9e82df JS |
2670 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str()); |
2671 | setAddress.insert(address); | |
0b9dc9c8 JS |
2672 | } |
2673 | } | |
ea9e82df | 2674 | |
38fc4b70 | 2675 | UniValue results(UniValue::VARR); |
0b9dc9c8 JS |
2676 | vector<COutput> vecOutputs; |
2677 | assert(pwalletMain != NULL); | |
2678 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
219953ce | 2679 | pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); |
0b9dc9c8 JS |
2680 | BOOST_FOREACH(const COutput& out, vecOutputs) { |
2681 | if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) | |
ea9e82df JS |
2682 | continue; |
2683 | ||
0b9dc9c8 JS |
2684 | if (setAddress.size()) { |
2685 | CTxDestination address; | |
2686 | if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) | |
ea9e82df JS |
2687 | continue; |
2688 | ||
0b9dc9c8 | 2689 | if (!setAddress.count(address)) |
ea9e82df | 2690 | continue; |
0b9dc9c8 | 2691 | } |
ea9e82df | 2692 | |
0b9dc9c8 JS |
2693 | CAmount nValue = out.tx->vout[out.i].nValue; |
2694 | const CScript& pk = out.tx->vout[out.i].scriptPubKey; | |
38fc4b70 | 2695 | UniValue entry(UniValue::VOBJ); |
805344dc | 2696 | entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); |
0b9dc9c8 | 2697 | entry.push_back(Pair("vout", out.i)); |
d77a0ac4 | 2698 | entry.push_back(Pair("generated", out.tx->IsCoinBase())); |
0b9dc9c8 JS |
2699 | CTxDestination address; |
2700 | if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { | |
2701 | entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); | |
2702 | if (pwalletMain->mapAddressBook.count(address)) | |
ea9e82df | 2703 | entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); |
0b9dc9c8 JS |
2704 | } |
2705 | entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); | |
2706 | if (pk.IsPayToScriptHash()) { | |
2707 | CTxDestination address; | |
2708 | if (ExtractDestination(pk, address)) { | |
8b08d953 | 2709 | const CScriptID& hash = boost::get<CScriptID>(address); |
0b9dc9c8 JS |
2710 | CScript redeemScript; |
2711 | if (pwalletMain->GetCScript(hash, redeemScript)) | |
ea9e82df | 2712 | entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end()))); |
0b9dc9c8 JS |
2713 | } |
2714 | } | |
2715 | entry.push_back(Pair("amount",ValueFromAmount(nValue))); | |
fdbf481f | 2716 | if ( out.tx->nLockTime != 0 ) |
e42867d1 | 2717 | { |
fdbf481f | 2718 | BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); |
0fec0cc4 | 2719 | CBlockIndex *tipindex,*pindex = it->second; |
e7876b36 | 2720 | uint64_t interest; uint32_t locktime; int32_t txheight; |
86131275 | 2721 | if ( pindex != 0 && (tipindex= chainActive.LastTip()) != 0 ) |
fdbf481f | 2722 | { |
ae3f3fea | 2723 | interest = komodo_accrued_interest(&txheight,&locktime,out.tx->GetHash(),out.i,0,nValue,(int32_t)tipindex->nHeight); |
2724 | //interest = komodo_interest(txheight,nValue,out.tx->nLockTime,tipindex->nTime); | |
fdbf481f | 2725 | entry.push_back(Pair("interest",ValueFromAmount(interest))); |
e7876b36 | 2726 | } |
86131275 | 2727 | //fprintf(stderr,"nValue %.8f pindex.%p tipindex.%p locktime.%u txheight.%d pindexht.%d\n",(double)nValue/COIN,pindex,chainActive.LastTip(),locktime,txheight,pindex->nHeight); |
e42867d1 | 2728 | } |
0b9dc9c8 JS |
2729 | entry.push_back(Pair("confirmations",out.nDepth)); |
2730 | entry.push_back(Pair("spendable", out.fSpendable)); | |
2731 | results.push_back(entry); | |
2732 | } | |
ea9e82df | 2733 | |
0b9dc9c8 | 2734 | return results; |
c9fd9078 | 2735 | } |
730790f7 | 2736 | |
da16b12e | 2737 | uint64_t komodo_interestsum() |
09e3cf94 | 2738 | { |
6fd79871 | 2739 | #ifdef ENABLE_WALLET |
7714b7f1 | 2740 | if ( GetBoolArg("-disablewallet", false) == 0 ) |
09e3cf94 | 2741 | { |
7714b7f1 | 2742 | uint64_t interest,sum = 0; int32_t txheight; uint32_t locktime; |
2743 | vector<COutput> vecOutputs; | |
2744 | assert(pwalletMain != NULL); | |
2745 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
2746 | pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); | |
2747 | BOOST_FOREACH(const COutput& out,vecOutputs) | |
09e3cf94 | 2748 | { |
7714b7f1 | 2749 | CAmount nValue = out.tx->vout[out.i].nValue; |
2750 | if ( out.tx->nLockTime != 0 && out.fSpendable != 0 ) | |
09e3cf94 | 2751 | { |
7714b7f1 | 2752 | BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); |
2753 | CBlockIndex *tipindex,*pindex = it->second; | |
86131275 | 2754 | if ( pindex != 0 && (tipindex= chainActive.LastTip()) != 0 ) |
7714b7f1 | 2755 | { |
2756 | interest = komodo_accrued_interest(&txheight,&locktime,out.tx->GetHash(),out.i,0,nValue,(int32_t)tipindex->nHeight); | |
2757 | //interest = komodo_interest(pindex->nHeight,nValue,out.tx->nLockTime,tipindex->nTime); | |
2758 | sum += interest; | |
2759 | } | |
09e3cf94 | 2760 | } |
2761 | } | |
7714b7f1 | 2762 | KOMODO_INTERESTSUM = sum; |
2763 | KOMODO_WALLETBALANCE = pwalletMain->GetBalance(); | |
2764 | return(sum); | |
09e3cf94 | 2765 | } |
6fd79871 | 2766 | #endif |
7714b7f1 | 2767 | return(0); |
09e3cf94 | 2768 | } |
2769 | ||
3d8013a0 MC |
2770 | UniValue fundrawtransaction(const UniValue& params, bool fHelp) |
2771 | { | |
2772 | if (!EnsureWalletIsAvailable(fHelp)) | |
2773 | return NullUniValue; | |
2774 | ||
2775 | if (fHelp || params.size() != 1) | |
2776 | throw runtime_error( | |
2777 | "fundrawtransaction \"hexstring\"\n" | |
2778 | "\nAdd inputs to a transaction until it has enough in value to meet its out value.\n" | |
2779 | "This will not modify existing inputs, and will add one change output to the outputs.\n" | |
2780 | "Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n" | |
2781 | "The inputs added will not be signed, use signrawtransaction for that.\n" | |
2782 | "\nArguments:\n" | |
2783 | "1. \"hexstring\" (string, required) The hex string of the raw transaction\n" | |
2784 | "\nResult:\n" | |
2785 | "{\n" | |
2786 | " \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n" | |
2787 | " \"fee\": n, (numeric) The fee added to the transaction\n" | |
2788 | " \"changepos\": n (numeric) The position of the added change output, or -1\n" | |
2789 | "}\n" | |
2790 | "\"hex\" \n" | |
2791 | "\nExamples:\n" | |
2792 | "\nCreate a transaction with no inputs\n" | |
2793 | + HelpExampleCli("createrawtransaction", "\"[]\" \"{\\\"myaddress\\\":0.01}\"") + | |
2794 | "\nAdd sufficient unsigned inputs to meet the output value\n" | |
2795 | + HelpExampleCli("fundrawtransaction", "\"rawtransactionhex\"") + | |
2796 | "\nSign the transaction\n" | |
2797 | + HelpExampleCli("signrawtransaction", "\"fundedtransactionhex\"") + | |
2798 | "\nSend the transaction\n" | |
2799 | + HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"") | |
2800 | ); | |
2801 | ||
2802 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); | |
2803 | ||
2804 | // parse hex string from parameter | |
2805 | CTransaction origTx; | |
2806 | if (!DecodeHexTx(origTx, params[0].get_str())) | |
2807 | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); | |
2808 | ||
2809 | CMutableTransaction tx(origTx); | |
2810 | CAmount nFee; | |
2811 | string strFailReason; | |
2812 | int nChangePos = -1; | |
2813 | if(!pwalletMain->FundTransaction(tx, nFee, nChangePos, strFailReason)) | |
2814 | throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason); | |
2815 | ||
2816 | UniValue result(UniValue::VOBJ); | |
2817 | result.push_back(Pair("hex", EncodeHexTx(tx))); | |
2818 | result.push_back(Pair("changepos", nChangePos)); | |
2819 | result.push_back(Pair("fee", ValueFromAmount(nFee))); | |
2820 | ||
2821 | return result; | |
2822 | } | |
2823 | ||
0d37ae3a | 2824 | UniValue zc_sample_joinsplit(const UniValue& params, bool fHelp) |
1737627c SB |
2825 | { |
2826 | if (fHelp) { | |
2827 | throw runtime_error( | |
2828 | "zcsamplejoinsplit\n" | |
2829 | "\n" | |
2830 | "Perform a joinsplit and return the JSDescription.\n" | |
2831 | ); | |
2832 | } | |
2833 | ||
2834 | LOCK(cs_main); | |
2835 | ||
2836 | uint256 pubKeyHash; | |
2837 | uint256 anchor = ZCIncrementalMerkleTree().root(); | |
2838 | JSDescription samplejoinsplit(*pzcashParams, | |
2839 | pubKeyHash, | |
2840 | anchor, | |
2841 | {JSInput(), JSInput()}, | |
2842 | {JSOutput(), JSOutput()}, | |
2843 | 0, | |
2844 | 0); | |
2845 | ||
2846 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
2847 | ss << samplejoinsplit; | |
2848 | ||
2849 | return HexStr(ss.begin(), ss.end()); | |
2850 | } | |
2851 | ||
0d37ae3a | 2852 | UniValue zc_benchmark(const UniValue& params, bool fHelp) |
6962bb3d TH |
2853 | { |
2854 | if (!EnsureWalletIsAvailable(fHelp)) { | |
0d37ae3a | 2855 | return NullUniValue; |
6962bb3d TH |
2856 | } |
2857 | ||
2858 | if (fHelp || params.size() < 2) { | |
2859 | throw runtime_error( | |
2860 | "zcbenchmark benchmarktype samplecount\n" | |
2861 | "\n" | |
2862 | "Runs a benchmark of the selected type samplecount times,\n" | |
2863 | "returning the running times of each sample.\n" | |
2864 | "\n" | |
2865 | "Output: [\n" | |
2866 | " {\n" | |
2867 | " \"runningtime\": runningtime\n" | |
2868 | " },\n" | |
2869 | " {\n" | |
2870 | " \"runningtime\": runningtime\n" | |
2871 | " }\n" | |
2872 | " ...\n" | |
2873 | "]\n" | |
2874 | ); | |
2875 | } | |
2876 | ||
2877 | LOCK(cs_main); | |
2878 | ||
2879 | std::string benchmarktype = params[0].get_str(); | |
2880 | int samplecount = params[1].get_int(); | |
2881 | ||
2882 | if (samplecount <= 0) { | |
2883 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid samplecount"); | |
2884 | } | |
2885 | ||
2886 | std::vector<double> sample_times; | |
2887 | ||
1737627c | 2888 | JSDescription samplejoinsplit; |
2fbbde59 SB |
2889 | |
2890 | if (benchmarktype == "verifyjoinsplit") { | |
1737627c SB |
2891 | CDataStream ss(ParseHexV(params[2].get_str(), "js"), SER_NETWORK, PROTOCOL_VERSION); |
2892 | ss >> samplejoinsplit; | |
2fbbde59 SB |
2893 | } |
2894 | ||
6962bb3d TH |
2895 | for (int i = 0; i < samplecount; i++) { |
2896 | if (benchmarktype == "sleep") { | |
2897 | sample_times.push_back(benchmark_sleep()); | |
2898 | } else if (benchmarktype == "parameterloading") { | |
2899 | sample_times.push_back(benchmark_parameter_loading()); | |
2900 | } else if (benchmarktype == "createjoinsplit") { | |
4082dcb1 JG |
2901 | if (params.size() < 3) { |
2902 | sample_times.push_back(benchmark_create_joinsplit()); | |
2903 | } else { | |
2904 | int nThreads = params[2].get_int(); | |
2905 | std::vector<double> vals = benchmark_create_joinsplit_threaded(nThreads); | |
2906 | // Divide by nThreads^2 to get average seconds per JoinSplit because | |
2907 | // we are running one JoinSplit per thread. | |
2908 | sample_times.push_back(std::accumulate(vals.begin(), vals.end(), 0.0) / (nThreads*nThreads)); | |
2909 | } | |
6962bb3d | 2910 | } else if (benchmarktype == "verifyjoinsplit") { |
1737627c | 2911 | sample_times.push_back(benchmark_verify_joinsplit(samplejoinsplit)); |
2cc0a252 | 2912 | #ifdef ENABLE_MINING |
bf8def97 | 2913 | } else if (benchmarktype == "solveequihash") { |
f7478de6 | 2914 | if (params.size() < 3) { |
9e52ca32 | 2915 | sample_times.push_back(benchmark_solve_equihash()); |
f7478de6 JG |
2916 | } else { |
2917 | int nThreads = params[2].get_int(); | |
9e52ca32 JG |
2918 | std::vector<double> vals = benchmark_solve_equihash_threaded(nThreads); |
2919 | sample_times.insert(sample_times.end(), vals.begin(), vals.end()); | |
f7478de6 | 2920 | } |
2cc0a252 | 2921 | #endif |
bf8def97 | 2922 | } else if (benchmarktype == "verifyequihash") { |
a1cd1a27 | 2923 | sample_times.push_back(benchmark_verify_equihash()); |
f5edc37f | 2924 | } else if (benchmarktype == "validatelargetx") { |
818b94f9 JG |
2925 | // Number of inputs in the spending transaction that we will simulate |
2926 | int nInputs = 555; | |
2927 | if (params.size() >= 3) { | |
2928 | nInputs = params[2].get_int(); | |
2929 | } | |
2930 | sample_times.push_back(benchmark_large_tx(nInputs)); | |
0fbab55b | 2931 | } else if (benchmarktype == "trydecryptnotes") { |
88b7f3c2 JG |
2932 | int nAddrs = params[2].get_int(); |
2933 | sample_times.push_back(benchmark_try_decrypt_notes(nAddrs)); | |
0bb3d40f JG |
2934 | } else if (benchmarktype == "incnotewitnesses") { |
2935 | int nTxs = params[2].get_int(); | |
2936 | sample_times.push_back(benchmark_increment_note_witnesses(nTxs)); | |
c66c731a JG |
2937 | } else if (benchmarktype == "connectblockslow") { |
2938 | if (Params().NetworkIDString() != "regtest") { | |
2939 | throw JSONRPCError(RPC_TYPE_ERROR, "Benchmark must be run in regtest mode"); | |
2940 | } | |
2941 | sample_times.push_back(benchmark_connectblock_slow()); | |
a76174b7 JG |
2942 | } else if (benchmarktype == "sendtoaddress") { |
2943 | if (Params().NetworkIDString() != "regtest") { | |
2944 | throw JSONRPCError(RPC_TYPE_ERROR, "Benchmark must be run in regtest mode"); | |
2945 | } | |
2946 | auto amount = AmountFromValue(params[2]); | |
2947 | sample_times.push_back(benchmark_sendtoaddress(amount)); | |
2e8aefdc AG |
2948 | } else if (benchmarktype == "loadwallet") { |
2949 | if (Params().NetworkIDString() != "regtest") { | |
2950 | throw JSONRPCError(RPC_TYPE_ERROR, "Benchmark must be run in regtest mode"); | |
2951 | } | |
2952 | sample_times.push_back(benchmark_loadwallet()); | |
99dd50c3 JG |
2953 | } else if (benchmarktype == "listunspent") { |
2954 | sample_times.push_back(benchmark_listunspent()); | |
6962bb3d TH |
2955 | } else { |
2956 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid benchmarktype"); | |
2957 | } | |
2958 | } | |
2959 | ||
0d37ae3a | 2960 | UniValue results(UniValue::VARR); |
9e52ca32 | 2961 | for (auto time : sample_times) { |
0d37ae3a | 2962 | UniValue result(UniValue::VOBJ); |
9e52ca32 | 2963 | result.push_back(Pair("runningtime", time)); |
6962bb3d TH |
2964 | results.push_back(result); |
2965 | } | |
2966 | ||
2967 | return results; | |
2968 | } | |
2969 | ||
0d37ae3a | 2970 | UniValue zc_raw_receive(const UniValue& params, bool fHelp) |
a8ac403d | 2971 | { |
f15b9549 | 2972 | if (!EnsureWalletIsAvailable(fHelp)) { |
0d37ae3a | 2973 | return NullUniValue; |
f15b9549 NW |
2974 | } |
2975 | ||
2976 | if (fHelp || params.size() != 2) { | |
2977 | throw runtime_error( | |
eae37941 | 2978 | "zcrawreceive zcsecretkey encryptednote\n" |
f15b9549 | 2979 | "\n" |
ca0ec80b | 2980 | "DEPRECATED. Decrypts encryptednote and checks if the coin commitments\n" |
f15b9549 NW |
2981 | "are in the blockchain as indicated by the \"exists\" result.\n" |
2982 | "\n" | |
2983 | "Output: {\n" | |
2984 | " \"amount\": value,\n" | |
4bc00dc1 | 2985 | " \"note\": noteplaintext,\n" |
f15b9549 NW |
2986 | " \"exists\": exists\n" |
2987 | "}\n" | |
2988 | ); | |
2989 | } | |
2990 | ||
0d37ae3a | 2991 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VSTR)); |
f15b9549 | 2992 | |
a8ac403d SB |
2993 | LOCK(cs_main); |
2994 | ||
0d6864e4 SB |
2995 | CZCSpendingKey spendingkey(params[0].get_str()); |
2996 | SpendingKey k = spendingkey.Get(); | |
a8ac403d | 2997 | |
6c36a9fe SB |
2998 | uint256 epk; |
2999 | unsigned char nonce; | |
3000 | ZCNoteEncryption::Ciphertext ct; | |
2dc35992 | 3001 | uint256 h_sig; |
6c36a9fe SB |
3002 | |
3003 | { | |
4bc00dc1 | 3004 | CDataStream ssData(ParseHexV(params[1], "encrypted_note"), SER_NETWORK, PROTOCOL_VERSION); |
6c36a9fe SB |
3005 | try { |
3006 | ssData >> nonce; | |
3007 | ssData >> epk; | |
3008 | ssData >> ct; | |
2dc35992 | 3009 | ssData >> h_sig; |
6c36a9fe SB |
3010 | } catch(const std::exception &) { |
3011 | throw runtime_error( | |
4bc00dc1 | 3012 | "encrypted_note could not be decoded" |
6c36a9fe SB |
3013 | ); |
3014 | } | |
3015 | } | |
3016 | ||
642a1caf | 3017 | ZCNoteDecryption decryptor(k.receiving_key()); |
a8ac403d | 3018 | |
2dc35992 SB |
3019 | NotePlaintext npt = NotePlaintext::decrypt( |
3020 | decryptor, | |
3021 | ct, | |
3022 | epk, | |
3023 | h_sig, | |
3024 | nonce | |
3025 | ); | |
3026 | PaymentAddress payment_addr = k.address(); | |
3027 | Note decrypted_note = npt.note(payment_addr); | |
a8ac403d SB |
3028 | |
3029 | assert(pwalletMain != NULL); | |
2dc35992 | 3030 | std::vector<boost::optional<ZCIncrementalWitness>> witnesses; |
a8ac403d | 3031 | uint256 anchor; |
2dc35992 | 3032 | uint256 commitment = decrypted_note.cm(); |
4bc00dc1 | 3033 | pwalletMain->WitnessNoteCommitment( |
2dc35992 SB |
3034 | {commitment}, |
3035 | witnesses, | |
3036 | anchor | |
3037 | ); | |
a8ac403d SB |
3038 | |
3039 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
2dc35992 | 3040 | ss << npt; |
a8ac403d | 3041 | |
0d37ae3a | 3042 | UniValue result(UniValue::VOBJ); |
2dc35992 | 3043 | result.push_back(Pair("amount", ValueFromAmount(decrypted_note.value))); |
4bc00dc1 | 3044 | result.push_back(Pair("note", HexStr(ss.begin(), ss.end()))); |
2dc35992 | 3045 | result.push_back(Pair("exists", (bool) witnesses[0])); |
a8ac403d SB |
3046 | return result; |
3047 | } | |
3048 | ||
2dc35992 SB |
3049 | |
3050 | ||
0d37ae3a | 3051 | UniValue zc_raw_joinsplit(const UniValue& params, bool fHelp) |
730790f7 | 3052 | { |
f15b9549 | 3053 | if (!EnsureWalletIsAvailable(fHelp)) { |
0d37ae3a | 3054 | return NullUniValue; |
f15b9549 NW |
3055 | } |
3056 | ||
3057 | if (fHelp || params.size() != 5) { | |
3058 | throw runtime_error( | |
eae37941 | 3059 | "zcrawjoinsplit rawtx inputs outputs vpub_old vpub_new\n" |
4bc00dc1 | 3060 | " inputs: a JSON object mapping {note: zcsecretkey, ...}\n" |
f15b9549 NW |
3061 | " outputs: a JSON object mapping {zcaddr: value, ...}\n" |
3062 | "\n" | |
ca0ec80b | 3063 | "DEPRECATED. Splices a joinsplit into rawtx. Inputs are unilaterally confidential.\n" |
f15b9549 NW |
3064 | "Outputs are confidential between sender/receiver. The vpub_old and\n" |
3065 | "vpub_new values are globally public and move transparent value into\n" | |
3066 | "or out of the confidential value store, respectively.\n" | |
3067 | "\n" | |
3068 | "Note: The caller is responsible for delivering the output enc1 and\n" | |
3069 | "enc2 to the appropriate recipients, as well as signing rawtxout and\n" | |
3070 | "ensuring it is mined. (A future RPC call will deliver the confidential\n" | |
3071 | "payments in-band on the blockchain.)\n" | |
3072 | "\n" | |
3073 | "Output: {\n" | |
4bc00dc1 DH |
3074 | " \"encryptednote1\": enc1,\n" |
3075 | " \"encryptednote2\": enc2,\n" | |
f15b9549 NW |
3076 | " \"rawtxn\": rawtxout\n" |
3077 | "}\n" | |
3078 | ); | |
3079 | } | |
3080 | ||
a8ac403d | 3081 | LOCK(cs_main); |
730790f7 SB |
3082 | |
3083 | CTransaction tx; | |
3084 | if (!DecodeHexTx(tx, params[0].get_str())) | |
3085 | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); | |
3086 | ||
0d37ae3a JG |
3087 | UniValue inputs = params[1].get_obj(); |
3088 | UniValue outputs = params[2].get_obj(); | |
730790f7 SB |
3089 | |
3090 | CAmount vpub_old(0); | |
3091 | CAmount vpub_new(0); | |
3092 | ||
3093 | if (params[3].get_real() != 0.0) | |
3094 | vpub_old = AmountFromValue(params[3]); | |
3095 | ||
3096 | if (params[4].get_real() != 0.0) | |
3097 | vpub_new = AmountFromValue(params[4]); | |
3098 | ||
b7e4abd6 SB |
3099 | std::vector<JSInput> vjsin; |
3100 | std::vector<JSOutput> vjsout; | |
2dc35992 SB |
3101 | std::vector<Note> notes; |
3102 | std::vector<SpendingKey> keys; | |
3103 | std::vector<uint256> commitments; | |
a8ac403d | 3104 | |
0d37ae3a JG |
3105 | for (const string& name_ : inputs.getKeys()) { |
3106 | CZCSpendingKey spendingkey(inputs[name_].get_str()); | |
0d6864e4 | 3107 | SpendingKey k = spendingkey.Get(); |
a8ac403d | 3108 | |
2dc35992 | 3109 | keys.push_back(k); |
a8ac403d | 3110 | |
2dc35992 | 3111 | NotePlaintext npt; |
a8ac403d | 3112 | |
2dc35992 | 3113 | { |
0d37ae3a | 3114 | CDataStream ssData(ParseHexV(name_, "note"), SER_NETWORK, PROTOCOL_VERSION); |
2dc35992 | 3115 | ssData >> npt; |
a8ac403d SB |
3116 | } |
3117 | ||
2dc35992 SB |
3118 | PaymentAddress addr = k.address(); |
3119 | Note note = npt.note(addr); | |
3120 | notes.push_back(note); | |
3121 | commitments.push_back(note.cm()); | |
3122 | } | |
3123 | ||
3124 | uint256 anchor; | |
3125 | std::vector<boost::optional<ZCIncrementalWitness>> witnesses; | |
4bc00dc1 | 3126 | pwalletMain->WitnessNoteCommitment(commitments, witnesses, anchor); |
2dc35992 SB |
3127 | |
3128 | assert(witnesses.size() == notes.size()); | |
3129 | assert(notes.size() == keys.size()); | |
3130 | ||
3131 | { | |
3132 | for (size_t i = 0; i < witnesses.size(); i++) { | |
3133 | if (!witnesses[i]) { | |
3134 | throw runtime_error( | |
b7e4abd6 | 3135 | "joinsplit input could not be found in tree" |
2dc35992 SB |
3136 | ); |
3137 | } | |
3138 | ||
b7e4abd6 | 3139 | vjsin.push_back(JSInput(*witnesses[i], notes[i], keys[i])); |
2dc35992 | 3140 | } |
730790f7 | 3141 | } |
730790f7 | 3142 | |
b7e4abd6 SB |
3143 | while (vjsin.size() < ZC_NUM_JS_INPUTS) { |
3144 | vjsin.push_back(JSInput()); | |
a8ac403d | 3145 | } |
730790f7 | 3146 | |
0d37ae3a JG |
3147 | for (const string& name_ : outputs.getKeys()) { |
3148 | CZCPaymentAddress pubaddr(name_); | |
e104fcdd | 3149 | PaymentAddress addrTo = pubaddr.Get(); |
0d37ae3a | 3150 | CAmount nAmount = AmountFromValue(outputs[name_]); |
730790f7 | 3151 | |
b7e4abd6 | 3152 | vjsout.push_back(JSOutput(addrTo, nAmount)); |
730790f7 SB |
3153 | } |
3154 | ||
b7e4abd6 SB |
3155 | while (vjsout.size() < ZC_NUM_JS_OUTPUTS) { |
3156 | vjsout.push_back(JSOutput()); | |
730790f7 SB |
3157 | } |
3158 | ||
3159 | // TODO | |
b7e4abd6 SB |
3160 | if (vjsout.size() != ZC_NUM_JS_INPUTS || vjsin.size() != ZC_NUM_JS_OUTPUTS) { |
3161 | throw runtime_error("unsupported joinsplit input/output counts"); | |
730790f7 SB |
3162 | } |
3163 | ||
320f2cc7 SB |
3164 | uint256 joinSplitPubKey; |
3165 | unsigned char joinSplitPrivKey[crypto_sign_SECRETKEYBYTES]; | |
3166 | crypto_sign_keypair(joinSplitPubKey.begin(), joinSplitPrivKey); | |
6aae9d1a TH |
3167 | |
3168 | CMutableTransaction mtx(tx); | |
3169 | mtx.nVersion = 2; | |
3170 | mtx.joinSplitPubKey = joinSplitPubKey; | |
3171 | ||
22de1602 SB |
3172 | JSDescription jsdesc(*pzcashParams, |
3173 | joinSplitPubKey, | |
3174 | anchor, | |
3175 | {vjsin[0], vjsin[1]}, | |
3176 | {vjsout[0], vjsout[1]}, | |
3177 | vpub_old, | |
3178 | vpub_new); | |
320f2cc7 | 3179 | |
bc59f537 SB |
3180 | { |
3181 | auto verifier = libzcash::ProofVerifier::Strict(); | |
3182 | assert(jsdesc.Verify(*pzcashParams, verifier, joinSplitPubKey)); | |
3183 | } | |
730790f7 | 3184 | |
22de1602 | 3185 | mtx.vjoinsplit.push_back(jsdesc); |
730790f7 | 3186 | |
6aae9d1a TH |
3187 | // Empty output script. |
3188 | CScript scriptCode; | |
3189 | CTransaction signTx(mtx); | |
be126699 JG |
3190 | auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); |
3191 | uint256 dataToBeSigned = SignatureHash(scriptCode, signTx, NOT_AN_INPUT, SIGHASH_ALL, 0, consensusBranchId); | |
6aae9d1a TH |
3192 | |
3193 | // Add the signature | |
320f2cc7 SB |
3194 | assert(crypto_sign_detached(&mtx.joinSplitSig[0], NULL, |
3195 | dataToBeSigned.begin(), 32, | |
3196 | joinSplitPrivKey | |
3197 | ) == 0); | |
3198 | ||
3199 | // Sanity check | |
3200 | assert(crypto_sign_verify_detached(&mtx.joinSplitSig[0], | |
3201 | dataToBeSigned.begin(), 32, | |
3202 | mtx.joinSplitPubKey.begin() | |
3203 | ) == 0); | |
6aae9d1a | 3204 | |
730790f7 SB |
3205 | CTransaction rawTx(mtx); |
3206 | ||
3207 | CDataStream ss(SER_NETWORK, PROTOCOL_VERSION); | |
3208 | ss << rawTx; | |
3209 | ||
4bc00dc1 DH |
3210 | std::string encryptedNote1; |
3211 | std::string encryptedNote2; | |
6c36a9fe SB |
3212 | { |
3213 | CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); | |
3214 | ss2 << ((unsigned char) 0x00); | |
22de1602 SB |
3215 | ss2 << jsdesc.ephemeralKey; |
3216 | ss2 << jsdesc.ciphertexts[0]; | |
3217 | ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey); | |
6c36a9fe | 3218 | |
4bc00dc1 | 3219 | encryptedNote1 = HexStr(ss2.begin(), ss2.end()); |
6c36a9fe SB |
3220 | } |
3221 | { | |
3222 | CDataStream ss2(SER_NETWORK, PROTOCOL_VERSION); | |
3223 | ss2 << ((unsigned char) 0x01); | |
22de1602 SB |
3224 | ss2 << jsdesc.ephemeralKey; |
3225 | ss2 << jsdesc.ciphertexts[1]; | |
3226 | ss2 << jsdesc.h_sig(*pzcashParams, joinSplitPubKey); | |
6c36a9fe | 3227 | |
4bc00dc1 | 3228 | encryptedNote2 = HexStr(ss2.begin(), ss2.end()); |
6c36a9fe SB |
3229 | } |
3230 | ||
0d37ae3a | 3231 | UniValue result(UniValue::VOBJ); |
4bc00dc1 DH |
3232 | result.push_back(Pair("encryptednote1", encryptedNote1)); |
3233 | result.push_back(Pair("encryptednote2", encryptedNote2)); | |
730790f7 SB |
3234 | result.push_back(Pair("rawtxn", HexStr(ss.begin(), ss.end()))); |
3235 | return result; | |
3236 | } | |
3237 | ||
0d37ae3a | 3238 | UniValue zc_raw_keygen(const UniValue& params, bool fHelp) |
730790f7 | 3239 | { |
f15b9549 | 3240 | if (!EnsureWalletIsAvailable(fHelp)) { |
0d37ae3a | 3241 | return NullUniValue; |
f15b9549 NW |
3242 | } |
3243 | ||
3244 | if (fHelp || params.size() != 0) { | |
3245 | throw runtime_error( | |
eae37941 | 3246 | "zcrawkeygen\n" |
f15b9549 | 3247 | "\n" |
ca0ec80b | 3248 | "DEPRECATED. Generate a zcaddr which can send and receive confidential values.\n" |
f15b9549 NW |
3249 | "\n" |
3250 | "Output: {\n" | |
3251 | " \"zcaddress\": zcaddr,\n" | |
3252 | " \"zcsecretkey\": zcsecretkey,\n" | |
7b8d4f87 | 3253 | " \"zcviewingkey\": zcviewingkey,\n" |
f15b9549 NW |
3254 | "}\n" |
3255 | ); | |
3256 | } | |
3257 | ||
2dc35992 SB |
3258 | auto k = SpendingKey::random(); |
3259 | auto addr = k.address(); | |
3260 | auto viewing_key = k.viewing_key(); | |
730790f7 | 3261 | |
e104fcdd | 3262 | CZCPaymentAddress pubaddr(addr); |
0d6864e4 | 3263 | CZCSpendingKey spendingkey(k); |
7b8d4f87 | 3264 | CZCViewingKey viewingkey(viewing_key); |
730790f7 | 3265 | |
0d37ae3a | 3266 | UniValue result(UniValue::VOBJ); |
e104fcdd | 3267 | result.push_back(Pair("zcaddress", pubaddr.ToString())); |
0d6864e4 | 3268 | result.push_back(Pair("zcsecretkey", spendingkey.ToString())); |
7b8d4f87 | 3269 | result.push_back(Pair("zcviewingkey", viewingkey.ToString())); |
730790f7 | 3270 | return result; |
f15b9549 | 3271 | } |
c1c45943 S |
3272 | |
3273 | ||
0d37ae3a | 3274 | UniValue z_getnewaddress(const UniValue& params, bool fHelp) |
c1c45943 S |
3275 | { |
3276 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3277 | return NullUniValue; |
c1c45943 | 3278 | |
49e591eb | 3279 | if (fHelp || params.size() > 0) |
c1c45943 S |
3280 | throw runtime_error( |
3281 | "z_getnewaddress\n" | |
3282 | "\nReturns a new zaddr for receiving payments.\n" | |
3283 | "\nArguments:\n" | |
3284 | "\nResult:\n" | |
1333d0d7 | 3285 | "\"" + strprintf("%s",komodo_chainname()) + "_address\" (string) The new zaddr\n" |
c1c45943 S |
3286 | "\nExamples:\n" |
3287 | + HelpExampleCli("z_getnewaddress", "") | |
3288 | + HelpExampleRpc("z_getnewaddress", "") | |
3289 | ); | |
3290 | ||
3291 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3292 | ||
73699cea S |
3293 | EnsureWalletIsUnlocked(); |
3294 | ||
c1c45943 S |
3295 | CZCPaymentAddress pubaddr = pwalletMain->GenerateNewZKey(); |
3296 | std::string result = pubaddr.ToString(); | |
3297 | return result; | |
3298 | } | |
3299 | ||
e709997f | 3300 | |
0d37ae3a | 3301 | UniValue z_listaddresses(const UniValue& params, bool fHelp) |
e709997f S |
3302 | { |
3303 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3304 | return NullUniValue; |
e709997f S |
3305 | |
3306 | if (fHelp || params.size() > 1) | |
3307 | throw runtime_error( | |
44e37656 | 3308 | "z_listaddresses ( includeWatchonly )\n" |
e709997f S |
3309 | "\nReturns the list of zaddr belonging to the wallet.\n" |
3310 | "\nArguments:\n" | |
44e37656 | 3311 | "1. includeWatchonly (bool, optional, default=false) Also include watchonly addresses (see 'z_importviewingkey')\n" |
e709997f S |
3312 | "\nResult:\n" |
3313 | "[ (json array of string)\n" | |
3314 | " \"zaddr\" (string) a zaddr belonging to the wallet\n" | |
3315 | " ,...\n" | |
3316 | "]\n" | |
3317 | "\nExamples:\n" | |
3318 | + HelpExampleCli("z_listaddresses", "") | |
3319 | + HelpExampleRpc("z_listaddresses", "") | |
3320 | ); | |
3321 | ||
3322 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3323 | ||
44e37656 JG |
3324 | bool fIncludeWatchonly = false; |
3325 | if (params.size() > 0) { | |
3326 | fIncludeWatchonly = params[0].get_bool(); | |
3327 | } | |
3328 | ||
0d37ae3a | 3329 | UniValue ret(UniValue::VARR); |
e709997f S |
3330 | std::set<libzcash::PaymentAddress> addresses; |
3331 | pwalletMain->GetPaymentAddresses(addresses); | |
3332 | for (auto addr : addresses ) { | |
44e37656 JG |
3333 | if (fIncludeWatchonly || pwalletMain->HaveSpendingKey(addr)) { |
3334 | ret.push_back(CZCPaymentAddress(addr).ToString()); | |
3335 | } | |
e709997f S |
3336 | } |
3337 | return ret; | |
3338 | } | |
3339 | ||
44e37656 | 3340 | CAmount getBalanceTaddr(std::string transparentAddress, int minDepth=1, bool ignoreUnspendable=true) { |
a0a3334c S |
3341 | set<CBitcoinAddress> setAddress; |
3342 | vector<COutput> vecOutputs; | |
9d365796 | 3343 | CAmount balance = 0; |
3344 | ||
a0a3334c S |
3345 | if (transparentAddress.length() > 0) { |
3346 | CBitcoinAddress taddr = CBitcoinAddress(transparentAddress); | |
3347 | if (!taddr.IsValid()) { | |
3348 | throw std::runtime_error("invalid transparent address"); | |
3349 | } | |
3350 | setAddress.insert(taddr); | |
3351 | } | |
9d365796 | 3352 | |
a0a3334c S |
3353 | LOCK2(cs_main, pwalletMain->cs_wallet); |
3354 | ||
3355 | pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); | |
3356 | ||
3357 | BOOST_FOREACH(const COutput& out, vecOutputs) { | |
3358 | if (out.nDepth < minDepth) { | |
3359 | continue; | |
3360 | } | |
3361 | ||
44e37656 JG |
3362 | if (ignoreUnspendable && !out.fSpendable) { |
3363 | continue; | |
3364 | } | |
3365 | ||
a0a3334c S |
3366 | if (setAddress.size()) { |
3367 | CTxDestination address; | |
3368 | if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { | |
3369 | continue; | |
3370 | } | |
3371 | ||
3372 | if (!setAddress.count(address)) { | |
3373 | continue; | |
3374 | } | |
3375 | } | |
9d365796 | 3376 | |
47658758 | 3377 | CAmount nValue = out.tx->vout[out.i].nValue; // komodo_interest |
a0a3334c S |
3378 | balance += nValue; |
3379 | } | |
3380 | return balance; | |
3381 | } | |
3382 | ||
44e37656 | 3383 | CAmount getBalanceZaddr(std::string address, int minDepth = 1, bool ignoreUnspendable=true) { |
a0a3334c | 3384 | CAmount balance = 0; |
a9743bc8 | 3385 | std::vector<CNotePlaintextEntry> entries; |
a0a3334c | 3386 | LOCK2(cs_main, pwalletMain->cs_wallet); |
44e37656 | 3387 | pwalletMain->GetFilteredNotes(entries, address, minDepth, true, ignoreUnspendable); |
a9743bc8 S |
3388 | for (auto & entry : entries) { |
3389 | balance += CAmount(entry.plaintext.value); | |
a0a3334c | 3390 | } |
a0a3334c S |
3391 | return balance; |
3392 | } | |
3393 | ||
3394 | ||
0d37ae3a | 3395 | UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp) |
6c41028f S |
3396 | { |
3397 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3398 | return NullUniValue; |
6c41028f S |
3399 | |
3400 | if (fHelp || params.size()==0 || params.size() >2) | |
3401 | throw runtime_error( | |
3402 | "z_listreceivedbyaddress \"address\" ( minconf )\n" | |
3403 | "\nReturn a list of amounts received by a zaddr belonging to the node’s wallet.\n" | |
3404 | "\nArguments:\n" | |
3405 | "1. \"address\" (string) The private address.\n" | |
3406 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" | |
3407 | "\nResult:\n" | |
3408 | "{\n" | |
3409 | " \"txid\": xxxxx, (string) the transaction id\n" | |
3410 | " \"amount\": xxxxx, (numeric) the amount of value in the note\n" | |
3411 | " \"memo\": xxxxx, (string) hexademical string representation of memo field\n" | |
3412 | "}\n" | |
337a99a2 JG |
3413 | "\nExamples:\n" |
3414 | + HelpExampleCli("z_listreceivedbyaddress", "\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") | |
3415 | + HelpExampleRpc("z_listreceivedbyaddress", "\"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") | |
6c41028f S |
3416 | ); |
3417 | ||
3418 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3419 | ||
3420 | int nMinDepth = 1; | |
3421 | if (params.size() > 1) { | |
3422 | nMinDepth = params[1].get_int(); | |
3423 | } | |
3424 | if (nMinDepth < 0) { | |
3425 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); | |
3426 | } | |
9d365796 | 3427 | |
6c41028f S |
3428 | // Check that the from address is valid. |
3429 | auto fromaddress = params[0].get_str(); | |
3430 | ||
3431 | libzcash::PaymentAddress zaddr; | |
3432 | CZCPaymentAddress address(fromaddress); | |
3433 | try { | |
3434 | zaddr = address.Get(); | |
fce72608 | 3435 | } catch (const std::runtime_error&) { |
6c41028f S |
3436 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid zaddr."); |
3437 | } | |
3438 | ||
44e37656 JG |
3439 | if (!(pwalletMain->HaveSpendingKey(zaddr) || pwalletMain->HaveViewingKey(zaddr))) { |
3440 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found."); | |
6c41028f | 3441 | } |
9d365796 | 3442 | |
3443 | ||
0d37ae3a | 3444 | UniValue result(UniValue::VARR); |
6c41028f | 3445 | std::vector<CNotePlaintextEntry> entries; |
44e37656 | 3446 | pwalletMain->GetFilteredNotes(entries, fromaddress, nMinDepth, false, false); |
6c41028f | 3447 | for (CNotePlaintextEntry & entry : entries) { |
0d37ae3a | 3448 | UniValue obj(UniValue::VOBJ); |
6c41028f S |
3449 | obj.push_back(Pair("txid",entry.jsop.hash.ToString())); |
3450 | obj.push_back(Pair("amount", ValueFromAmount(CAmount(entry.plaintext.value)))); | |
3451 | std::string data(entry.plaintext.memo.begin(), entry.plaintext.memo.end()); | |
3452 | obj.push_back(Pair("memo", HexStr(data))); | |
3453 | result.push_back(obj); | |
3454 | } | |
3455 | return result; | |
3456 | } | |
3457 | ||
3458 | ||
0d37ae3a | 3459 | UniValue z_getbalance(const UniValue& params, bool fHelp) |
a0a3334c S |
3460 | { |
3461 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3462 | return NullUniValue; |
a0a3334c S |
3463 | |
3464 | if (fHelp || params.size()==0 || params.size() >2) | |
3465 | throw runtime_error( | |
3466 | "z_getbalance \"address\" ( minconf )\n" | |
3467 | "\nReturns the balance of a taddr or zaddr belonging to the node’s wallet.\n" | |
2bbfe6c4 JG |
3468 | "\nCAUTION: If address is a watch-only zaddr, the returned balance may be larger than the actual balance," |
3469 | "\nbecause spends cannot be detected with incoming viewing keys.\n" | |
a0a3334c S |
3470 | "\nArguments:\n" |
3471 | "1. \"address\" (string) The selected address. It may be a transparent or private address.\n" | |
3472 | "2. minconf (numeric, optional, default=1) Only include transactions confirmed at least this many times.\n" | |
3473 | "\nResult:\n" | |
f37f614e | 3474 | "amount (numeric) The total amount in KMD received for this address.\n" |
a0a3334c S |
3475 | "\nExamples:\n" |
3476 | "\nThe total amount received by address \"myaddress\"\n" | |
3477 | + HelpExampleCli("z_getbalance", "\"myaddress\"") + | |
3478 | "\nThe total amount received by address \"myaddress\" at least 5 blocks confirmed\n" | |
3479 | + HelpExampleCli("z_getbalance", "\"myaddress\" 5") + | |
3480 | "\nAs a json rpc call\n" | |
3481 | + HelpExampleRpc("z_getbalance", "\"myaddress\", 5") | |
3482 | ); | |
3483 | ||
3484 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3485 | ||
3486 | int nMinDepth = 1; | |
3487 | if (params.size() > 1) { | |
3488 | nMinDepth = params[1].get_int(); | |
3489 | } | |
12448b64 S |
3490 | if (nMinDepth < 0) { |
3491 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); | |
3492 | } | |
9d365796 | 3493 | |
a0a3334c S |
3494 | // Check that the from address is valid. |
3495 | auto fromaddress = params[0].get_str(); | |
3496 | bool fromTaddr = false; | |
3497 | CBitcoinAddress taddr(fromaddress); | |
3498 | fromTaddr = taddr.IsValid(); | |
3499 | libzcash::PaymentAddress zaddr; | |
3500 | if (!fromTaddr) { | |
3501 | CZCPaymentAddress address(fromaddress); | |
3502 | try { | |
3503 | zaddr = address.Get(); | |
fce72608 | 3504 | } catch (const std::runtime_error&) { |
a0a3334c S |
3505 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); |
3506 | } | |
44e37656 JG |
3507 | if (!(pwalletMain->HaveSpendingKey(zaddr) || pwalletMain->HaveViewingKey(zaddr))) { |
3508 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key or viewing key not found."); | |
12448b64 | 3509 | } |
a0a3334c S |
3510 | } |
3511 | ||
3512 | CAmount nBalance = 0; | |
3513 | if (fromTaddr) { | |
44e37656 | 3514 | nBalance = getBalanceTaddr(fromaddress, nMinDepth, false); |
a0a3334c | 3515 | } else { |
44e37656 | 3516 | nBalance = getBalanceZaddr(fromaddress, nMinDepth, false); |
a0a3334c S |
3517 | } |
3518 | ||
3519 | return ValueFromAmount(nBalance); | |
3520 | } | |
3521 | ||
3522 | ||
0d37ae3a | 3523 | UniValue z_gettotalbalance(const UniValue& params, bool fHelp) |
a0a3334c S |
3524 | { |
3525 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3526 | return NullUniValue; |
a0a3334c | 3527 | |
44e37656 | 3528 | if (fHelp || params.size() > 2) |
a0a3334c | 3529 | throw runtime_error( |
44e37656 | 3530 | "z_gettotalbalance ( minconf includeWatchonly )\n" |
a0a3334c | 3531 | "\nReturn the total value of funds stored in the node’s wallet.\n" |
2bbfe6c4 JG |
3532 | "\nCAUTION: If the wallet contains watch-only zaddrs, the returned private balance may be larger than the actual balance," |
3533 | "\nbecause spends cannot be detected with incoming viewing keys.\n" | |
a0a3334c S |
3534 | "\nArguments:\n" |
3535 | "1. minconf (numeric, optional, default=1) Only include private and transparent transactions confirmed at least this many times.\n" | |
44e37656 | 3536 | "2. includeWatchonly (bool, optional, default=false) Also include balance in watchonly addresses (see 'importaddress' and 'z_importviewingkey')\n" |
a0a3334c S |
3537 | "\nResult:\n" |
3538 | "{\n" | |
3539 | " \"transparent\": xxxxx, (numeric) the total balance of transparent funds\n" | |
3540 | " \"private\": xxxxx, (numeric) the total balance of private funds\n" | |
3541 | " \"total\": xxxxx, (numeric) the total balance of both transparent and private funds\n" | |
3542 | "}\n" | |
3543 | "\nExamples:\n" | |
3544 | "\nThe total amount in the wallet\n" | |
3545 | + HelpExampleCli("z_gettotalbalance", "") + | |
3546 | "\nThe total amount in the wallet at least 5 blocks confirmed\n" | |
3547 | + HelpExampleCli("z_gettotalbalance", "5") + | |
3548 | "\nAs a json rpc call\n" | |
3549 | + HelpExampleRpc("z_gettotalbalance", "5") | |
3550 | ); | |
3551 | ||
3552 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3553 | ||
3554 | int nMinDepth = 1; | |
44e37656 | 3555 | if (params.size() > 0) { |
a0a3334c S |
3556 | nMinDepth = params[0].get_int(); |
3557 | } | |
12448b64 S |
3558 | if (nMinDepth < 0) { |
3559 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); | |
3560 | } | |
a0a3334c | 3561 | |
44e37656 JG |
3562 | bool fIncludeWatchonly = false; |
3563 | if (params.size() > 1) { | |
3564 | fIncludeWatchonly = params[1].get_bool(); | |
3565 | } | |
3566 | ||
a0a3334c | 3567 | // getbalance and "getbalance * 1 true" should return the same number |
9d365796 | 3568 | // but they don't because wtx.GetAmounts() does not handle tx where there are no outputs |
a0a3334c S |
3569 | // pwalletMain->GetBalance() does not accept min depth parameter |
3570 | // so we use our own method to get balance of utxos. | |
44e37656 JG |
3571 | CAmount nBalance = getBalanceTaddr("", nMinDepth, !fIncludeWatchonly); |
3572 | CAmount nPrivateBalance = getBalanceZaddr("", nMinDepth, !fIncludeWatchonly); | |
da16b12e | 3573 | uint64_t interest = komodo_interestsum(); |
a0a3334c | 3574 | CAmount nTotalBalance = nBalance + nPrivateBalance; |
0d37ae3a | 3575 | UniValue result(UniValue::VOBJ); |
f54db399 | 3576 | result.push_back(Pair("transparent", FormatMoney(nBalance))); |
945f015d | 3577 | result.push_back(Pair("interest", FormatMoney(interest))); |
f54db399 JG |
3578 | result.push_back(Pair("private", FormatMoney(nPrivateBalance))); |
3579 | result.push_back(Pair("total", FormatMoney(nTotalBalance))); | |
a0a3334c S |
3580 | return result; |
3581 | } | |
3582 | ||
0d37ae3a | 3583 | UniValue z_getoperationresult(const UniValue& params, bool fHelp) |
c1eae280 S |
3584 | { |
3585 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3586 | return NullUniValue; |
c1eae280 S |
3587 | |
3588 | if (fHelp || params.size() > 1) | |
3589 | throw runtime_error( | |
3590 | "z_getoperationresult ([\"operationid\", ... ]) \n" | |
3591 | "\nRetrieve the result and status of an operation which has finished, and then remove the operation from memory." | |
3592 | + HelpRequiringPassphrase() + "\n" | |
3593 | "\nArguments:\n" | |
3594 | "1. \"operationid\" (array, optional) A list of operation ids we are interested in. If not provided, examine all operations known to the node.\n" | |
3595 | "\nResult:\n" | |
3596 | "\" [object, ...]\" (array) A list of JSON objects\n" | |
337a99a2 JG |
3597 | "\nExamples:\n" |
3598 | + HelpExampleCli("z_getoperationresult", "'[\"operationid\", ... ]'") | |
3599 | + HelpExampleRpc("z_getoperationresult", "'[\"operationid\", ... ]'") | |
c1eae280 | 3600 | ); |
9d365796 | 3601 | |
c1eae280 S |
3602 | // This call will remove finished operations |
3603 | return z_getoperationstatus_IMPL(params, true); | |
3604 | } | |
fc72c078 | 3605 | |
0d37ae3a | 3606 | UniValue z_getoperationstatus(const UniValue& params, bool fHelp) |
fc72c078 S |
3607 | { |
3608 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3609 | return NullUniValue; |
fc72c078 | 3610 | |
34f0001c | 3611 | if (fHelp || params.size() > 1) |
fc72c078 | 3612 | throw runtime_error( |
34f0001c | 3613 | "z_getoperationstatus ([\"operationid\", ... ]) \n" |
c1eae280 | 3614 | "\nGet operation status and any associated result or error data. The operation will remain in memory." |
fc72c078 S |
3615 | + HelpRequiringPassphrase() + "\n" |
3616 | "\nArguments:\n" | |
c1eae280 | 3617 | "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 | 3618 | "\nResult:\n" |
34f0001c | 3619 | "\" [object, ...]\" (array) A list of JSON objects\n" |
337a99a2 JG |
3620 | "\nExamples:\n" |
3621 | + HelpExampleCli("z_getoperationstatus", "'[\"operationid\", ... ]'") | |
3622 | + HelpExampleRpc("z_getoperationstatus", "'[\"operationid\", ... ]'") | |
fc72c078 | 3623 | ); |
9d365796 | 3624 | |
c1eae280 S |
3625 | // This call is idempotent so we don't want to remove finished operations |
3626 | return z_getoperationstatus_IMPL(params, false); | |
3627 | } | |
fc72c078 | 3628 | |
0d37ae3a | 3629 | UniValue z_getoperationstatus_IMPL(const UniValue& params, bool fRemoveFinishedOperations=false) |
c1eae280 | 3630 | { |
fc72c078 S |
3631 | LOCK2(cs_main, pwalletMain->cs_wallet); |
3632 | ||
34f0001c S |
3633 | std::set<AsyncRPCOperationId> filter; |
3634 | if (params.size()==1) { | |
0d37ae3a | 3635 | UniValue ids = params[0].get_array(); |
c24109ec | 3636 | for (const UniValue & v : ids.getValues()) { |
34f0001c S |
3637 | filter.insert(v.get_str()); |
3638 | } | |
fc72c078 | 3639 | } |
34f0001c | 3640 | bool useFilter = (filter.size()>0); |
fc72c078 | 3641 | |
0d37ae3a | 3642 | UniValue ret(UniValue::VARR); |
34f0001c S |
3643 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); |
3644 | std::vector<AsyncRPCOperationId> ids = q->getAllOperationIds(); | |
3645 | ||
3646 | for (auto id : ids) { | |
3647 | if (useFilter && !filter.count(id)) | |
3648 | continue; | |
9d365796 | 3649 | |
34f0001c S |
3650 | std::shared_ptr<AsyncRPCOperation> operation = q->getOperationForId(id); |
3651 | if (!operation) { | |
3652 | continue; | |
3653 | // It's possible that the operation was removed from the internal queue and map during this loop | |
3654 | // throw JSONRPCError(RPC_INVALID_PARAMETER, "No operation exists for that id."); | |
3655 | } | |
fc72c078 | 3656 | |
2f21206c S |
3657 | UniValue obj = operation->getStatus(); |
3658 | std::string s = obj["status"].get_str(); | |
c1eae280 S |
3659 | if (fRemoveFinishedOperations) { |
3660 | // Caller is only interested in retrieving finished results | |
2f21206c S |
3661 | if ("success"==s || "failed"==s || "cancelled"==s) { |
3662 | ret.push_back(obj); | |
c1eae280 S |
3663 | q->popOperationForId(id); |
3664 | } | |
3665 | } else { | |
2f21206c | 3666 | ret.push_back(obj); |
34f0001c | 3667 | } |
fc72c078 S |
3668 | } |
3669 | ||
0d37ae3a JG |
3670 | std::vector<UniValue> arrTmp = ret.getValues(); |
3671 | ||
2d2f3d18 | 3672 | // sort results chronologically by creation_time |
0d37ae3a | 3673 | std::sort(arrTmp.begin(), arrTmp.end(), [](UniValue a, UniValue b) -> bool { |
2d2f3d18 S |
3674 | const int64_t t1 = find_value(a.get_obj(), "creation_time").get_int64(); |
3675 | const int64_t t2 = find_value(b.get_obj(), "creation_time").get_int64(); | |
3676 | return t1 < t2; | |
3677 | }); | |
3678 | ||
0d37ae3a JG |
3679 | ret.clear(); |
3680 | ret.setArray(); | |
3681 | ret.push_backV(arrTmp); | |
3682 | ||
34f0001c | 3683 | return ret; |
fc72c078 S |
3684 | } |
3685 | ||
3920292b S |
3686 | |
3687 | // Here we define the maximum number of zaddr outputs that can be included in a transaction. | |
3688 | // If input notes are small, we might actually require more than one joinsplit per zaddr output. | |
3689 | // For now though, we assume we use one joinsplit per zaddr output (and the second output note is change). | |
3690 | // We reduce the result by 1 to ensure there is room for non-joinsplit CTransaction data. | |
3691 | #define Z_SENDMANY_MAX_ZADDR_OUTPUTS ((MAX_TX_SIZE / JSDescription().GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION)) - 1) | |
3692 | ||
3693 | // transaction.h comment: spending taddr output requires CTxIn >= 148 bytes and typical taddr txout is 34 bytes | |
3694 | #define CTXIN_SPEND_DUST_SIZE 148 | |
3695 | #define CTXOUT_REGULAR_SIZE 34 | |
3696 | ||
0d37ae3a | 3697 | UniValue z_sendmany(const UniValue& params, bool fHelp) |
fc72c078 S |
3698 | { |
3699 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 3700 | return NullUniValue; |
fc72c078 | 3701 | |
af53da02 | 3702 | if (fHelp || params.size() < 2 || params.size() > 4) |
fc72c078 | 3703 | throw runtime_error( |
af53da02 | 3704 | "z_sendmany \"fromaddress\" [{\"address\":... ,\"amount\":...},...] ( minconf ) ( fee )\n" |
fc72c078 | 3705 | "\nSend multiple times. Amounts are double-precision floating point numbers." |
b7d7b2ad | 3706 | "\nChange from a taddr flows to a new taddr address, while change from zaddr returns to itself." |
48f9c65b | 3707 | "\nWhen sending coinbase UTXOs to a zaddr, change is not allowed. The entire value of the UTXO(s) must be consumed." |
3920292b | 3708 | + strprintf("\nCurrently, the maximum number of zaddr outputs is %d due to transaction size limits.\n", Z_SENDMANY_MAX_ZADDR_OUTPUTS) |
fc72c078 S |
3709 | + HelpRequiringPassphrase() + "\n" |
3710 | "\nArguments:\n" | |
3711 | "1. \"fromaddress\" (string, required) The taddr or zaddr to send the funds from.\n" | |
3712 | "2. \"amounts\" (array, required) An array of json objects representing the amounts to send.\n" | |
3713 | " [{\n" | |
3714 | " \"address\":address (string, required) The address is a taddr or zaddr\n" | |
f37f614e | 3715 | " \"amount\":amount (numeric, required) The numeric amount in KMD is the value\n" |
fc72c078 S |
3716 | " \"memo\":memo (string, optional) If the address is a zaddr, raw data represented in hexadecimal string format\n" |
3717 | " }, ... ]\n" | |
3718 | "3. minconf (numeric, optional, default=1) Only use funds confirmed at least this many times.\n" | |
af53da02 S |
3719 | "4. fee (numeric, optional, default=" |
3720 | + strprintf("%s", FormatMoney(ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n" | |
fc72c078 S |
3721 | "\nResult:\n" |
3722 | "\"operationid\" (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" | |
337a99a2 | 3723 | "\nExamples:\n" |
1333d0d7 | 3724 | + HelpExampleCli("z_sendmany", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" '[{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\" ,\"amount\": 5.0}]'") |
3725 | + HelpExampleRpc("z_sendmany", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", [{\"address\": \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\" ,\"amount\": 5.0}]") | |
fc72c078 S |
3726 | ); |
3727 | ||
3728 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3729 | ||
fc72c078 S |
3730 | // Check that the from address is valid. |
3731 | auto fromaddress = params[0].get_str(); | |
3732 | bool fromTaddr = false; | |
3733 | CBitcoinAddress taddr(fromaddress); | |
3734 | fromTaddr = taddr.IsValid(); | |
3735 | libzcash::PaymentAddress zaddr; | |
3736 | if (!fromTaddr) { | |
3737 | CZCPaymentAddress address(fromaddress); | |
3738 | try { | |
3739 | zaddr = address.Get(); | |
fce72608 | 3740 | } catch (const std::runtime_error&) { |
fc72c078 S |
3741 | // invalid |
3742 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or zaddr."); | |
3743 | } | |
3744 | } | |
3745 | ||
dafb8161 | 3746 | // Check that we have the spending key |
fc72c078 S |
3747 | if (!fromTaddr) { |
3748 | if (!pwalletMain->HaveSpendingKey(zaddr)) { | |
3749 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "From address does not belong to this node, zaddr spending key not found."); | |
3750 | } | |
3751 | } | |
3752 | ||
0d37ae3a | 3753 | UniValue outputs = params[1].get_array(); |
fc72c078 S |
3754 | |
3755 | if (outputs.size()==0) | |
3756 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amounts array is empty."); | |
3757 | ||
3758 | // Keep track of addresses to spot duplicates | |
3759 | set<std::string> setAddress; | |
3760 | ||
3761 | // Recipients | |
dafb8161 S |
3762 | std::vector<SendManyRecipient> taddrRecipients; |
3763 | std::vector<SendManyRecipient> zaddrRecipients; | |
af53da02 | 3764 | CAmount nTotalOut = 0; |
fc72c078 | 3765 | |
0d37ae3a JG |
3766 | for (const UniValue& o : outputs.getValues()) { |
3767 | if (!o.isObject()) | |
fc72c078 | 3768 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object"); |
fc72c078 | 3769 | |
fc72c078 | 3770 | // sanity check, report error if unknown key-value pairs |
0d37ae3a JG |
3771 | for (const string& name_ : o.getKeys()) { |
3772 | std::string s = name_; | |
fc72c078 S |
3773 | if (s != "address" && s != "amount" && s!="memo") |
3774 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown key: ")+s); | |
3775 | } | |
3776 | ||
3777 | string address = find_value(o, "address").get_str(); | |
3778 | bool isZaddr = false; | |
3779 | CBitcoinAddress taddr(address); | |
2f151c30 | 3780 | if (!taddr.IsValid()) |
3781 | { | |
fc72c078 S |
3782 | try { |
3783 | CZCPaymentAddress zaddr(address); | |
3784 | zaddr.Get(); | |
3785 | isZaddr = true; | |
fce72608 | 3786 | } catch (const std::runtime_error&) { |
fc72c078 S |
3787 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ")+address ); |
3788 | } | |
3789 | } | |
2f151c30 | 3790 | else if ( ASSETCHAINS_PRIVATE != 0 ) |
3791 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "cant use transparent addresses in private chain"); | |
fc72c078 S |
3792 | |
3793 | if (setAddress.count(address)) | |
3794 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+address); | |
3795 | setAddress.insert(address); | |
3796 | ||
0d37ae3a | 3797 | UniValue memoValue = find_value(o, "memo"); |
fc72c078 | 3798 | string memo; |
0d37ae3a | 3799 | if (!memoValue.isNull()) { |
fc72c078 S |
3800 | memo = memoValue.get_str(); |
3801 | if (!isZaddr) { | |
c938fb1f | 3802 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo cannot be used with a taddr. It can only be used with a zaddr."); |
fc72c078 S |
3803 | } else if (!IsHex(memo)) { |
3804 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format."); | |
3805 | } | |
6114cfe7 | 3806 | if (memo.length() > ZC_MEMO_SIZE*2) { |
dafb8161 S |
3807 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE )); |
3808 | } | |
fc72c078 S |
3809 | } |
3810 | ||
0d37ae3a | 3811 | UniValue av = find_value(o, "amount"); |
fc72c078 S |
3812 | CAmount nAmount = AmountFromValue( av ); |
3813 | if (nAmount < 0) | |
3814 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amount must be positive"); | |
3815 | ||
dafb8161 S |
3816 | if (isZaddr) { |
3817 | zaddrRecipients.push_back( SendManyRecipient(address, nAmount, memo) ); | |
3818 | } else { | |
3819 | taddrRecipients.push_back( SendManyRecipient(address, nAmount, memo) ); | |
3820 | } | |
af53da02 S |
3821 | |
3822 | nTotalOut += nAmount; | |
fc72c078 S |
3823 | } |
3824 | ||
3920292b S |
3825 | // Check the number of zaddr outputs does not exceed the limit. |
3826 | if (zaddrRecipients.size() > Z_SENDMANY_MAX_ZADDR_OUTPUTS) { | |
3827 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, too many zaddr outputs"); | |
3828 | } | |
3829 | ||
3830 | // As a sanity check, estimate and verify that the size of the transaction will be valid. | |
3831 | // Depending on the input notes, the actual tx size may turn out to be larger and perhaps invalid. | |
3832 | size_t txsize = 0; | |
3833 | CMutableTransaction mtx; | |
3834 | mtx.nVersion = 2; | |
3835 | for (int i = 0; i < zaddrRecipients.size(); i++) { | |
3836 | mtx.vjoinsplit.push_back(JSDescription()); | |
3837 | } | |
3838 | CTransaction tx(mtx); | |
3839 | txsize += tx.GetSerializeSize(SER_NETWORK, tx.nVersion); | |
3840 | if (fromTaddr) { | |
3841 | txsize += CTXIN_SPEND_DUST_SIZE; | |
3842 | txsize += CTXOUT_REGULAR_SIZE; // There will probably be taddr change | |
3843 | } | |
3844 | txsize += CTXOUT_REGULAR_SIZE * taddrRecipients.size(); | |
3845 | if (txsize > MAX_TX_SIZE) { | |
3846 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Too many outputs, size of raw transaction would be larger than limit of %d bytes", MAX_TX_SIZE )); | |
fc72c078 S |
3847 | } |
3848 | ||
fc72c078 S |
3849 | // Minimum confirmations |
3850 | int nMinDepth = 1; | |
12448b64 | 3851 | if (params.size() > 2) { |
fc72c078 | 3852 | nMinDepth = params[2].get_int(); |
12448b64 S |
3853 | } |
3854 | if (nMinDepth < 0) { | |
3855 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Minimum number of confirmations cannot be less than 0"); | |
3856 | } | |
fc72c078 | 3857 | |
af53da02 S |
3858 | // Fee in Zatoshis, not currency format) |
3859 | CAmount nFee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE; | |
3860 | if (params.size() > 3) { | |
7eccce4e S |
3861 | if (params[3].get_real() == 0.0) { |
3862 | nFee = 0; | |
3863 | } else { | |
3864 | nFee = AmountFromValue( params[3] ); | |
3865 | } | |
3866 | ||
af53da02 S |
3867 | // Check that the user specified fee is sane. |
3868 | if (nFee > nTotalOut) { | |
3869 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the sum of outputs %s", FormatMoney(nFee), FormatMoney(nTotalOut))); | |
3870 | } | |
3871 | } | |
3872 | ||
8aa7937d | 3873 | // Use input parameters as the optional context info to be returned by z_getoperationstatus and z_getoperationresult. |
0d37ae3a | 3874 | UniValue o(UniValue::VOBJ); |
8aa7937d S |
3875 | o.push_back(Pair("fromaddress", params[0])); |
3876 | o.push_back(Pair("amounts", params[1])); | |
3877 | o.push_back(Pair("minconf", nMinDepth)); | |
3878 | o.push_back(Pair("fee", std::stod(FormatMoney(nFee)))); | |
0d37ae3a | 3879 | UniValue contextInfo = o; |
8aa7937d | 3880 | |
072099d7 | 3881 | // Contextual transaction we will build on |
9bb37bf0 JG |
3882 | int nextBlockHeight = chainActive.Height() + 1; |
3883 | CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextBlockHeight); | |
e6cd2a83 S |
3884 | bool isShielded = !fromTaddr || zaddrRecipients.size() > 0; |
3885 | if (contextualTx.nVersion == 1 && isShielded) { | |
7d2e4b3c | 3886 | contextualTx.nVersion = 2; // Tx format should support vjoinsplits |
072099d7 | 3887 | } |
9bb37bf0 JG |
3888 | if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) { |
3889 | contextualTx.nExpiryHeight = nextBlockHeight + expiryDelta; | |
3890 | } | |
072099d7 | 3891 | |
dafb8161 | 3892 | // Create operation and add to global queue |
fc72c078 | 3893 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); |
072099d7 | 3894 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_sendmany(contextualTx, fromaddress, taddrRecipients, zaddrRecipients, nMinDepth, nFee, contextInfo) ); |
fc72c078 S |
3895 | q->addOperation(operation); |
3896 | AsyncRPCOperationId operationId = operation->getId(); | |
3897 | return operationId; | |
fc72c078 | 3898 | } |
34f0001c S |
3899 | |
3900 | ||
06c19063 S |
3901 | /** |
3902 | When estimating the number of coinbase utxos we can shield in a single transaction: | |
3903 | 1. Joinsplit description is 1802 bytes. | |
3904 | 2. Transaction overhead ~ 100 bytes | |
3905 | 3. Spending a typical P2PKH is >=148 bytes, as defined in CTXIN_SPEND_DUST_SIZE. | |
3906 | 4. Spending a multi-sig P2SH address can vary greatly: | |
3907 | https://github.com/bitcoin/bitcoin/blob/c3ad56f4e0b587d8d763af03d743fdfc2d180c9b/src/main.cpp#L517 | |
3908 | In real-world coinbase utxos, we consider a 3-of-3 multisig, where the size is roughly: | |
3909 | (3*(33+1))+3 = 105 byte redeem script | |
3910 | 105 + 1 + 3*(73+1) = 328 bytes of scriptSig, rounded up to 400 based on testnet experiments. | |
3911 | */ | |
3912 | #define CTXIN_SPEND_P2SH_SIZE 400 | |
3913 | ||
c5dabd2b S |
3914 | #define SHIELD_COINBASE_DEFAULT_LIMIT 50 |
3915 | ||
06c19063 S |
3916 | UniValue z_shieldcoinbase(const UniValue& params, bool fHelp) |
3917 | { | |
3918 | if (!EnsureWalletIsAvailable(fHelp)) | |
3919 | return NullUniValue; | |
3920 | ||
c5dabd2b | 3921 | if (fHelp || params.size() < 2 || params.size() > 4) |
06c19063 | 3922 | throw runtime_error( |
c5dabd2b | 3923 | "z_shieldcoinbase \"fromaddress\" \"tozaddress\" ( fee ) ( limit )\n" |
06c19063 S |
3924 | "\nShield transparent coinbase funds by sending to a shielded zaddr. This is an asynchronous operation and utxos" |
3925 | "\nselected for shielding will be locked. If there is an error, they are unlocked. The RPC call `listlockunspent`" | |
c5dabd2b S |
3926 | "\ncan be used to return a list of locked utxos. The number of coinbase utxos selected for shielding can be limited" |
3927 | "\nby the caller. If the limit parameter is set to zero, the -mempooltxinputlimit option will determine the number" | |
3928 | "\nof uxtos. Any limit is constrained by the consensus rule defining a maximum transaction size of " | |
06c19063 S |
3929 | + strprintf("%d bytes.", MAX_TX_SIZE) |
3930 | + HelpRequiringPassphrase() + "\n" | |
3931 | "\nArguments:\n" | |
3932 | "1. \"fromaddress\" (string, required) The address is a taddr or \"*\" for all taddrs belonging to the wallet.\n" | |
3933 | "2. \"toaddress\" (string, required) The address is a zaddr.\n" | |
3934 | "3. fee (numeric, optional, default=" | |
3935 | + strprintf("%s", FormatMoney(SHIELD_COINBASE_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n" | |
c5dabd2b S |
3936 | "4. limit (numeric, optional, default=" |
3937 | + strprintf("%d", SHIELD_COINBASE_DEFAULT_LIMIT) + ") Limit on the maximum number of utxos to shield. Set to 0 to use node option -mempooltxinputlimit.\n" | |
06c19063 S |
3938 | "\nResult:\n" |
3939 | "{\n" | |
06c19063 S |
3940 | " \"remainingUTXOs\": xxx (numeric) Number of coinbase utxos still available for shielding.\n" |
3941 | " \"remainingValue\": xxx (numeric) Value of coinbase utxos still available for shielding.\n" | |
9eb8089e JG |
3942 | " \"shieldingUTXOs\": xxx (numeric) Number of coinbase utxos being shielded.\n" |
3943 | " \"shieldingValue\": xxx (numeric) Value of coinbase utxos being shielded.\n" | |
3944 | " \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" | |
06c19063 | 3945 | "}\n" |
337a99a2 | 3946 | "\nExamples:\n" |
1333d0d7 | 3947 | + HelpExampleCli("z_shieldcoinbase", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\" \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") |
3948 | + HelpExampleRpc("z_shieldcoinbase", "\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\", \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") | |
06c19063 S |
3949 | ); |
3950 | ||
3951 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
3952 | ||
3953 | // Validate the from address | |
3954 | auto fromaddress = params[0].get_str(); | |
3955 | bool isFromWildcard = fromaddress == "*"; | |
3956 | CBitcoinAddress taddr; | |
3957 | if (!isFromWildcard) { | |
3958 | taddr = CBitcoinAddress(fromaddress); | |
3959 | if (!taddr.IsValid()) { | |
3960 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid from address, should be a taddr or \"*\"."); | |
3961 | } | |
3962 | } | |
3963 | ||
3964 | // Validate the destination address | |
3965 | auto destaddress = params[1].get_str(); | |
3966 | try { | |
3967 | CZCPaymentAddress pa(destaddress); | |
3968 | libzcash::PaymentAddress zaddr = pa.Get(); | |
3969 | } catch (const std::runtime_error&) { | |
3970 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); | |
3971 | } | |
3972 | ||
3973 | // Convert fee from currency format to zatoshis | |
3974 | CAmount nFee = SHIELD_COINBASE_DEFAULT_MINERS_FEE; | |
3975 | if (params.size() > 2) { | |
3976 | if (params[2].get_real() == 0.0) { | |
3977 | nFee = 0; | |
3978 | } else { | |
3979 | nFee = AmountFromValue( params[2] ); | |
3980 | } | |
3981 | } | |
3982 | ||
c5dabd2b S |
3983 | int nLimit = SHIELD_COINBASE_DEFAULT_LIMIT; |
3984 | if (params.size() > 3) { | |
3985 | nLimit = params[3].get_int(); | |
3986 | if (nLimit < 0) { | |
3987 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of utxos cannot be negative"); | |
3988 | } | |
3989 | } | |
3990 | ||
06c19063 S |
3991 | // Prepare to get coinbase utxos |
3992 | std::vector<ShieldCoinbaseUTXO> inputs; | |
3993 | CAmount shieldedValue = 0; | |
3994 | CAmount remainingValue = 0; | |
3995 | size_t estimatedTxSize = 2000; // 1802 joinsplit description + tx overhead + wiggle room | |
7d2e4b3c | 3996 | |
3997 | #ifdef __LP64__ | |
3998 | uint64_t utxoCounter = 0; | |
3999 | #else | |
06c19063 | 4000 | size_t utxoCounter = 0; |
7d2e4b3c | 4001 | #endif |
4002 | ||
06c19063 | 4003 | bool maxedOutFlag = false; |
c5dabd2b | 4004 | size_t mempoolLimit = (nLimit != 0) ? nLimit : (size_t)GetArg("-mempooltxinputlimit", 0); |
06c19063 S |
4005 | |
4006 | // Set of addresses to filter utxos by | |
4007 | set<CBitcoinAddress> setAddress = {}; | |
4008 | if (!isFromWildcard) { | |
4009 | setAddress.insert(taddr); | |
4010 | } | |
4011 | ||
4012 | // Get available utxos | |
4013 | vector<COutput> vecOutputs; | |
4014 | pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, true); | |
4015 | ||
4016 | // Find unspent coinbase utxos and update estimated size | |
4017 | BOOST_FOREACH(const COutput& out, vecOutputs) { | |
4018 | if (!out.fSpendable) { | |
4019 | continue; | |
4020 | } | |
4021 | ||
4022 | CTxDestination address; | |
4023 | if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { | |
4024 | continue; | |
4025 | } | |
4026 | // If taddr is not wildcard "*", filter utxos | |
4027 | if (setAddress.size()>0 && !setAddress.count(address)) { | |
4028 | continue; | |
4029 | } | |
4030 | ||
4031 | if (!out.tx->IsCoinBase()) { | |
4032 | continue; | |
4033 | } | |
4034 | ||
4035 | utxoCounter++; | |
4036 | CAmount nValue = out.tx->vout[out.i].nValue; | |
4037 | ||
4038 | if (!maxedOutFlag) { | |
4039 | CBitcoinAddress ba(address); | |
4040 | size_t increase = (ba.IsScript()) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_DUST_SIZE; | |
4041 | if (estimatedTxSize + increase >= MAX_TX_SIZE || | |
4042 | (mempoolLimit > 0 && utxoCounter > mempoolLimit)) | |
4043 | { | |
4044 | maxedOutFlag = true; | |
4045 | } else { | |
4046 | estimatedTxSize += increase; | |
4047 | ShieldCoinbaseUTXO utxo = {out.tx->GetHash(), out.i, nValue}; | |
4048 | inputs.push_back(utxo); | |
4049 | shieldedValue += nValue; | |
4050 | } | |
4051 | } | |
4052 | ||
4053 | if (maxedOutFlag) { | |
4054 | remainingValue += nValue; | |
4055 | } | |
4056 | } | |
4057 | ||
7d2e4b3c | 4058 | #ifdef __LP64__ |
4059 | uint64_t numUtxos = inputs.size(); | |
4060 | #else | |
06c19063 | 4061 | size_t numUtxos = inputs.size(); |
7d2e4b3c | 4062 | #endif |
06c19063 S |
4063 | |
4064 | if (numUtxos == 0) { | |
4065 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any coinbase funds to shield."); | |
4066 | } | |
4067 | ||
4068 | if (shieldedValue < nFee) { | |
4069 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, | |
4070 | strprintf("Insufficient coinbase funds, have %s, which is less than miners fee %s", | |
4071 | FormatMoney(shieldedValue), FormatMoney(nFee))); | |
4072 | } | |
4073 | ||
4074 | // Check that the user specified fee is sane (if too high, it can result in error -25 absurd fee) | |
4075 | CAmount netAmount = shieldedValue - nFee; | |
4076 | if (nFee > netAmount) { | |
4077 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the net amount to be shielded %s", FormatMoney(nFee), FormatMoney(netAmount))); | |
4078 | } | |
4079 | ||
4080 | // Keep record of parameters in context object | |
4081 | UniValue contextInfo(UniValue::VOBJ); | |
4082 | contextInfo.push_back(Pair("fromaddress", params[0])); | |
4083 | contextInfo.push_back(Pair("toaddress", params[1])); | |
4084 | contextInfo.push_back(Pair("fee", ValueFromAmount(nFee))); | |
4085 | ||
072099d7 | 4086 | // Contextual transaction we will build on |
9bb37bf0 | 4087 | int nextBlockHeight = chainActive.Height() + 1; |
072099d7 | 4088 | CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction( |
9bb37bf0 | 4089 | Params().GetConsensus(), nextBlockHeight); |
072099d7 | 4090 | if (contextualTx.nVersion == 1) { |
7d2e4b3c | 4091 | contextualTx.nVersion = 2; // Tx format should support vjoinsplits |
072099d7 | 4092 | } |
9bb37bf0 JG |
4093 | if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) { |
4094 | contextualTx.nExpiryHeight = nextBlockHeight + expiryDelta; | |
4095 | } | |
072099d7 | 4096 | |
06c19063 S |
4097 | // Create operation and add to global queue |
4098 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); | |
072099d7 | 4099 | std::shared_ptr<AsyncRPCOperation> operation( new AsyncRPCOperation_shieldcoinbase(contextualTx, inputs, destaddress, nFee, contextInfo) ); |
06c19063 S |
4100 | q->addOperation(operation); |
4101 | AsyncRPCOperationId operationId = operation->getId(); | |
4102 | ||
4103 | // Return continuation information | |
4104 | UniValue o(UniValue::VOBJ); | |
4105 | o.push_back(Pair("remainingUTXOs", utxoCounter - numUtxos)); | |
4106 | o.push_back(Pair("remainingValue", ValueFromAmount(remainingValue))); | |
4107 | o.push_back(Pair("shieldingUTXOs", numUtxos)); | |
4108 | o.push_back(Pair("shieldingValue", ValueFromAmount(shieldedValue))); | |
4109 | o.push_back(Pair("opid", operationId)); | |
4110 | return o; | |
4111 | } | |
4112 | ||
4113 | ||
6e9c7629 JG |
4114 | #define MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT 50 |
4115 | #define MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT 10 | |
4116 | ||
4117 | #define JOINSPLIT_SIZE JSDescription().GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION) | |
4118 | ||
4119 | UniValue z_mergetoaddress(const UniValue& params, bool fHelp) | |
4120 | { | |
4121 | if (!EnsureWalletIsAvailable(fHelp)) | |
4122 | return NullUniValue; | |
4123 | ||
f5446e44 | 4124 | auto fEnableMergeToAddress = true; //fExperimentalMode && GetBoolArg("-zmergetoaddress", false); |
553a5c1a JG |
4125 | std::string strDisabledMsg = ""; |
4126 | if (!fEnableMergeToAddress) { | |
4127 | strDisabledMsg = "\nWARNING: z_mergetoaddress is DISABLED but can be enabled as an experimental feature.\n"; | |
4128 | } | |
4129 | ||
6e9c7629 JG |
4130 | if (fHelp || params.size() < 2 || params.size() > 6) |
4131 | throw runtime_error( | |
4132 | "z_mergetoaddress [\"fromaddress\", ... ] \"toaddress\" ( fee ) ( transparent_limit ) ( shielded_limit ) ( memo )\n" | |
553a5c1a | 4133 | + strDisabledMsg + |
6e9c7629 JG |
4134 | "\nMerge multiple UTXOs and notes into a single UTXO or note. Coinbase UTXOs are ignored; use `z_shieldcoinbase`" |
4135 | "\nto combine those into a single note." | |
4136 | "\n\nThis is an asynchronous operation, and UTXOs selected for merging will be locked. If there is an error, they" | |
4137 | "\nare unlocked. The RPC call `listlockunspent` can be used to return a list of locked UTXOs." | |
4138 | "\n\nThe number of UTXOs and notes selected for merging can be limited by the caller. If the transparent limit" | |
4139 | "\nparameter is set to zero, the -mempooltxinputlimit option will determine the number of UTXOs. Any limit is" | |
4140 | "\nconstrained by the consensus rule defining a maximum transaction size of " | |
4141 | + strprintf("%d bytes.", MAX_TX_SIZE) | |
4142 | + HelpRequiringPassphrase() + "\n" | |
4143 | "\nArguments:\n" | |
4144 | "1. fromaddresses (string, required) A JSON array with addresses.\n" | |
4145 | " The following special strings are accepted inside the array:\n" | |
4146 | " - \"*\": Merge both UTXOs and notes from all addresses belonging to the wallet.\n" | |
4147 | " - \"ANY_TADDR\": Merge UTXOs from all t-addrs belonging to the wallet.\n" | |
4148 | " - \"ANY_ZADDR\": Merge notes from all z-addrs belonging to the wallet.\n" | |
4149 | " If a special string is given, any given addresses of that type will be ignored.\n" | |
4150 | " [\n" | |
4151 | " \"address\" (string) Can be a t-addr or a z-addr\n" | |
4152 | " ,...\n" | |
4153 | " ]\n" | |
4154 | "2. \"toaddress\" (string, required) The t-addr or z-addr to send the funds to.\n" | |
4155 | "3. fee (numeric, optional, default=" | |
4156 | + strprintf("%s", FormatMoney(MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n" | |
4157 | "4. transparent_limit (numeric, optional, default=" | |
4158 | + strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT) + ") Limit on the maximum number of UTXOs to merge. Set to 0 to use node option -mempooltxinputlimit.\n" | |
4159 | "4. shielded_limit (numeric, optional, default=" | |
4160 | + strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT) + ") Limit on the maximum number of notes to merge. Set to 0 to merge as many as will fit in the transaction.\n" | |
4161 | "5. \"memo\" (string, optional) Encoded as hex. When toaddress is a z-addr, this will be stored in the memo field of the new note.\n" | |
4162 | "\nResult:\n" | |
4163 | "{\n" | |
4164 | " \"remainingUTXOs\": xxx (numeric) Number of UTXOs still available for merging.\n" | |
4165 | " \"remainingTransparentValue\": xxx (numeric) Value of UTXOs still available for merging.\n" | |
4166 | " \"remainingNotes\": xxx (numeric) Number of notes still available for merging.\n" | |
4167 | " \"remainingShieldedValue\": xxx (numeric) Value of notes still available for merging.\n" | |
4168 | " \"mergingUTXOs\": xxx (numeric) Number of UTXOs being merged.\n" | |
4169 | " \"mergingTransparentValue\": xxx (numeric) Value of UTXOs being merged.\n" | |
4170 | " \"mergingNotes\": xxx (numeric) Number of notes being merged.\n" | |
4171 | " \"mergingShieldedValue\": xxx (numeric) Value of notes being merged.\n" | |
4172 | " \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" | |
4173 | "}\n" | |
4174 | "\nExamples:\n" | |
1333d0d7 | 4175 | + HelpExampleCli("z_mergetoaddress", "'[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"]' ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf") |
4176 | + HelpExampleRpc("z_mergetoaddress", "[\"RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV\"], \"ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf\"") | |
6e9c7629 JG |
4177 | ); |
4178 | ||
553a5c1a JG |
4179 | if (!fEnableMergeToAddress) { |
4180 | throw JSONRPCError(RPC_WALLET_ERROR, "Error: z_mergetoaddress is disabled."); | |
4181 | } | |
4182 | ||
6e9c7629 JG |
4183 | LOCK2(cs_main, pwalletMain->cs_wallet); |
4184 | ||
4185 | bool useAny = false; | |
4186 | bool useAnyUTXO = false; | |
4187 | bool useAnyNote = false; | |
4188 | std::set<CBitcoinAddress> taddrs = {}; | |
4189 | std::set<libzcash::PaymentAddress> zaddrs = {}; | |
4190 | ||
4191 | UniValue addresses = params[0].get_array(); | |
4192 | if (addresses.size()==0) | |
4193 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, fromaddresses array is empty."); | |
4194 | ||
4195 | // Keep track of addresses to spot duplicates | |
4196 | std::set<std::string> setAddress; | |
4197 | ||
4198 | // Sources | |
4199 | for (const UniValue& o : addresses.getValues()) { | |
4200 | if (!o.isStr()) | |
4201 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected string"); | |
4202 | ||
4203 | std::string address = o.get_str(); | |
4204 | if (address == "*") { | |
4205 | useAny = true; | |
4206 | } else if (address == "ANY_TADDR") { | |
4207 | useAnyUTXO = true; | |
4208 | } else if (address == "ANY_ZADDR") { | |
4209 | useAnyNote = true; | |
4210 | } else { | |
4211 | CBitcoinAddress taddr(address); | |
4212 | if (taddr.IsValid()) { | |
4213 | // Ignore any listed t-addrs if we are using all of them | |
4214 | if (!(useAny || useAnyUTXO)) { | |
4215 | taddrs.insert(taddr); | |
4216 | } | |
4217 | } else { | |
4218 | try { | |
4219 | CZCPaymentAddress zaddr(address); | |
4220 | // Ignore listed z-addrs if we are using all of them | |
4221 | if (!(useAny || useAnyNote)) { | |
4222 | zaddrs.insert(zaddr.Get()); | |
4223 | } | |
4224 | } catch (const std::runtime_error&) { | |
4225 | throw JSONRPCError( | |
4226 | RPC_INVALID_PARAMETER, | |
4227 | string("Invalid parameter, unknown address format: ") + address); | |
4228 | } | |
4229 | } | |
4230 | } | |
4231 | ||
4232 | if (setAddress.count(address)) | |
4233 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ") + address); | |
4234 | setAddress.insert(address); | |
4235 | } | |
4236 | ||
4237 | // Validate the destination address | |
4238 | auto destaddress = params[1].get_str(); | |
4239 | bool isToZaddr = false; | |
4240 | CBitcoinAddress taddr(destaddress); | |
4241 | if (!taddr.IsValid()) { | |
4242 | try { | |
4243 | CZCPaymentAddress zaddr(destaddress); | |
4244 | zaddr.Get(); | |
4245 | isToZaddr = true; | |
4246 | } catch (const std::runtime_error&) { | |
4247 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); | |
4248 | } | |
4249 | } | |
2f151c30 | 4250 | else if ( ASSETCHAINS_PRIVATE != 0 ) |
4251 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "cant use transparent addresses in private chain"); | |
6e9c7629 JG |
4252 | |
4253 | // Convert fee from currency format to zatoshis | |
4254 | CAmount nFee = SHIELD_COINBASE_DEFAULT_MINERS_FEE; | |
4255 | if (params.size() > 2) { | |
4256 | if (params[2].get_real() == 0.0) { | |
4257 | nFee = 0; | |
4258 | } else { | |
4259 | nFee = AmountFromValue( params[2] ); | |
4260 | } | |
4261 | } | |
4262 | ||
4263 | int nUTXOLimit = MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT; | |
4264 | if (params.size() > 3) { | |
4265 | nUTXOLimit = params[3].get_int(); | |
4266 | if (nUTXOLimit < 0) { | |
4267 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of UTXOs cannot be negative"); | |
4268 | } | |
4269 | } | |
4270 | ||
4271 | int nNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT; | |
4272 | if (params.size() > 4) { | |
4273 | nNoteLimit = params[4].get_int(); | |
4274 | if (nNoteLimit < 0) { | |
4275 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of notes cannot be negative"); | |
4276 | } | |
4277 | } | |
4278 | ||
4279 | std::string memo; | |
4280 | if (params.size() > 5) { | |
4281 | memo = params[5].get_str(); | |
4282 | if (!isToZaddr) { | |
4283 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo can not be used with a taddr. It can only be used with a zaddr."); | |
4284 | } else if (!IsHex(memo)) { | |
4285 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format."); | |
4286 | } | |
4287 | if (memo.length() > ZC_MEMO_SIZE*2) { | |
4288 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE )); | |
4289 | } | |
4290 | } | |
4291 | ||
4292 | MergeToAddressRecipient recipient(destaddress, memo); | |
4293 | ||
4294 | // Prepare to get UTXOs and notes | |
4295 | std::vector<MergeToAddressInputUTXO> utxoInputs; | |
4296 | std::vector<MergeToAddressInputNote> noteInputs; | |
4297 | CAmount mergedUTXOValue = 0; | |
4298 | CAmount mergedNoteValue = 0; | |
4299 | CAmount remainingUTXOValue = 0; | |
4300 | CAmount remainingNoteValue = 0; | |
7d2e4b3c | 4301 | #ifdef __LP64__ |
4302 | uint64_t utxoCounter = 0; | |
4303 | uint64_t noteCounter = 0; | |
4304 | #else | |
6e9c7629 JG |
4305 | size_t utxoCounter = 0; |
4306 | size_t noteCounter = 0; | |
7d2e4b3c | 4307 | #endif |
6e9c7629 JG |
4308 | bool maxedOutUTXOsFlag = false; |
4309 | bool maxedOutNotesFlag = false; | |
4310 | size_t mempoolLimit = (nUTXOLimit != 0) ? nUTXOLimit : (size_t)GetArg("-mempooltxinputlimit", 0); | |
4311 | ||
4312 | size_t estimatedTxSize = 200; // tx overhead + wiggle room | |
4313 | if (isToZaddr) { | |
4314 | estimatedTxSize += JOINSPLIT_SIZE; | |
4315 | } | |
4316 | ||
4317 | if (useAny || useAnyUTXO || taddrs.size() > 0) { | |
4318 | // Get available utxos | |
4319 | vector<COutput> vecOutputs; | |
4320 | pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, false); | |
4321 | ||
4322 | // Find unspent utxos and update estimated size | |
4323 | for (const COutput& out : vecOutputs) { | |
4324 | if (!out.fSpendable) { | |
4325 | continue; | |
4326 | } | |
4327 | ||
4328 | CTxDestination address; | |
4329 | if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { | |
4330 | continue; | |
4331 | } | |
4332 | // If taddr is not wildcard "*", filter utxos | |
4333 | if (taddrs.size() > 0 && !taddrs.count(address)) { | |
4334 | continue; | |
4335 | } | |
4336 | ||
4337 | utxoCounter++; | |
4338 | CAmount nValue = out.tx->vout[out.i].nValue; | |
4339 | ||
4340 | if (!maxedOutUTXOsFlag) { | |
4341 | CBitcoinAddress ba(address); | |
4342 | size_t increase = (ba.IsScript()) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_DUST_SIZE; | |
4343 | if (estimatedTxSize + increase >= MAX_TX_SIZE || | |
4344 | (mempoolLimit > 0 && utxoCounter > mempoolLimit)) | |
4345 | { | |
4346 | maxedOutUTXOsFlag = true; | |
4347 | } else { | |
4348 | estimatedTxSize += increase; | |
4349 | COutPoint utxo(out.tx->GetHash(), out.i); | |
4350 | utxoInputs.emplace_back(utxo, nValue); | |
4351 | mergedUTXOValue += nValue; | |
4352 | } | |
4353 | } | |
4354 | ||
4355 | if (maxedOutUTXOsFlag) { | |
4356 | remainingUTXOValue += nValue; | |
4357 | } | |
4358 | } | |
4359 | } | |
4360 | ||
4361 | if (useAny || useAnyNote || zaddrs.size() > 0) { | |
4362 | // Get available notes | |
4363 | std::vector<CNotePlaintextEntry> entries; | |
4364 | pwalletMain->GetFilteredNotes(entries, zaddrs); | |
4365 | ||
4366 | // Find unspent notes and update estimated size | |
4367 | for (CNotePlaintextEntry& entry : entries) { | |
4368 | noteCounter++; | |
4369 | CAmount nValue = entry.plaintext.value; | |
4370 | ||
4371 | if (!maxedOutNotesFlag) { | |
4372 | // If we haven't added any notes yet and the merge is to a | |
4373 | // z-address, we have already accounted for the first JoinSplit. | |
4374 | size_t increase = (noteInputs.empty() && !isToZaddr) || (noteInputs.size() % 2 == 0) ? JOINSPLIT_SIZE : 0; | |
4375 | if (estimatedTxSize + increase >= MAX_TX_SIZE || | |
4376 | (nNoteLimit > 0 && noteCounter > nNoteLimit)) | |
4377 | { | |
4378 | maxedOutNotesFlag = true; | |
4379 | } else { | |
4380 | estimatedTxSize += increase; | |
4381 | SpendingKey zkey; | |
4382 | pwalletMain->GetSpendingKey(entry.address, zkey); | |
4383 | noteInputs.emplace_back(entry.jsop, entry.plaintext.note(entry.address), nValue, zkey); | |
4384 | mergedNoteValue += nValue; | |
4385 | } | |
4386 | } | |
4387 | ||
4388 | if (maxedOutNotesFlag) { | |
4389 | remainingNoteValue += nValue; | |
4390 | } | |
4391 | } | |
4392 | } | |
4393 | ||
7d2e4b3c | 4394 | #ifdef __LP64__ |
4395 | uint64_t numUtxos = utxoInputs.size(); //ca333 | |
4396 | uint64_t numNotes = noteInputs.size(); | |
4397 | #else | |
6e9c7629 JG |
4398 | size_t numUtxos = utxoInputs.size(); |
4399 | size_t numNotes = noteInputs.size(); | |
7d2e4b3c | 4400 | #endif |
4401 | ||
6e9c7629 JG |
4402 | |
4403 | if (numUtxos == 0 && numNotes == 0) { | |
4404 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any funds to merge."); | |
4405 | } | |
4406 | ||
4407 | // Sanity check: Don't do anything if: | |
4408 | // - We only have one from address | |
4409 | // - It's equal to toaddress | |
4410 | // - The address only contains a single UTXO or note | |
4411 | if (setAddress.size() == 1 && setAddress.count(destaddress) && (numUtxos + numNotes) == 1) { | |
4412 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Destination address is also the only source address, and all its funds are already merged."); | |
4413 | } | |
4414 | ||
4415 | CAmount mergedValue = mergedUTXOValue + mergedNoteValue; | |
4416 | if (mergedValue < nFee) { | |
4417 | throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, | |
4418 | strprintf("Insufficient funds, have %s, which is less than miners fee %s", | |
4419 | FormatMoney(mergedValue), FormatMoney(nFee))); | |
4420 | } | |
4421 | ||
4422 | // Check that the user specified fee is sane (if too high, it can result in error -25 absurd fee) | |
4423 | CAmount netAmount = mergedValue - nFee; | |
4424 | if (nFee > netAmount) { | |
4425 | throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the net amount to be shielded %s", FormatMoney(nFee), FormatMoney(netAmount))); | |
4426 | } | |
4427 | ||
4428 | // Keep record of parameters in context object | |
4429 | UniValue contextInfo(UniValue::VOBJ); | |
4430 | contextInfo.push_back(Pair("fromaddresses", params[0])); | |
4431 | contextInfo.push_back(Pair("toaddress", params[1])); | |
4432 | contextInfo.push_back(Pair("fee", ValueFromAmount(nFee))); | |
4433 | ||
4434 | // Contextual transaction we will build on | |
7b92f27e | 4435 | int nextBlockHeight = chainActive.Height() + 1; |
6e9c7629 JG |
4436 | CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction( |
4437 | Params().GetConsensus(), | |
7b92f27e | 4438 | nextBlockHeight); |
6e9c7629 JG |
4439 | bool isShielded = numNotes > 0 || isToZaddr; |
4440 | if (contextualTx.nVersion == 1 && isShielded) { | |
4441 | contextualTx.nVersion = 2; // Tx format should support vjoinsplit | |
4442 | } | |
7b92f27e JG |
4443 | if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) { |
4444 | contextualTx.nExpiryHeight = nextBlockHeight + expiryDelta; | |
4445 | } | |
6e9c7629 JG |
4446 | |
4447 | // Create operation and add to global queue | |
4448 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); | |
4449 | std::shared_ptr<AsyncRPCOperation> operation( | |
4450 | new AsyncRPCOperation_mergetoaddress(contextualTx, utxoInputs, noteInputs, recipient, nFee, contextInfo) ); | |
4451 | q->addOperation(operation); | |
4452 | AsyncRPCOperationId operationId = operation->getId(); | |
4453 | ||
4454 | // Return continuation information | |
4455 | UniValue o(UniValue::VOBJ); | |
4456 | o.push_back(Pair("remainingUTXOs", utxoCounter - numUtxos)); | |
4457 | o.push_back(Pair("remainingTransparentValue", ValueFromAmount(remainingUTXOValue))); | |
4458 | o.push_back(Pair("remainingNotes", noteCounter - numNotes)); | |
4459 | o.push_back(Pair("remainingShieldedValue", ValueFromAmount(remainingNoteValue))); | |
4460 | o.push_back(Pair("mergingUTXOs", numUtxos)); | |
4461 | o.push_back(Pair("mergingTransparentValue", ValueFromAmount(mergedUTXOValue))); | |
4462 | o.push_back(Pair("mergingNotes", numNotes)); | |
4463 | o.push_back(Pair("mergingShieldedValue", ValueFromAmount(mergedNoteValue))); | |
4464 | o.push_back(Pair("opid", operationId)); | |
4465 | return o; | |
4466 | } | |
4467 | ||
4468 | ||
0d37ae3a | 4469 | UniValue z_listoperationids(const UniValue& params, bool fHelp) |
34f0001c S |
4470 | { |
4471 | if (!EnsureWalletIsAvailable(fHelp)) | |
0d37ae3a | 4472 | return NullUniValue; |
34f0001c S |
4473 | |
4474 | if (fHelp || params.size() > 1) | |
4475 | throw runtime_error( | |
4476 | "z_listoperationids\n" | |
4477 | "\nReturns the list of operation ids currently known to the wallet.\n" | |
4478 | "\nArguments:\n" | |
c938fb1f | 4479 | "1. \"status\" (string, optional) Filter result by the operation's state e.g. \"success\".\n" |
34f0001c S |
4480 | "\nResult:\n" |
4481 | "[ (json array of string)\n" | |
4482 | " \"operationid\" (string) an operation id belonging to the wallet\n" | |
4483 | " ,...\n" | |
4484 | "]\n" | |
4485 | "\nExamples:\n" | |
4486 | + HelpExampleCli("z_listoperationids", "") | |
4487 | + HelpExampleRpc("z_listoperationids", "") | |
4488 | ); | |
4489 | ||
4490 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
4491 | ||
4492 | std::string filter; | |
4493 | bool useFilter = false; | |
4494 | if (params.size()==1) { | |
4495 | filter = params[0].get_str(); | |
4496 | useFilter = true; | |
4497 | } | |
4498 | ||
0d37ae3a | 4499 | UniValue ret(UniValue::VARR); |
34f0001c S |
4500 | std::shared_ptr<AsyncRPCQueue> q = getAsyncRPCQueue(); |
4501 | std::vector<AsyncRPCOperationId> ids = q->getAllOperationIds(); | |
4502 | for (auto id : ids) { | |
4503 | std::shared_ptr<AsyncRPCOperation> operation = q->getOperationForId(id); | |
4504 | if (!operation) { | |
4505 | continue; | |
4506 | } | |
4507 | std::string state = operation->getStateAsString(); | |
4508 | if (useFilter && filter.compare(state)!=0) | |
4509 | continue; | |
4510 | ret.push_back(id); | |
4511 | } | |
4512 | ||
4513 | return ret; | |
4514 | } | |
4c62ef37 | 4515 | |
4516 | ||
4517 | #include "script/sign.h" | |
4518 | int32_t decode_hex(uint8_t *bytes,int32_t n,char *hex); | |
4519 | extern std::string NOTARY_PUBKEY; | |
a7fc9554 | 4520 | uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeight,uint256 hash,int32_t n,uint32_t blocktime,uint32_t prevtime,char *destaddr); |
ac531095 | 4521 | int8_t komodo_stakehash(uint256 *hashp,char *address,uint8_t *hashbuf,uint256 txid,int32_t vout); |
1273624d | 4522 | int32_t komodo_segids(uint8_t *hashbuf,int32_t height,int32_t n); |
4c62ef37 | 4523 | |
496f1fd2 | 4524 | int32_t komodo_notaryvin(CMutableTransaction &txNew,uint8_t *notarypub33) |
4525 | { | |
f31815fc | 4526 | set<CBitcoinAddress> setAddress; uint8_t *script,utxosig[128]; uint256 utxotxid; uint64_t utxovalue; int32_t i,siglen=0,nMinDepth = 1,nMaxDepth = 9999999; vector<COutput> vecOutputs; uint32_t utxovout,eligible,earliest = 0; CScript best_scriptPubKey; bool fNegative,fOverflow; |
15000127 | 4527 | bool signSuccess; SignatureData sigdata; uint64_t txfee; uint8_t *ptr; |
4528 | auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); | |
4529 | const CKeyStore& keystore = *pwalletMain; | |
496f1fd2 | 4530 | assert(pwalletMain != NULL); |
4531 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
4532 | utxovalue = 0; | |
4533 | memset(&utxotxid,0,sizeof(utxotxid)); | |
4534 | memset(&utxovout,0,sizeof(utxovout)); | |
4535 | memset(utxosig,0,sizeof(utxosig)); | |
4536 | pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); | |
4537 | BOOST_FOREACH(const COutput& out, vecOutputs) | |
4538 | { | |
4539 | if ( out.nDepth < nMinDepth || out.nDepth > nMaxDepth ) | |
4540 | continue; | |
4541 | if ( setAddress.size() ) | |
4542 | { | |
4543 | CTxDestination address; | |
4544 | if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) | |
4545 | continue; | |
4546 | if (!setAddress.count(address)) | |
4547 | continue; | |
4548 | } | |
4549 | CAmount nValue = out.tx->vout[out.i].nValue; | |
39fefa6b | 4550 | if ( nValue != 10000 ) |
4551 | continue; | |
496f1fd2 | 4552 | const CScript& pk = out.tx->vout[out.i].scriptPubKey; |
4553 | CTxDestination address; | |
4554 | if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) | |
4555 | { | |
4556 | //entry.push_back(Pair("address", CBitcoinAddress(address).ToString())); | |
4557 | //if (pwalletMain->mapAddressBook.count(address)) | |
4558 | // entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name)); | |
4559 | } | |
4560 | script = (uint8_t *)out.tx->vout[out.i].scriptPubKey.data(); | |
4561 | if ( out.tx->vout[out.i].scriptPubKey.size() != 35 || script[0] != 33 || script[34] != OP_CHECKSIG || memcmp(notarypub33,script+1,33) != 0 ) | |
c4eb1abb | 4562 | { |
137e08d7 | 4563 | //fprintf(stderr,"scriptsize.%d [0] %02x\n",(int32_t)out.tx->vout[out.i].scriptPubKey.size(),script[0]); |
496f1fd2 | 4564 | continue; |
c4eb1abb | 4565 | } |
496f1fd2 | 4566 | utxovalue = (uint64_t)nValue; |
15000127 | 4567 | //decode_hex((uint8_t *)&utxotxid,32,(char *)out.tx->GetHash().GetHex().c_str()); |
4568 | utxotxid = out.tx->GetHash(); | |
496f1fd2 | 4569 | utxovout = out.i; |
ff1ee86e | 4570 | best_scriptPubKey = out.tx->vout[out.i].scriptPubKey; |
15000127 | 4571 | //fprintf(stderr,"check %s/v%d %llu\n",(char *)utxotxid.GetHex().c_str(),utxovout,(long long)utxovalue); |
fd2fd9a7 | 4572 | |
496f1fd2 | 4573 | txNew.vin.resize(1); |
4574 | txNew.vout.resize(1); | |
2ad15573 | 4575 | txfee = utxovalue / 2; |
3566cdf6 | 4576 | //for (i=0; i<32; i++) |
4577 | // ((uint8_t *)&revtxid)[i] = ((uint8_t *)&utxotxid)[31 - i]; | |
4578 | txNew.vin[0].prevout.hash = utxotxid; //revtxid; | |
496f1fd2 | 4579 | txNew.vin[0].prevout.n = utxovout; |
221cc791 | 4580 | txNew.vout[0].scriptPubKey = CScript() << ParseHex(CRYPTO777_PUBSECPSTR) << OP_CHECKSIG; |
496f1fd2 | 4581 | txNew.vout[0].nValue = utxovalue - txfee; |
4582 | CTransaction txNewConst(txNew); | |
4583 | signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, utxovalue, SIGHASH_ALL), best_scriptPubKey, sigdata, consensusBranchId); | |
4584 | if (!signSuccess) | |
4585 | fprintf(stderr,"notaryvin failed to create signature\n"); | |
4586 | else | |
4587 | { | |
4588 | UpdateTransaction(txNew,0,sigdata); | |
4589 | ptr = (uint8_t *)sigdata.scriptSig.data(); | |
4590 | siglen = sigdata.scriptSig.size(); | |
4591 | for (i=0; i<siglen; i++) | |
db4b45a6 | 4592 | utxosig[i] = ptr[i];//, fprintf(stderr,"%02x",ptr[i]); |
4593 | //fprintf(stderr," siglen.%d notaryvin %s/v%d\n",siglen,utxotxid.GetHex().c_str(),utxovout); | |
ff1ee86e | 4594 | break; |
496f1fd2 | 4595 | } |
4596 | } | |
4597 | return(siglen); | |
4598 | } | |
4599 | ||
ac531095 | 4600 | struct komodo_staking |
4601 | { | |
4602 | char address[64]; | |
4603 | uint256 txid; | |
4604 | arith_uint256 hashval; | |
4605 | uint64_t nValue; | |
1273624d | 4606 | uint32_t segid32,txtime; |
ac531095 | 4607 | int32_t vout; |
1273624d | 4608 | CScript scriptPubKey; |
ac531095 | 4609 | }; |
4610 | ||
1273624d | 4611 | struct komodo_staking *komodo_addutxo(struct komodo_staking *array,int32_t *numkp,int32_t *maxkp,uint32_t txtime,uint64_t nValue,uint256 txid,int32_t vout,char *address,uint8_t *hashbuf,CScript pk) |
ac531095 | 4612 | { |
4613 | uint256 hash; uint32_t segid32; struct komodo_staking *kp; | |
4614 | segid32 = komodo_stakehash(&hash,address,hashbuf,txid,vout); | |
1273624d | 4615 | if ( *numkp >= *maxkp ) |
4616 | { | |
4617 | *maxkp += 1000; | |
4618 | array = (struct komodo_staking *)realloc(array,sizeof(*array) * (*maxkp)); | |
4619 | } | |
4620 | kp = &array[(*numkp)++]; | |
4621 | memset(kp,0,sizeof(*kp)); | |
ac531095 | 4622 | strcpy(kp->address,address); |
4623 | kp->txid = txid; | |
4624 | kp->vout = vout; | |
4625 | kp->hashval = UintToArith256(hash); | |
4626 | kp->txtime = txtime; | |
4627 | kp->segid32 = segid32; | |
4628 | kp->nValue = nValue; | |
4629 | kp->scriptPubKey = pk; | |
4630 | return(array); | |
4631 | } | |
4632 | ||
4633 | arith_uint256 _komodo_eligible(struct komodo_staking *kp,arith_uint256 ratio,uint32_t blocktime,int32_t iter,int32_t minage,int32_t segid,int32_t nHeight,uint32_t prevtime) | |
4634 | { | |
4635 | int32_t diff; uint64_t coinage; arith_uint256 coinage256,hashval; | |
4636 | diff = (iter + blocktime - kp->txtime - minage); | |
4637 | if ( diff < 0 ) | |
4638 | diff = 60; | |
4639 | else if ( diff > 3600*24*30 ) | |
4640 | diff = 3600*24*30; | |
4641 | if ( iter > 0 ) | |
4642 | diff += segid*2; | |
4643 | coinage = ((uint64_t)kp->nValue/COIN * diff); | |
0ce374b7 | 4644 | if ( blocktime+iter+segid*2 > prevtime+480 ) |
4645 | coinage *= ((blocktime+iter+segid*2) - (prevtime+400)); | |
4646 | //if ( nHeight >= 2500 && blocktime+iter+segid*2 > prevtime+180 ) | |
4647 | // coinage *= ((blocktime+iter+segid*2) - (prevtime+60)); | |
ac531095 | 4648 | coinage256 = arith_uint256(coinage+1); |
4649 | hashval = ratio * (kp->hashval / coinage256); | |
0ce374b7 | 4650 | //if ( nHeight >= 900 && nHeight < 916 ) |
4651 | // hashval = (hashval / coinage256); | |
ac531095 | 4652 | return(hashval); |
4653 | } | |
4654 | ||
cca0bdc6 | 4655 | uint32_t komodo_eligible(arith_uint256 bnTarget,arith_uint256 ratio,struct komodo_staking *kp,int32_t nHeight,uint32_t blocktime,uint32_t prevtime,int32_t minage,uint8_t *hashbuf) |
ac531095 | 4656 | { |
8e1ca1ad | 4657 | int32_t maxiters = 600; uint256 hash; |
ac531095 | 4658 | int32_t segid,iter,diff; uint64_t coinage; arith_uint256 hashval,coinage256; |
cca0bdc6 | 4659 | komodo_stakehash(&hash,kp->address,hashbuf,kp->txid,kp->vout); |
4660 | kp->hashval = UintToArith256(hash); | |
ac531095 | 4661 | segid = ((nHeight + kp->segid32) & 0x3f); |
4662 | hashval = _komodo_eligible(kp,ratio,blocktime,maxiters,minage,segid,nHeight,prevtime); | |
d3ce166f | 4663 | //for (int i=32; i>=0; i--) |
4664 | // fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]); | |
4665 | //fprintf(stderr," b.%u minage.%d segid.%d ht.%d prev.%u\n",blocktime,minage,segid,nHeight,prevtime); | |
ac531095 | 4666 | if ( hashval <= bnTarget ) |
4667 | { | |
4668 | for (iter=0; iter<maxiters; iter++) | |
4669 | { | |
4670 | if ( blocktime+iter+segid*2 < kp->txtime+minage ) | |
4671 | continue; | |
37d6294d | 4672 | hashval = _komodo_eligible(kp,ratio,blocktime,iter,minage,segid,nHeight,prevtime); |
ac531095 | 4673 | if ( hashval <= bnTarget ) |
4674 | { | |
1973dfa0 | 4675 | //fprintf(stderr,"winner %.8f blocktime.%u iter.%d segid.%d\n",(double)kp->nValue/COIN,blocktime,iter,segid); |
ac531095 | 4676 | blocktime += iter; |
4677 | blocktime += segid * 2; | |
4678 | return(blocktime); | |
4679 | } | |
4680 | } | |
4681 | } | |
4682 | return(0); | |
4683 | } | |
4684 | ||
d231a6a7 | 4685 | int32_t komodo_staked(CMutableTransaction &txNew,uint32_t nBits,uint32_t *blocktimep,uint32_t *txtimep,uint256 *utxotxidp,int32_t *utxovoutp,uint64_t *utxovaluep,uint8_t *utxosig) |
4c62ef37 | 4686 | { |
ac531095 | 4687 | static struct komodo_staking *array; static int32_t numkp,maxkp; static uint32_t lasttime; |
17839698 | 4688 | set<CBitcoinAddress> setAddress; struct komodo_staking *kp; int32_t winners,segid,minage,nHeight,counter=0,i,m,siglen=0,nMinDepth = 1,nMaxDepth = 99999999; vector<COutput> vecOutputs; uint32_t block_from_future_rejecttime,besttime,eligible,eligible2,earliest = 0; CScript best_scriptPubKey; arith_uint256 mindiff,ratio,bnTarget; CBlockIndex *tipindex,*pindex; CTxDestination address; bool fNegative,fOverflow; uint8_t hashbuf[256]; CTransaction tx; uint256 hashBlock; |
357e4ca5 | 4689 | bnTarget.SetCompact(nBits, &fNegative, &fOverflow); |
ac531095 | 4690 | mindiff.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow); |
4691 | ratio = (mindiff / bnTarget); | |
4c62ef37 | 4692 | assert(pwalletMain != NULL); |
b8b06863 | 4693 | LOCK2(cs_main, pwalletMain->cs_wallet); |
4c62ef37 | 4694 | *utxovaluep = 0; |
4695 | memset(utxotxidp,0,sizeof(*utxotxidp)); | |
4696 | memset(utxovoutp,0,sizeof(*utxovoutp)); | |
4697 | memset(utxosig,0,72); | |
4698 | pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); | |
55dd6622 | 4699 | if ( (tipindex= chainActive.Tip()) == 0 ) |
4700 | return(0); | |
ac531095 | 4701 | nHeight = tipindex->nHeight + 1; |
4702 | if ( (minage= nHeight*3) > 6000 ) // about 100 blocks | |
4703 | minage = 6000; | |
4704 | komodo_segids(hashbuf,nHeight-101,100); | |
6e7dcf31 | 4705 | if ( *blocktimep > tipindex->nTime+60 ) |
4706 | *blocktimep = tipindex->nTime+60; | |
2d481115 | 4707 | //fprintf(stderr,"Start scan of utxo for staking %u ht.%d\n",(uint32_t)time(NULL),nHeight); |
1973dfa0 | 4708 | if ( time(NULL) > lasttime+600 ) |
4c62ef37 | 4709 | { |
ac531095 | 4710 | if ( array != 0 ) |
2e4cc2a2 | 4711 | { |
ac531095 | 4712 | free(array); |
4713 | array = 0; | |
4714 | maxkp = numkp = 0; | |
817aedba | 4715 | lasttime = 0; |
2e4cc2a2 | 4716 | } |
ac531095 | 4717 | BOOST_FOREACH(const COutput& out, vecOutputs) |
4c62ef37 | 4718 | { |
ac531095 | 4719 | if ( (tipindex= chainActive.Tip()) == 0 || tipindex->nHeight+1 > nHeight ) |
2e4cc2a2 | 4720 | { |
ac531095 | 4721 | fprintf(stderr,"chain tip changed during staking loop t.%u counter.%d\n",(uint32_t)time(NULL),counter); |
4722 | return(0); | |
2e4cc2a2 | 4723 | } |
ac531095 | 4724 | counter++; |
4725 | if ( out.nDepth < nMinDepth || out.nDepth > nMaxDepth ) | |
2e4cc2a2 | 4726 | { |
ac531095 | 4727 | //fprintf(stderr,"komodo_staked invalid depth %d\n",(int32_t)out.nDepth); |
4c62ef37 | 4728 | continue; |
2e4cc2a2 | 4729 | } |
ac531095 | 4730 | CAmount nValue = out.tx->vout[out.i].nValue; |
4731 | if ( nValue < COIN || !out.fSpendable ) | |
55dd6622 | 4732 | continue; |
ac531095 | 4733 | const CScript& pk = out.tx->vout[out.i].scriptPubKey; |
4734 | if ( ExtractDestination(pk,address) != 0 ) | |
4c62ef37 | 4735 | { |
ac531095 | 4736 | if ( IsMine(*pwalletMain,address) == 0 ) |
4737 | continue; | |
aecf5859 | 4738 | if ( GetTransaction(out.tx->GetHash(),tx,hashBlock,true) != 0 && (pindex= mapBlockIndex[hashBlock]) != 0 ) |
7b6d6985 | 4739 | { |
1273624d | 4740 | array = komodo_addutxo(array,&numkp,&maxkp,(uint32_t)pindex->nTime,(uint64_t)nValue,out.tx->GetHash(),out.i,(char *)CBitcoinAddress(address).ToString().c_str(),hashbuf,(CScript)pk); |
ac531095 | 4741 | } |
4742 | } | |
4743 | } | |
4744 | lasttime = (uint32_t)time(NULL); | |
8cbf8d6a | 4745 | //fprintf(stderr,"finished kp data of utxo for staking %u ht.%d numkp.%d maxkp.%d\n",(uint32_t)time(NULL),nHeight,numkp,maxkp); |
ac531095 | 4746 | } |
17839698 | 4747 | block_from_future_rejecttime = (uint32_t)GetAdjustedTime() + 57; |
2d481115 | 4748 | for (i=winners=0; i<numkp; i++) |
ac531095 | 4749 | { |
817aedba | 4750 | if ( (tipindex= chainActive.Tip()) == 0 || tipindex->nHeight+1 > nHeight ) |
4751 | { | |
4752 | fprintf(stderr,"chain tip changed during staking loop t.%u counter.%d\n",(uint32_t)time(NULL),counter); | |
4753 | return(0); | |
4754 | } | |
ac531095 | 4755 | kp = &array[i]; |
cca0bdc6 | 4756 | if ( (eligible2= komodo_eligible(bnTarget,ratio,kp,nHeight,*blocktimep,(uint32_t)tipindex->nTime+27,minage,hashbuf)) == 0 ) |
76df25be | 4757 | continue; |
ecae680b | 4758 | eligible = komodo_stake(0,bnTarget,nHeight,kp->txid,kp->vout,0,(uint32_t)tipindex->nTime+27,kp->address); |
1973dfa0 | 4759 | //fprintf(stderr,"i.%d %u vs %u\n",i,eligible2,eligible); |
ac531095 | 4760 | if ( eligible > 0 ) |
4761 | { | |
4762 | besttime = m = 0; | |
4763 | if ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+27,kp->address) ) | |
4764 | { | |
4765 | while ( eligible == komodo_stake(1,bnTarget,nHeight,kp->txid,kp->vout,eligible,(uint32_t)tipindex->nTime+27,kp->address) ) | |
e005f576 | 4766 | { |
ac531095 | 4767 | besttime = eligible; |
4768 | eligible--; | |
17839698 | 4769 | if ( eligible < block_from_future_rejecttime ) // nothing gained by going earlier |
aa2ef3ab | 4770 | break; |
ac531095 | 4771 | m++; |
011b0a24 | 4772 | //fprintf(stderr,"m.%d ht.%d validated winning blocktime %u -> %.8f eligible.%u test prior\n",m,nHeight,*blocktimep,(double)kp->nValue/COIN,eligible); |
e005f576 | 4773 | } |
ac531095 | 4774 | } |
4775 | else | |
4776 | { | |
aecf5859 | 4777 | fprintf(stderr,"ht.%d error validating winning blocktime %u -> %.8f eligible.%u test prior\n",nHeight,*blocktimep,(double)kp->nValue/COIN,eligible); |
ac531095 | 4778 | continue; |
4779 | } | |
4780 | eligible = besttime; | |
2d481115 | 4781 | winners++; |
4782 | //fprintf(stderr,"ht.%d validated winning [%d] -> %.8f eligible.%u test prior\n",nHeight,(int32_t)(eligible - tipindex->nTime),(double)kp->nValue/COIN,eligible); | |
aecf5859 | 4783 | if ( earliest == 0 || eligible < earliest || (eligible == earliest && (*utxovaluep == 0 || kp->nValue < *utxovaluep)) ) |
ac531095 | 4784 | { |
4785 | earliest = eligible; | |
4786 | best_scriptPubKey = kp->scriptPubKey; //out.tx->vout[out.i].scriptPubKey; | |
4787 | *utxovaluep = (uint64_t)kp->nValue; | |
4788 | //decode_hex((uint8_t *)utxotxidp,32,(char *)out.tx->GetHash().GetHex().c_str()); | |
4789 | decode_hex((uint8_t *)utxotxidp,32,(char *)kp->txid.GetHex().c_str()); | |
4790 | *utxovoutp = kp->vout; | |
4791 | *txtimep = kp->txtime;//(uint32_t)out.tx->nLockTime; | |
779b7b3a | 4792 | fprintf(stderr,"ht.%d earliest.%u [%d].%d (%s) nValue %.8f locktime.%u counter.%d winners.%d\n",nHeight,earliest,(int32_t)(earliest - tipindex->nTime),m,kp->address,(double)kp->nValue/COIN,*txtimep,counter,winners); |
ac531095 | 4793 | } |
4794 | } //else fprintf(stderr,"utxo not eligible\n"); | |
4795 | } //else fprintf(stderr,"no tipindex\n"); | |
2d481115 | 4796 | if ( numkp < 10000 && array != 0 ) |
011b0a24 | 4797 | { |
4798 | free(array); | |
4799 | array = 0; | |
4800 | maxkp = numkp = 0; | |
817aedba | 4801 | lasttime = 0; |
011b0a24 | 4802 | } |
8c218b48 | 4803 | if ( earliest != 0 ) |
4804 | { | |
4805 | bool signSuccess; SignatureData sigdata; uint64_t txfee; uint8_t *ptr; uint256 revtxid,utxotxid; | |
4806 | auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); | |
4807 | const CKeyStore& keystore = *pwalletMain; | |
8c218b48 | 4808 | txNew.vin.resize(1); |
4809 | txNew.vout.resize(1); | |
4810 | txfee = 0; | |
4811 | for (i=0; i<32; i++) | |
4812 | ((uint8_t *)&revtxid)[i] = ((uint8_t *)utxotxidp)[31 - i]; | |
4813 | txNew.vin[0].prevout.hash = revtxid; | |
4814 | txNew.vin[0].prevout.n = *utxovoutp; | |
4d068367 | 4815 | txNew.vout[0].scriptPubKey = best_scriptPubKey;// CScript() << ParseHex(NOTARY_PUBKEY) << OP_CHECKSIG; |
3c3e04c4 | 4816 | txNew.vout[0].nValue = *utxovaluep - txfee; |
09cde8ab | 4817 | txNew.nLockTime = earliest; |
8c218b48 | 4818 | CTransaction txNewConst(txNew); |
3c3e04c4 | 4819 | signSuccess = ProduceSignature(TransactionSignatureCreator(&keystore, &txNewConst, 0, *utxovaluep, SIGHASH_ALL), best_scriptPubKey, sigdata, consensusBranchId); |
8c218b48 | 4820 | if (!signSuccess) |
4821 | fprintf(stderr,"failed to create signature\n"); | |
4822 | else | |
4c62ef37 | 4823 | { |
d231a6a7 | 4824 | UpdateTransaction(txNew,0,sigdata); |
8c218b48 | 4825 | ptr = (uint8_t *)sigdata.scriptSig.data(); |
4826 | siglen = sigdata.scriptSig.size(); | |
4827 | for (i=0; i<siglen; i++) | |
4828 | utxosig[i] = ptr[i];//, fprintf(stderr,"%02x",ptr[i]); | |
4829 | //fprintf(stderr," siglen.%d\n",siglen); | |
34bcc309 | 4830 | //fprintf(stderr,"best %u from %u, gap %d lag.%d\n",earliest,*blocktimep,(int32_t)(earliest - *blocktimep),(int32_t)(time(NULL) - *blocktimep)); |
09cde8ab | 4831 | *blocktimep = earliest; |
4c62ef37 | 4832 | } |
2b72340d | 4833 | } //else fprintf(stderr,"no earliest utxo for staking\n"); |
8cbf8d6a | 4834 | //fprintf(stderr,"end scan of utxo for staking t.%u counter.%d numkp.%d winners.%d\n",(uint32_t)time(NULL),counter,numkp,winners); |
4c62ef37 | 4835 | return(siglen); |
4836 | } | |
4f02fc40 | 4837 | |
e10def86 | 4838 | int32_t ensure_CCrequirements() |
4839 | { | |
efa4ed1f | 4840 | extern uint8_t NOTARY_PUBKEY33[]; |
eafcb941 | 4841 | CCerror = ""; |
e10def86 | 4842 | if ( NOTARY_PUBKEY33[0] == 0 ) |
4843 | return(-1); | |
4844 | else if ( GetBoolArg("-addressindex", DEFAULT_ADDRESSINDEX) == 0 ) | |
4845 | return(-1); | |
6deb8c09 | 4846 | else if ( GetBoolArg("-spentindex", DEFAULT_SPENTINDEX) == 0 ) |
4847 | return(-1); | |
e10def86 | 4848 | else return(0); |
4849 | } | |
4850 | ||
7c766994 | 4851 | #include "../cc/CCfaucet.h" |
4852 | #include "../cc/CCassets.h" | |
287efad4 | 4853 | #include "../cc/CCrewards.h" |
cfea7a46 | 4854 | #include "../cc/CCdice.h" |
7137a022 | 4855 | #include "../cc/CCfsm.h" |
eca34fd9 | 4856 | #include "../cc/CCauction.h" |
4857 | #include "../cc/CClotto.h" | |
810f6366 | 4858 | #include "../cc/CCchannels.h" |
3d6ee3e0 | 4859 | #include "../cc/CCOracles.h" |
98a1f520 | 4860 | #include "../cc/CCGateways.h" |
e37d99ce | 4861 | |
cfea7a46 | 4862 | UniValue CCaddress(struct CCcontract_info *cp,char *name,std::vector<unsigned char> &pubkey) |
e37d99ce | 4863 | { |
b3965baa | 4864 | UniValue result(UniValue::VOBJ); char destaddr[64],str[64]; CPubKey pk; |
4865 | pk = GetUnspendable(cp,0); | |
4866 | GetCCaddress(cp,destaddr,pk); | |
4867 | if ( strcmp(destaddr,cp->unspendableCCaddr) != 0 ) | |
4868 | { | |
4869 | uint8_t priv[32]; | |
aed3f987 | 4870 | Myprivkey(priv); // it is assumed the CC's normal address'es -pubkey was used |
b3965baa | 4871 | fprintf(stderr,"fix mismatched CCaddr %s -> %s\n",cp->unspendableCCaddr,destaddr); |
4872 | strcpy(cp->unspendableCCaddr,destaddr); | |
4873 | } | |
e37d99ce | 4874 | result.push_back(Pair("result", "success")); |
cfea7a46 | 4875 | sprintf(str,"%sCCaddress",name); |
1702dcef | 4876 | result.push_back(Pair(str,cp->unspendableCCaddr)); |
4877 | sprintf(str,"%smarker",name); | |
4878 | result.push_back(Pair(str,cp->normaladdr)); | |
3515c101 | 4879 | if ( _GetCCaddress(destaddr,EVAL_ASSETS,pubkey2pk(pubkey)) > 0 ) |
4880 | { | |
4881 | sprintf(str,"%sCCassets",name); | |
4882 | result.push_back(Pair(str,destaddr)); | |
4883 | } | |
cfea7a46 | 4884 | if ( pubkey.size() == 33 ) |
e37d99ce | 4885 | { |
e04b5c08 | 4886 | if ( GetCCaddress(cp,destaddr,pubkey2pk(pubkey)) != 0 ) |
e37d99ce | 4887 | result.push_back(Pair("CCaddress",destaddr)); |
8bbfc238 | 4888 | } |
e04b5c08 | 4889 | if ( GetCCaddress(cp,destaddr,pubkey2pk(Mypubkey())) != 0 ) |
e37d99ce | 4890 | result.push_back(Pair("myCCaddress",destaddr)); |
e09492b4 | 4891 | if ( Getscriptaddress(destaddr,(CScript() << Mypubkey() << OP_CHECKSIG)) != 0 ) |
939cd4b6 | 4892 | result.push_back(Pair("myaddress",destaddr)); |
e37d99ce | 4893 | return(result); |
4894 | } | |
4895 | ||
810f6366 | 4896 | UniValue channelsaddress(const UniValue& params, bool fHelp) |
4897 | { | |
4898 | UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::vector<unsigned char> destpubkey; CPubKey pk,pk2; char destaddr[64]; | |
4899 | cp = CCinit(&C,EVAL_CHANNELS); | |
4900 | if ( fHelp || params.size() != 1 ) | |
4901 | throw runtime_error("channelsaddress destpubkey\n"); | |
4902 | if ( ensure_CCrequirements() < 0 ) | |
4903 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
4904 | destpubkey = ParseHex(params[0].get_str().c_str()); | |
4905 | pk = pubkey2pk(Mypubkey()); | |
4906 | pk2 = pubkey2pk(destpubkey); | |
cafa63fb | 4907 | result = CCaddress(cp,(char *)"Channels",destpubkey); |
810f6366 | 4908 | result.push_back(Pair("otherpubkey", params[0].get_str())); |
4909 | GetCCaddress1of2(cp,destaddr,pk,pk2); | |
54690bb0 | 4910 | result.push_back(Pair("channeladdress",destaddr)); |
810f6366 | 4911 | return(result); |
4912 | } | |
4913 | ||
c926780f | 4914 | UniValue oraclesaddress(const UniValue& params, bool fHelp) |
4915 | { | |
4916 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
4917 | cp = CCinit(&C,EVAL_ORACLES); | |
4918 | if ( fHelp || params.size() > 1 ) | |
4919 | throw runtime_error("oraclesaddress [pubkey]\n"); | |
4920 | if ( ensure_CCrequirements() < 0 ) | |
4921 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
4922 | if ( params.size() == 1 ) | |
4923 | pubkey = ParseHex(params[0].get_str().c_str()); | |
4924 | return(CCaddress(cp,(char *)"Oracles",pubkey)); | |
4925 | } | |
4926 | ||
4927 | UniValue pricesaddress(const UniValue& params, bool fHelp) | |
4928 | { | |
4929 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
4930 | cp = CCinit(&C,EVAL_PRICES); | |
4931 | if ( fHelp || params.size() > 1 ) | |
4932 | throw runtime_error("pricesaddress [pubkey]\n"); | |
4933 | if ( ensure_CCrequirements() < 0 ) | |
4934 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
4935 | if ( params.size() == 1 ) | |
4936 | pubkey = ParseHex(params[0].get_str().c_str()); | |
4937 | return(CCaddress(cp,(char *)"Prices",pubkey)); | |
4938 | } | |
4939 | ||
4940 | UniValue pegsaddress(const UniValue& params, bool fHelp) | |
4941 | { | |
4942 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
4943 | cp = CCinit(&C,EVAL_PEGS); | |
4944 | if ( fHelp || params.size() > 1 ) | |
4945 | throw runtime_error("pegssaddress [pubkey]\n"); | |
4946 | if ( ensure_CCrequirements() < 0 ) | |
4947 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
4948 | if ( params.size() == 1 ) | |
4949 | pubkey = ParseHex(params[0].get_str().c_str()); | |
4950 | return(CCaddress(cp,(char *)"Pegs",pubkey)); | |
4951 | } | |
4952 | ||
4953 | UniValue triggersaddress(const UniValue& params, bool fHelp) | |
4954 | { | |
4955 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
4956 | cp = CCinit(&C,EVAL_TRIGGERS); | |
4957 | if ( fHelp || params.size() > 1 ) | |
4958 | throw runtime_error("triggersaddress [pubkey]\n"); | |
4959 | if ( ensure_CCrequirements() < 0 ) | |
4960 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
4961 | if ( params.size() == 1 ) | |
4962 | pubkey = ParseHex(params[0].get_str().c_str()); | |
4963 | return(CCaddress(cp,(char *)"Triggers",pubkey)); | |
4964 | } | |
4965 | ||
4966 | UniValue paymentsaddress(const UniValue& params, bool fHelp) | |
4967 | { | |
4968 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
4969 | cp = CCinit(&C,EVAL_PAYMENTS); | |
4970 | if ( fHelp || params.size() > 1 ) | |
4971 | throw runtime_error("paymentsaddress [pubkey]\n"); | |
4972 | if ( ensure_CCrequirements() < 0 ) | |
4973 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
4974 | if ( params.size() == 1 ) | |
4975 | pubkey = ParseHex(params[0].get_str().c_str()); | |
4976 | return(CCaddress(cp,(char *)"Payments",pubkey)); | |
4977 | } | |
4978 | ||
4979 | UniValue gatewaysaddress(const UniValue& params, bool fHelp) | |
4980 | { | |
4981 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
4982 | cp = CCinit(&C,EVAL_GATEWAYS); | |
4983 | if ( fHelp || params.size() > 1 ) | |
4984 | throw runtime_error("gatewaysaddress [pubkey]\n"); | |
4985 | if ( ensure_CCrequirements() < 0 ) | |
4986 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
4987 | if ( params.size() == 1 ) | |
4988 | pubkey = ParseHex(params[0].get_str().c_str()); | |
4989 | return(CCaddress(cp,(char *)"Gateways",pubkey)); | |
4990 | } | |
4991 | ||
da629dfe | 4992 | UniValue mofnaddress(const UniValue& params, bool fHelp) |
4993 | { | |
4994 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
4995 | cp = CCinit(&C,EVAL_MOFN); | |
4996 | if ( fHelp || params.size() > 1 ) | |
4997 | throw runtime_error("mofnaddress [pubkey]\n"); | |
4998 | if ( ensure_CCrequirements() < 0 ) | |
4999 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5000 | if ( params.size() == 1 ) | |
5001 | pubkey = ParseHex(params[0].get_str().c_str()); | |
b3965baa | 5002 | return(CCaddress(cp,(char *)"MofN",pubkey)); |
da629dfe | 5003 | } |
5004 | ||
eca34fd9 | 5005 | UniValue lottoaddress(const UniValue& params, bool fHelp) |
5006 | { | |
5007 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
5008 | cp = CCinit(&C,EVAL_LOTTO); | |
5009 | if ( fHelp || params.size() > 1 ) | |
5010 | throw runtime_error("lottoaddress [pubkey]\n"); | |
5011 | if ( ensure_CCrequirements() < 0 ) | |
5012 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5013 | if ( params.size() == 1 ) | |
5014 | pubkey = ParseHex(params[0].get_str().c_str()); | |
5015 | return(CCaddress(cp,(char *)"Lotto",pubkey)); | |
5016 | } | |
5017 | ||
7137a022 | 5018 | UniValue FSMaddress(const UniValue& params, bool fHelp) |
eca34fd9 | 5019 | { |
5020 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
7137a022 | 5021 | cp = CCinit(&C,EVAL_FSM); |
eca34fd9 | 5022 | if ( fHelp || params.size() > 1 ) |
7137a022 | 5023 | throw runtime_error("FSMaddress [pubkey]\n"); |
eca34fd9 | 5024 | if ( ensure_CCrequirements() < 0 ) |
5025 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5026 | if ( params.size() == 1 ) | |
5027 | pubkey = ParseHex(params[0].get_str().c_str()); | |
7137a022 | 5028 | return(CCaddress(cp,(char *)"FSM",pubkey)); |
eca34fd9 | 5029 | } |
5030 | ||
5031 | UniValue auctionaddress(const UniValue& params, bool fHelp) | |
5032 | { | |
5033 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
b935ab65 | 5034 | cp = CCinit(&C,EVAL_AUCTION); |
eca34fd9 | 5035 | if ( fHelp || params.size() > 1 ) |
5036 | throw runtime_error("auctionaddress [pubkey]\n"); | |
5037 | if ( ensure_CCrequirements() < 0 ) | |
5038 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5039 | if ( params.size() == 1 ) | |
5040 | pubkey = ParseHex(params[0].get_str().c_str()); | |
5041 | return(CCaddress(cp,(char *)"Auction",pubkey)); | |
5042 | } | |
5043 | ||
cfea7a46 | 5044 | UniValue diceaddress(const UniValue& params, bool fHelp) |
5045 | { | |
5046 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
b2b7d05b | 5047 | cp = CCinit(&C,EVAL_DICE); |
cfea7a46 | 5048 | if ( fHelp || params.size() > 1 ) |
5049 | throw runtime_error("diceaddress [pubkey]\n"); | |
5050 | if ( ensure_CCrequirements() < 0 ) | |
5051 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5052 | if ( params.size() == 1 ) | |
5053 | pubkey = ParseHex(params[0].get_str().c_str()); | |
f0f5f6c0 | 5054 | return(CCaddress(cp,(char *)"Dice",pubkey)); |
cfea7a46 | 5055 | } |
5056 | ||
5057 | UniValue faucetaddress(const UniValue& params, bool fHelp) | |
5058 | { | |
5059 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
63583429 | 5060 | int errno; |
b2b7d05b | 5061 | cp = CCinit(&C,EVAL_FAUCET); |
cfea7a46 | 5062 | if ( fHelp || params.size() > 1 ) |
5063 | throw runtime_error("faucetaddress [pubkey]\n"); | |
63583429 JDL |
5064 | errno = ensure_CCrequirements(); |
5065 | if ( errno < 0 ) | |
5066 | throw runtime_error(strprintf("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet. ERR=%d\n", errno)); | |
cfea7a46 | 5067 | if ( params.size() == 1 ) |
5068 | pubkey = ParseHex(params[0].get_str().c_str()); | |
f0f5f6c0 | 5069 | return(CCaddress(cp,(char *)"Faucet",pubkey)); |
cfea7a46 | 5070 | } |
5071 | ||
5072 | UniValue rewardsaddress(const UniValue& params, bool fHelp) | |
5073 | { | |
5074 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
5075 | cp = CCinit(&C,EVAL_REWARDS); | |
5076 | if ( fHelp || params.size() > 1 ) | |
5077 | throw runtime_error("rewardsaddress [pubkey]\n"); | |
5078 | if ( ensure_CCrequirements() < 0 ) | |
5079 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5080 | if ( params.size() == 1 ) | |
5081 | pubkey = ParseHex(params[0].get_str().c_str()); | |
f0f5f6c0 | 5082 | return(CCaddress(cp,(char *)"Rewards",pubkey)); |
cfea7a46 | 5083 | } |
5084 | ||
5085 | UniValue tokenaddress(const UniValue& params, bool fHelp) | |
5086 | { | |
5087 | struct CCcontract_info *cp,C; std::vector<unsigned char> pubkey; | |
b2b7d05b | 5088 | cp = CCinit(&C,EVAL_ASSETS); |
cfea7a46 | 5089 | if ( fHelp || params.size() > 1 ) |
5090 | throw runtime_error("tokenaddress [pubkey]\n"); | |
5091 | if ( ensure_CCrequirements() < 0 ) | |
5092 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5093 | if ( params.size() == 1 ) | |
5094 | pubkey = ParseHex(params[0].get_str().c_str()); | |
f0f5f6c0 | 5095 | return(CCaddress(cp,(char *)"Assets",pubkey)); |
cfea7a46 | 5096 | } |
5097 | ||
54690bb0 | 5098 | UniValue channelsinfo(const UniValue& params, bool fHelp) |
5099 | { | |
5100 | if ( fHelp || params.size() != 0 ) | |
5101 | throw runtime_error("channelsinfo\n"); | |
5102 | if ( ensure_CCrequirements() < 0 ) | |
5103 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5104 | return(ChannelsInfo()); | |
5105 | } | |
5106 | ||
810f6366 | 5107 | UniValue channelsopen(const UniValue& params, bool fHelp) |
5108 | { | |
02da4225 | 5109 | UniValue result(UniValue::VOBJ); int32_t numpayments; int64_t payment; std::vector<unsigned char> destpub; struct CCcontract_info *cp,C; std::string hex; |
810f6366 | 5110 | cp = CCinit(&C,EVAL_CHANNELS); |
5111 | if ( fHelp || params.size() != 3 ) | |
5112 | throw runtime_error("channelsopen destpubkey numpayments payment\n"); | |
5113 | if ( ensure_CCrequirements() < 0 ) | |
5114 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5115 | LOCK(cs_main); | |
5116 | destpub = ParseHex(params[0].get_str().c_str()); | |
5117 | numpayments = atoi(params[1].get_str().c_str()); | |
11020cf2 | 5118 | payment = atol(params[2].get_str().c_str()); |
810f6366 | 5119 | hex = ChannelOpen(0,pubkey2pk(destpub),numpayments,payment); |
5120 | if ( hex.size() > 0 ) | |
5121 | { | |
5122 | result.push_back(Pair("result", "success")); | |
5123 | result.push_back(Pair("hex", hex)); | |
5124 | } else ERR_RESULT("couldnt create channelsopen transaction"); | |
5125 | return(result); | |
5126 | } | |
5127 | ||
02da4225 | 5128 | UniValue channelsstop(const UniValue& params, bool fHelp) |
5129 | { | |
5130 | UniValue result(UniValue::VOBJ); std::vector<unsigned char> destpub; struct CCcontract_info *cp,C; std::string hex; uint256 origtxid; | |
5131 | cp = CCinit(&C,EVAL_CHANNELS); | |
5132 | if ( fHelp || params.size() != 2 ) | |
5133 | throw runtime_error("channelsstop destpubkey origtxid\n"); | |
5134 | if ( ensure_CCrequirements() < 0 ) | |
5135 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5136 | LOCK(cs_main); | |
5137 | destpub = ParseHex(params[0].get_str().c_str()); | |
5138 | origtxid = Parseuint256((char *)params[1].get_str().c_str()); | |
5139 | hex = ChannelStop(0,pubkey2pk(destpub),origtxid); | |
5140 | if ( hex.size() > 0 ) | |
5141 | { | |
5142 | result.push_back(Pair("result", "success")); | |
5143 | result.push_back(Pair("hex", hex)); | |
5144 | } else ERR_RESULT("couldnt create channelsstop transaction"); | |
5145 | return(result); | |
5146 | } | |
5147 | ||
5148 | UniValue channelspayment(const UniValue& params, bool fHelp) | |
5149 | { | |
5150 | UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::string hex; uint256 origtxid,prevtxid; int32_t n; int64_t amount; | |
5151 | cp = CCinit(&C,EVAL_CHANNELS); | |
5152 | if ( fHelp || params.size() != 4 ) | |
5153 | throw runtime_error("channelspayment prevtxid origtxid n amount\n"); | |
5154 | if ( ensure_CCrequirements() < 0 ) | |
5155 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5156 | LOCK(cs_main); | |
5157 | prevtxid = Parseuint256((char *)params[0].get_str().c_str()); | |
5158 | origtxid = Parseuint256((char *)params[1].get_str().c_str()); | |
5159 | n = atoi((char *)params[2].get_str().c_str()); | |
5160 | amount = atoi((char *)params[3].get_str().c_str()); | |
2b349eff | 5161 | hex = ChannelPayment(0,prevtxid,origtxid,n,amount); |
02da4225 | 5162 | if ( hex.size() > 0 ) |
5163 | { | |
5164 | result.push_back(Pair("result", "success")); | |
5165 | result.push_back(Pair("hex", hex)); | |
5166 | } else ERR_RESULT("couldnt create channelspayment transaction"); | |
5167 | return(result); | |
5168 | } | |
5169 | ||
5170 | UniValue channelscollect(const UniValue& params, bool fHelp) | |
5171 | { | |
3737d456 | 5172 | UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::string hex; uint256 origtxid,paytxid; int32_t n; int64_t amount; |
02da4225 | 5173 | cp = CCinit(&C,EVAL_CHANNELS); |
5174 | if ( fHelp || params.size() != 4 ) | |
3737d456 | 5175 | throw runtime_error("channelscollect paytxid origtxid n amount\n"); |
02da4225 | 5176 | if ( ensure_CCrequirements() < 0 ) |
5177 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5178 | LOCK(cs_main); | |
3737d456 | 5179 | paytxid = Parseuint256((char *)params[0].get_str().c_str()); |
02da4225 | 5180 | origtxid = Parseuint256((char *)params[1].get_str().c_str()); |
5181 | n = atoi((char *)params[2].get_str().c_str()); | |
5182 | amount = atoi((char *)params[3].get_str().c_str()); | |
3737d456 | 5183 | hex = ChannelCollect(0,paytxid,origtxid,n,amount); |
02da4225 | 5184 | if ( hex.size() > 0 ) |
5185 | { | |
5186 | result.push_back(Pair("result", "success")); | |
5187 | result.push_back(Pair("hex", hex)); | |
5188 | } else ERR_RESULT("couldnt create channelscollect transaction"); | |
5189 | return(result); | |
5190 | } | |
5191 | ||
5192 | UniValue channelsrefund(const UniValue& params, bool fHelp) | |
5193 | { | |
5194 | UniValue result(UniValue::VOBJ); struct CCcontract_info *cp,C; std::string hex; uint256 origtxid,stoptxid; | |
5195 | cp = CCinit(&C,EVAL_CHANNELS); | |
5196 | if ( fHelp || params.size() != 2 ) | |
5197 | throw runtime_error("channelsrefund stoptxid origtxid\n"); | |
5198 | if ( ensure_CCrequirements() < 0 ) | |
5199 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5200 | LOCK(cs_main); | |
5201 | stoptxid = Parseuint256((char *)params[0].get_str().c_str()); | |
5202 | origtxid = Parseuint256((char *)params[1].get_str().c_str()); | |
5203 | hex = ChannelRefund(0,stoptxid,origtxid); | |
5204 | if ( hex.size() > 0 ) | |
5205 | { | |
5206 | result.push_back(Pair("result", "success")); | |
5207 | result.push_back(Pair("hex", hex)); | |
5208 | } else ERR_RESULT("couldnt create channelsrefund transaction"); | |
5209 | return(result); | |
5210 | } | |
5211 | ||
e95b9582 | 5212 | UniValue rewardscreatefunding(const UniValue& params, bool fHelp) |
e37d99ce | 5213 | { |
a03146b3 | 5214 | UniValue result(UniValue::VOBJ); char *name; int64_t funds,APR,minseconds,maxseconds,mindeposit; std::string hex; |
f0f5f6c0 | 5215 | if ( fHelp || params.size() > 6 || params.size() < 2 ) |
eac2c15e | 5216 | throw runtime_error("rewardscreatefunding name amount APR mindays maxdays mindeposit\n"); |
e37d99ce | 5217 | if ( ensure_CCrequirements() < 0 ) |
5218 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 5219 | const CKeyStore& keystore = *pwalletMain; |
5220 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
5221 | // default to OOT params | |
e37d99ce | 5222 | APR = 5 * COIN; |
5223 | minseconds = maxseconds = 60 * 3600 * 24; | |
5224 | mindeposit = 100 * COIN; | |
11787469 | 5225 | name = (char *)params[0].get_str().c_str(); |
f0f5f6c0 | 5226 | funds = atof(params[1].get_str().c_str()) * COIN; |
a03146b3 | 5227 | |
bad5d1c3 AL |
5228 | if (!VALID_PLAN_NAME(name)) { |
5229 | ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); | |
5230 | return(result); | |
5231 | } | |
5232 | ||
a03146b3 JDL |
5233 | if ( funds <= 0 ) { |
5234 | ERR_RESULT("funds must be positive"); | |
5235 | return result; | |
5236 | } | |
f0f5f6c0 | 5237 | if ( params.size() > 2 ) |
e37d99ce | 5238 | { |
f0f5f6c0 | 5239 | APR = atof(params[2].get_str().c_str()) * COIN; |
a03146b3 JDL |
5240 | if ( APR > REWARDSCC_MAXAPR ) |
5241 | { | |
5242 | ERR_RESULT("25% APR is maximum"); | |
5243 | return result; | |
5244 | } | |
f0f5f6c0 | 5245 | if ( params.size() > 3 ) |
e37d99ce | 5246 | { |
f0f5f6c0 | 5247 | minseconds = atol(params[3].get_str().c_str()) * 3600 * 24; |
a03146b3 JDL |
5248 | if ( minseconds < 0 ) { |
5249 | ERR_RESULT("mindays must be non-negative"); | |
5250 | return result; | |
5251 | } | |
f0f5f6c0 | 5252 | if ( params.size() > 4 ) |
e37d99ce | 5253 | { |
f0f5f6c0 | 5254 | maxseconds = atol(params[4].get_str().c_str()) * 3600 * 24; |
a03146b3 JDL |
5255 | if ( maxseconds <= 0 ) { |
5256 | ERR_RESULT("maxdays must be positive"); | |
5257 | return result; | |
5258 | } | |
5259 | if ( maxseconds < minseconds ) { | |
5260 | ERR_RESULT("maxdays must be greater than mindays"); | |
5261 | return result; | |
5262 | } | |
f0f5f6c0 | 5263 | if ( params.size() > 5 ) |
5264 | mindeposit = atof(params[5].get_str().c_str()) * COIN; | |
a03146b3 JDL |
5265 | if ( mindeposit <= 0 ) { |
5266 | ERR_RESULT("mindeposit must be positive"); | |
5267 | return result; | |
5268 | } | |
e37d99ce | 5269 | } |
5270 | } | |
5271 | } | |
4f394f44 | 5272 | hex = RewardsCreateFunding(0,name,funds,APR,minseconds,maxseconds,mindeposit); |
e37d99ce | 5273 | if ( hex.size() > 0 ) |
5274 | { | |
5275 | result.push_back(Pair("result", "success")); | |
5276 | result.push_back(Pair("hex", hex)); | |
8a3e1884 | 5277 | } else ERR_RESULT("couldnt create rewards funding transaction"); |
e37d99ce | 5278 | return(result); |
5279 | } | |
5280 | ||
5281 | UniValue rewardslock(const UniValue& params, bool fHelp) | |
5282 | { | |
66027c02 | 5283 | UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid; int64_t amount; std::string hex; |
c4e7f616 | 5284 | if ( fHelp || params.size() != 3 ) |
5285 | throw runtime_error("rewardslock name fundingtxid amount\n"); | |
e37d99ce | 5286 | if ( ensure_CCrequirements() < 0 ) |
5287 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 5288 | const CKeyStore& keystore = *pwalletMain; |
5289 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
f0f5f6c0 | 5290 | name = (char *)params[0].get_str().c_str(); |
c4e7f616 | 5291 | fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); |
5292 | amount = atof(params[2].get_str().c_str()) * COIN; | |
5293 | hex = RewardsLock(0,name,fundingtxid,amount); | |
bad5d1c3 AL |
5294 | |
5295 | if (!VALID_PLAN_NAME(name)) { | |
5296 | ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); | |
5297 | return(result); | |
5298 | } | |
a03146b3 JDL |
5299 | if ( CCerror != "" ){ |
5300 | ERR_RESULT(CCerror); | |
5301 | } else if ( amount > 0 ) { | |
66027c02 JDL |
5302 | if ( hex.size() > 0 ) |
5303 | { | |
5304 | result.push_back(Pair("result", "success")); | |
5305 | result.push_back(Pair("hex", hex)); | |
8a3e1884 JDL |
5306 | } else ERR_RESULT( "couldnt create rewards lock transaction"); |
5307 | } else ERR_RESULT("amount must be positive"); | |
c4e7f616 | 5308 | return(result); |
5309 | } | |
5310 | ||
5311 | UniValue rewardsaddfunding(const UniValue& params, bool fHelp) | |
5312 | { | |
88e71457 | 5313 | UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid; int64_t amount; std::string hex; |
c4e7f616 | 5314 | if ( fHelp || params.size() != 3 ) |
5315 | throw runtime_error("rewardsaddfunding name fundingtxid amount\n"); | |
5316 | if ( ensure_CCrequirements() < 0 ) | |
5317 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 5318 | const CKeyStore& keystore = *pwalletMain; |
5319 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
c4e7f616 | 5320 | name = (char *)params[0].get_str().c_str(); |
5321 | fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); | |
5322 | amount = atof(params[2].get_str().c_str()) * COIN; | |
e95b9582 | 5323 | hex = RewardsAddfunding(0,name,fundingtxid,amount); |
bad5d1c3 AL |
5324 | |
5325 | if (!VALID_PLAN_NAME(name)) { | |
5326 | ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); | |
5327 | return(result); | |
5328 | } | |
a03146b3 JDL |
5329 | if (CCerror != "") { |
5330 | ERR_RESULT(CCerror); | |
5331 | } else if (amount > 0) { | |
88e71457 JDL |
5332 | if ( hex.size() > 0 ) |
5333 | { | |
5334 | result.push_back(Pair("result", "success")); | |
5335 | result.push_back(Pair("hex", hex)); | |
5336 | } else { | |
5337 | result.push_back(Pair("result", "error")); | |
5338 | result.push_back(Pair("error", "couldnt create rewards addfunding transaction")); | |
5339 | } | |
5340 | } else { | |
a03146b3 | 5341 | ERR_RESULT("funding amount must be positive"); |
88e71457 | 5342 | } |
e37d99ce | 5343 | return(result); |
5344 | } | |
5345 | ||
5346 | UniValue rewardsunlock(const UniValue& params, bool fHelp) | |
5347 | { | |
c4e7f616 | 5348 | UniValue result(UniValue::VOBJ); std::string hex; char *name; uint256 fundingtxid,txid; |
81915d9f | 5349 | if ( fHelp || params.size() > 3 || params.size() < 2 ) |
c4e7f616 | 5350 | throw runtime_error("rewardsunlock name fundingtxid [txid]\n"); |
e37d99ce | 5351 | if ( ensure_CCrequirements() < 0 ) |
5352 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 5353 | const CKeyStore& keystore = *pwalletMain; |
5354 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
f0f5f6c0 | 5355 | name = (char *)params[0].get_str().c_str(); |
c4e7f616 | 5356 | fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); |
bad5d1c3 AL |
5357 | |
5358 | if (!VALID_PLAN_NAME(name)) { | |
5359 | ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); | |
5360 | return(result); | |
5361 | } | |
c4e7f616 | 5362 | if ( params.size() > 2 ) |
5363 | txid = Parseuint256((char *)params[2].get_str().c_str()); | |
f0f5f6c0 | 5364 | else memset(&txid,0,sizeof(txid)); |
c4e7f616 | 5365 | hex = RewardsUnlock(0,name,fundingtxid,txid); |
8e0ff2b7 | 5366 | if (CCerror != "") { |
8a3e1884 JDL |
5367 | ERR_RESULT(CCerror); |
5368 | } else if ( hex.size() > 0 ) { | |
e37d99ce | 5369 | result.push_back(Pair("result", "success")); |
5370 | result.push_back(Pair("hex", hex)); | |
8a3e1884 | 5371 | } else ERR_RESULT("couldnt create rewards unlock transaction"); |
e37d99ce | 5372 | return(result); |
5373 | } | |
5374 | ||
c857567a | 5375 | UniValue rewardslist(const UniValue& params, bool fHelp) |
5376 | { | |
c857567a | 5377 | if ( fHelp || params.size() > 0 ) |
5378 | throw runtime_error("rewardslist\n"); | |
5379 | if ( ensure_CCrequirements() < 0 ) | |
5380 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5381 | return(RewardsList()); | |
5382 | } | |
5383 | ||
5384 | UniValue rewardsinfo(const UniValue& params, bool fHelp) | |
5385 | { | |
5386 | uint256 fundingtxid; | |
5387 | if ( fHelp || params.size() != 1 ) | |
5388 | throw runtime_error("rewardsinfo fundingtxid\n"); | |
5389 | if ( ensure_CCrequirements() < 0 ) | |
5390 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5391 | fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); | |
5392 | return(RewardsInfo(fundingtxid)); | |
5393 | } | |
5394 | ||
3515c101 | 5395 | UniValue gatewayslist(const UniValue& params, bool fHelp) |
5396 | { | |
5397 | if ( fHelp || params.size() > 0 ) | |
5398 | throw runtime_error("gatewayslist\n"); | |
5399 | if ( ensure_CCrequirements() < 0 ) | |
5400 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5401 | return(GatewaysList()); | |
5402 | } | |
5403 | ||
5404 | UniValue gatewaysinfo(const UniValue& params, bool fHelp) | |
5405 | { | |
5406 | uint256 txid; | |
5407 | if ( fHelp || params.size() != 1 ) | |
5408 | throw runtime_error("gatewaysinfo bindtxid\n"); | |
5409 | if ( ensure_CCrequirements() < 0 ) | |
5410 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5411 | txid = Parseuint256((char *)params[0].get_str().c_str()); | |
5412 | return(GatewaysInfo(txid)); | |
5413 | } | |
5414 | ||
5415 | UniValue gatewaysbind(const UniValue& params, bool fHelp) | |
5416 | { | |
dbf8484e | 5417 | UniValue result(UniValue::VOBJ); uint256 tokenid,oracletxid; int32_t i; int64_t totalsupply; std::vector<CPubKey> pubkeys; uint8_t M,N; std::string hex,coin; std::vector<unsigned char> pubkey; |
5418 | if ( fHelp || params.size() < 6 ) | |
5419 | throw runtime_error("gatewaysbind tokenid oracletxid coin tokensupply M N pubkey(s)\n"); | |
3515c101 | 5420 | if ( ensure_CCrequirements() < 0 ) |
5421 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5422 | tokenid = Parseuint256((char *)params[0].get_str().c_str()); | |
dbf8484e | 5423 | oracletxid = Parseuint256((char *)params[1].get_str().c_str()); |
5424 | coin = params[2].get_str(); | |
5425 | totalsupply = atol((char *)params[3].get_str().c_str()); | |
5426 | M = atoi((char *)params[4].get_str().c_str()); | |
5427 | N = atoi((char *)params[5].get_str().c_str()); | |
98a1f520 | 5428 | if ( M > N || N == 0 || N > 15 || totalsupply < COIN/100 || tokenid == zeroid ) |
3515c101 | 5429 | throw runtime_error("illegal M or N > 15 or tokensupply or invalid tokenid\n"); |
3515c101 | 5430 | for (i=0; i<N; i++) |
5431 | { | |
77fad432 | 5432 | if ( params.size() < 6+i+1 ) |
3515c101 | 5433 | throw runtime_error("not enough parameters for N pubkeys\n"); |
77fad432 | 5434 | pubkey = ParseHex(params[6+i].get_str().c_str()); |
3515c101 | 5435 | pubkeys.push_back(pubkey2pk(pubkey)); |
5436 | } | |
dbf8484e | 5437 | hex = GatewaysBind(0,coin,tokenid,totalsupply,oracletxid,M,N,pubkeys); |
3515c101 | 5438 | if ( hex.size() > 0 ) |
5439 | { | |
5440 | result.push_back(Pair("result", "success")); | |
5441 | result.push_back(Pair("hex", hex)); | |
5442 | } else ERR_RESULT("couldnt gatewaysbind"); | |
5443 | return(result); | |
5444 | } | |
5445 | ||
5446 | UniValue gatewaysdeposit(const UniValue& params, bool fHelp) | |
5447 | { | |
d73f18f5 | 5448 | UniValue result(UniValue::VOBJ); int32_t i,claimvout,height,numpks; int64_t amount; std::string hex,coin,deposithex; uint256 bindtxid,cointxid; std::vector<CPubKey>pubkeys; std::vector<uint256>proof,redeemscript; |
5449 | if ( fHelp || params.size() != 11 ) | |
5450 | throw runtime_error("gatewaysdeposit bindtxid height coin cointxid claimvout deposithex proof redeemscript amount numpks oraclepks\n"); | |
5451 | if ( ensure_CCrequirements() < 0 ) | |
5452 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5453 | bindtxid = Parseuint256((char *)params[0].get_str().c_str()); | |
5454 | height = atoi((char *)params[1].get_str().c_str()); | |
5455 | coin = params[2].get_str(); | |
5456 | cointxid = Parseuint256((char *)params[3].get_str().c_str()); | |
5457 | claimvout = atoi((char *)params[4].get_str().c_str()); | |
5458 | deposithex = params[5].get_str(); | |
5459 | proof = ParseHex(params[6].get_str()); | |
5460 | redeemscript = ParseHex(params[7].get_str()); | |
5461 | amount = atof((char *)params[8].get_str().c_str()) * COIN; | |
5462 | numpks = atoi((char *)params[9].get_str().c_str()); | |
5463 | if ( amount <= 0 || numpks <= 0 || claimvout < 0 ) | |
5464 | throw runtime_error("invalid param: amount, numpks or claimvout\n"); | |
5465 | for (i=0; i<numpks; i++) | |
5466 | { | |
5467 | if ( params.size() < 10+i+1 ) | |
5468 | throw runtime_error("not enough parameters for numpks oraclepubkeys\n"); | |
5469 | pubkey = ParseHex(params[10+i].get_str().c_str()); | |
5470 | pubkeys.push_back(pubkey2pk(pubkey)); | |
5471 | } | |
5472 | hex = GatewaysDeposit(0,bindtxid,pubkeys,height,coin,cointxid,claimvout,deposithex,proof,redeemscript,amount); | |
3515c101 | 5473 | if ( hex.size() > 0 ) |
5474 | { | |
5475 | result.push_back(Pair("result", "success")); | |
5476 | result.push_back(Pair("hex", hex)); | |
5477 | } else ERR_RESULT("couldnt gatewaysdeposit"); | |
5478 | return(result); | |
5479 | } | |
5480 | ||
5481 | UniValue gatewaysclaim(const UniValue& params, bool fHelp) | |
5482 | { | |
98a1f520 | 5483 | UniValue result(UniValue::VOBJ); std::string hex; |
3515c101 | 5484 | // std::string GatewaysClaim(uint64_t txfee,uint256 bindtxid,std::string coin,uint256 deposittxid,std::string claimaddr,int64_t amount) |
5485 | if ( hex.size() > 0 ) | |
5486 | { | |
5487 | result.push_back(Pair("result", "success")); | |
5488 | result.push_back(Pair("hex", hex)); | |
5489 | } else ERR_RESULT("couldnt gatewaysclaim"); | |
5490 | return(result); | |
5491 | } | |
5492 | ||
5493 | UniValue gatewayswithdraw(const UniValue& params, bool fHelp) | |
5494 | { | |
98a1f520 | 5495 | UniValue result(UniValue::VOBJ); std::string hex; |
3515c101 | 5496 | // std::string GatewaysWithdraw(uint64_t txfee,uint256 bindtxid,std::string refcoin,std::vector<uint8_t> withdrawpub,int64_t amount) |
5497 | if ( hex.size() > 0 ) | |
5498 | { | |
5499 | result.push_back(Pair("result", "success")); | |
5500 | result.push_back(Pair("hex", hex)); | |
5501 | } else ERR_RESULT("couldnt gatewayswithdraw"); | |
5502 | return(result); | |
5503 | } | |
5504 | ||
366625ca | 5505 | UniValue oracleslist(const UniValue& params, bool fHelp) |
5506 | { | |
5507 | if ( fHelp || params.size() > 0 ) | |
5508 | throw runtime_error("oracleslist\n"); | |
5509 | if ( ensure_CCrequirements() < 0 ) | |
5510 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5511 | return(OraclesList()); | |
5512 | } | |
5513 | ||
5514 | UniValue oraclesinfo(const UniValue& params, bool fHelp) | |
5515 | { | |
5516 | uint256 txid; | |
5517 | if ( fHelp || params.size() != 1 ) | |
5518 | throw runtime_error("oraclesinfo oracletxid\n"); | |
5519 | if ( ensure_CCrequirements() < 0 ) | |
5520 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5521 | txid = Parseuint256((char *)params[0].get_str().c_str()); | |
5522 | return(OracleInfo(txid)); | |
5523 | } | |
5524 | ||
5525 | UniValue oraclesregister(const UniValue& params, bool fHelp) | |
5526 | { | |
3d6ee3e0 | 5527 | UniValue result(UniValue::VOBJ); uint256 txid; int64_t datafee; std::string hex; |
366625ca | 5528 | if ( fHelp || params.size() != 2 ) |
5529 | throw runtime_error("oraclesregister oracletxid datafee\n"); | |
5530 | if ( ensure_CCrequirements() < 0 ) | |
5531 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5532 | txid = Parseuint256((char *)params[0].get_str().c_str()); | |
5533 | datafee = atol((char *)params[1].get_str().c_str()); | |
5534 | hex = OracleRegister(0,txid,datafee); | |
5535 | if ( hex.size() > 0 ) | |
5536 | { | |
5537 | result.push_back(Pair("result", "success")); | |
5538 | result.push_back(Pair("hex", hex)); | |
5539 | } else ERR_RESULT("couldnt register with oracle txid"); | |
5540 | return(result); | |
5541 | } | |
5542 | ||
5543 | UniValue oraclessubscribe(const UniValue& params, bool fHelp) | |
5544 | { | |
3d6ee3e0 | 5545 | UniValue result(UniValue::VOBJ); uint256 txid; int64_t amount; std::string hex; std::vector<unsigned char> pubkey; |
366625ca | 5546 | if ( fHelp || params.size() != 3 ) |
d95908b8 | 5547 | throw runtime_error("oraclessubscribe oracletxid publisher amount\n"); |
366625ca | 5548 | if ( ensure_CCrequirements() < 0 ) |
5549 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5550 | txid = Parseuint256((char *)params[0].get_str().c_str()); | |
5551 | pubkey = ParseHex(params[1].get_str().c_str()); | |
d95908b8 | 5552 | amount = atof((char *)params[2].get_str().c_str()) * COIN; |
366625ca | 5553 | hex = OracleSubscribe(0,txid,pubkey2pk(pubkey),amount); |
5554 | if ( hex.size() > 0 ) | |
5555 | { | |
5556 | result.push_back(Pair("result", "success")); | |
5557 | result.push_back(Pair("hex", hex)); | |
26ca942e | 5558 | } else ERR_RESULT("couldnt subscribe with oracle txid"); |
5559 | return(result); | |
5560 | } | |
5561 | ||
5562 | UniValue oraclessamples(const UniValue& params, bool fHelp) | |
5563 | { | |
a82dd70f | 5564 | UniValue result(UniValue::VOBJ); uint256 txid,batontxid; int32_t num; |
26ca942e | 5565 | if ( fHelp || params.size() != 3 ) |
5566 | throw runtime_error("oraclessamples oracletxid batonutxo num\n"); | |
5567 | if ( ensure_CCrequirements() < 0 ) | |
5568 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5569 | txid = Parseuint256((char *)params[0].get_str().c_str()); | |
5570 | batontxid = Parseuint256((char *)params[1].get_str().c_str()); | |
5571 | num = atoi((char *)params[2].get_str().c_str()); | |
a82dd70f | 5572 | return(OracleDataSamples(txid,batontxid,num)); |
366625ca | 5573 | } |
5574 | ||
5575 | UniValue oraclesdata(const UniValue& params, bool fHelp) | |
5576 | { | |
3d6ee3e0 | 5577 | UniValue result(UniValue::VOBJ); uint256 txid; std::vector<unsigned char> data; std::string hex; |
366625ca | 5578 | if ( fHelp || params.size() != 2 ) |
5579 | throw runtime_error("oraclesdata oracletxid hexstr\n"); | |
5580 | if ( ensure_CCrequirements() < 0 ) | |
5581 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5582 | txid = Parseuint256((char *)params[0].get_str().c_str()); | |
5583 | data = ParseHex(params[1].get_str().c_str()); | |
5584 | hex = OracleData(0,txid,data); | |
5585 | if ( hex.size() > 0 ) | |
5586 | { | |
5587 | result.push_back(Pair("result", "success")); | |
5588 | result.push_back(Pair("hex", hex)); | |
5589 | } else ERR_RESULT("couldnt publish data with oracle txid"); | |
5590 | return(result); | |
5591 | } | |
5592 | ||
5593 | UniValue oraclescreate(const UniValue& params, bool fHelp) | |
5594 | { | |
5595 | UniValue result(UniValue::VOBJ); std::string name,description,format,hex; | |
5596 | if ( fHelp || params.size() != 3 ) | |
5597 | throw runtime_error("oraclescreate name description format\n"); | |
5598 | if ( ensure_CCrequirements() < 0 ) | |
5599 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5600 | const CKeyStore& keystore = *pwalletMain; | |
5601 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
5602 | name = params[0].get_str(); | |
5603 | if ( name.size() == 0 || name.size() > 32) | |
5604 | { | |
5605 | ERR_RESULT("oracles name must not be empty and up to 32 characters"); | |
5606 | return(result); | |
5607 | } | |
5608 | description = params[1].get_str(); | |
5609 | if ( description.size() > 4096 ) | |
5610 | { | |
5611 | ERR_RESULT("oracles description must be <= 4096 characters"); | |
5612 | return(result); | |
5613 | } | |
5614 | format = params[2].get_str(); | |
5615 | if ( format.size() > 4096 ) | |
5616 | { | |
5617 | ERR_RESULT("oracles format must be <= 4096 characters"); | |
5618 | return(result); | |
5619 | } | |
5620 | hex = OracleCreate(0,name,description,format); | |
5621 | if ( hex.size() > 0 ) | |
5622 | { | |
5623 | result.push_back(Pair("result", "success")); | |
5624 | result.push_back(Pair("hex", hex)); | |
5625 | } else ERR_RESULT("couldnt create oracle"); | |
5626 | return(result); | |
5627 | } | |
5628 | ||
7137a022 | 5629 | UniValue FSMcreate(const UniValue& params, bool fHelp) |
5630 | { | |
5631 | UniValue result(UniValue::VOBJ); std::string name,states,hex; | |
5632 | if ( fHelp || params.size() != 2 ) | |
5633 | throw runtime_error("FSMcreate name states\n"); | |
5634 | if ( ensure_CCrequirements() < 0 ) | |
5635 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5636 | const CKeyStore& keystore = *pwalletMain; | |
5637 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
5638 | name = params[0].get_str(); | |
5639 | states = params[1].get_str(); | |
d9cf43a5 | 5640 | hex = FSMCreate(0,name,states); |
7137a022 | 5641 | if ( hex.size() > 0 ) |
5642 | { | |
5643 | result.push_back(Pair("result", "success")); | |
5644 | result.push_back(Pair("hex", hex)); | |
5645 | } else result.push_back(Pair("error", "couldnt create FSM transaction")); | |
5646 | return(result); | |
5647 | } | |
5648 | ||
5649 | UniValue FSMlist(const UniValue& params, bool fHelp) | |
5650 | { | |
5651 | uint256 tokenid; | |
5652 | if ( fHelp || params.size() > 0 ) | |
5653 | throw runtime_error("FSMlist\n"); | |
5654 | if ( ensure_CCrequirements() < 0 ) | |
5655 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5656 | return(FSMList()); | |
5657 | } | |
5658 | ||
5659 | UniValue FSMinfo(const UniValue& params, bool fHelp) | |
5660 | { | |
5661 | uint256 FSMtxid; | |
5662 | if ( fHelp || params.size() != 1 ) | |
5663 | throw runtime_error("FSMinfo fundingtxid\n"); | |
5664 | if ( ensure_CCrequirements() < 0 ) | |
5665 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
d9cf43a5 | 5666 | FSMtxid = Parseuint256((char *)params[0].get_str().c_str()); |
7137a022 | 5667 | return(FSMInfo(FSMtxid)); |
5668 | } | |
5669 | ||
4608d170 | 5670 | UniValue faucetinfo(const UniValue& params, bool fHelp) |
5671 | { | |
5672 | uint256 fundingtxid; | |
5673 | if ( fHelp || params.size() != 0 ) | |
5674 | throw runtime_error("faucetinfo\n"); | |
5675 | if ( ensure_CCrequirements() < 0 ) | |
5676 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5677 | return(FaucetInfo()); | |
5678 | } | |
5679 | ||
cfea7a46 | 5680 | UniValue faucetfund(const UniValue& params, bool fHelp) |
6ff08712 | 5681 | { |
2098a4c9 | 5682 | UniValue result(UniValue::VOBJ); int64_t funds; std::string hex; |
6ff08712 | 5683 | if ( fHelp || params.size() > 1 ) |
cfea7a46 | 5684 | throw runtime_error("faucetfund amount\n"); |
6ff08712 | 5685 | if ( ensure_CCrequirements() < 0 ) |
5686 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 5687 | const CKeyStore& keystore = *pwalletMain; |
5688 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
cfea7a46 | 5689 | funds = atof(params[0].get_str().c_str()) * COIN; |
700c1fcf | 5690 | if (funds > 0) { |
2098a4c9 | 5691 | hex = FaucetFund(0,(uint64_t) funds); |
700c1fcf JDL |
5692 | if ( hex.size() > 0 ) |
5693 | { | |
5694 | result.push_back(Pair("result", "success")); | |
5695 | result.push_back(Pair("hex", hex)); | |
8a3e1884 JDL |
5696 | } else ERR_RESULT("couldnt create faucet funding transaction"); |
5697 | } else ERR_RESULT( "funding amount must be positive"); | |
6ff08712 | 5698 | return(result); |
5699 | } | |
5700 | ||
cfea7a46 | 5701 | UniValue faucetget(const UniValue& params, bool fHelp) |
5702 | { | |
5703 | UniValue result(UniValue::VOBJ); std::string hex; | |
5704 | if ( fHelp || params.size() > 0 ) | |
5705 | throw runtime_error("faucetget\n"); | |
5706 | if ( ensure_CCrequirements() < 0 ) | |
5707 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 5708 | const CKeyStore& keystore = *pwalletMain; |
5709 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
cfea7a46 | 5710 | hex = FaucetGet(0); |
8e0ff2b7 | 5711 | if ( hex.size() > 0 ) { |
cfea7a46 | 5712 | result.push_back(Pair("result", "success")); |
5713 | result.push_back(Pair("hex", hex)); | |
8a3e1884 | 5714 | } else ERR_RESULT("couldnt create faucet get transaction"); |
cfea7a46 | 5715 | return(result); |
5716 | } | |
5717 | ||
5718 | UniValue dicefund(const UniValue& params, bool fHelp) | |
65a961ff | 5719 | { |
9025093e | 5720 | UniValue result(UniValue::VOBJ); int64_t funds,minbet,maxbet,maxodds,timeoutblocks; std::string hex; char *name; |
5bd03ad7 | 5721 | if ( fHelp || params.size() != 6 ) |
9025093e | 5722 | throw runtime_error("dicefund name funds minbet maxbet maxodds timeoutblocks\n"); |
e10def86 | 5723 | if ( ensure_CCrequirements() < 0 ) |
5724 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 5725 | const CKeyStore& keystore = *pwalletMain; |
5726 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
5bd03ad7 | 5727 | name = (char *)params[0].get_str().c_str(); |
5728 | funds = atof(params[1].get_str().c_str()) * COIN; | |
5729 | minbet = atof(params[2].get_str().c_str()) * COIN; | |
5730 | maxbet = atof(params[3].get_str().c_str()) * COIN; | |
5731 | maxodds = atol(params[4].get_str().c_str()); | |
9025093e | 5732 | timeoutblocks = atol(params[5].get_str().c_str()); |
124819ce JDL |
5733 | |
5734 | if (!VALID_PLAN_NAME(name)) { | |
5735 | ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); | |
5736 | return(result); | |
5737 | } | |
5738 | ||
9025093e | 5739 | hex = DiceCreateFunding(0,name,funds,minbet,maxbet,maxodds,timeoutblocks); |
8e0ff2b7 | 5740 | if (CCerror != "") { |
8a3e1884 | 5741 | ERR_RESULT(CCerror); |
8e0ff2b7 | 5742 | } else if ( hex.size() > 0 ) { |
65a961ff | 5743 | result.push_back(Pair("result", "success")); |
5744 | result.push_back(Pair("hex", hex)); | |
8e0ff2b7 | 5745 | } else { |
8a3e1884 | 5746 | ERR_RESULT( "couldnt create dice funding transaction"); |
8e0ff2b7 | 5747 | } |
65a961ff | 5748 | return(result); |
5749 | } | |
5750 | ||
587e715d | 5751 | UniValue diceaddfunds(const UniValue& params, bool fHelp) |
5752 | { | |
8e0ff2b7 | 5753 | UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid; int64_t amount; std::string hex; |
587e715d | 5754 | if ( fHelp || params.size() != 3 ) |
5755 | throw runtime_error("diceaddfunds name fundingtxid amount\n"); | |
5756 | if ( ensure_CCrequirements() < 0 ) | |
5757 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 5758 | const CKeyStore& keystore = *pwalletMain; |
5759 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
587e715d | 5760 | name = (char *)params[0].get_str().c_str(); |
5761 | fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); | |
5762 | amount = atof(params[2].get_str().c_str()) * COIN; | |
124819ce JDL |
5763 | if (!VALID_PLAN_NAME(name)) { |
5764 | ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); | |
5765 | return(result); | |
5766 | } | |
8e0ff2b7 JDL |
5767 | if ( amount > 0 ) { |
5768 | hex = DiceAddfunding(0,name,fundingtxid,amount); | |
5769 | if (CCerror != "") { | |
8a3e1884 | 5770 | ERR_RESULT(CCerror); |
8e0ff2b7 JDL |
5771 | } else if ( hex.size() > 0 ) { |
5772 | result.push_back(Pair("result", "success")); | |
5773 | result.push_back(Pair("hex", hex)); | |
8a3e1884 JDL |
5774 | } else ERR_RESULT("couldnt create dice addfunding transaction"); |
5775 | } else ERR_RESULT("amount must be positive"); | |
587e715d | 5776 | return(result); |
5777 | } | |
5778 | ||
cfea7a46 | 5779 | UniValue dicebet(const UniValue& params, bool fHelp) |
65a961ff | 5780 | { |
8e0ff2b7 | 5781 | UniValue result(UniValue::VOBJ); std::string hex; uint256 fundingtxid; int64_t amount,odds; char *name; |
135ead85 | 5782 | if ( fHelp || params.size() != 4 ) |
7d821112 | 5783 | throw runtime_error("dicebet name fundingtxid amount odds\n"); |
e10def86 | 5784 | if ( ensure_CCrequirements() < 0 ) |
5785 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 5786 | const CKeyStore& keystore = *pwalletMain; |
5787 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
7d821112 | 5788 | name = (char *)params[0].get_str().c_str(); |
5789 | fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); | |
5790 | amount = atof(params[2].get_str().c_str()) * COIN; | |
5791 | odds = atol(params[3].get_str().c_str()); | |
124819ce JDL |
5792 | |
5793 | if (!VALID_PLAN_NAME(name)) { | |
5794 | ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); | |
5795 | return(result); | |
5796 | } | |
8e0ff2b7 JDL |
5797 | if (amount > 0 && odds > 0) { |
5798 | hex = DiceBet(0,name,fundingtxid,amount,odds); | |
5799 | if ( hex.size() > 0 ) | |
5800 | { | |
5801 | result.push_back(Pair("result", "success")); | |
5802 | result.push_back(Pair("hex", hex)); | |
eb4cf14d | 5803 | } else ERR_RESULT("couldnt create dice bet transaction. make sure your address has funds"); |
8e0ff2b7 JDL |
5804 | } else { |
5805 | ERR_RESULT("amount and odds must be positive"); | |
5806 | } | |
65a961ff | 5807 | return(result); |
5808 | } | |
5809 | ||
7e988759 | 5810 | UniValue dicefinish(const UniValue& params, bool fHelp) |
5d3d3a87 | 5811 | { |
8e0ff2b7 | 5812 | UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid,bettxid; std::string hex; int32_t r; |
5d3d3a87 | 5813 | if ( fHelp || params.size() != 3 ) |
7e988759 | 5814 | throw runtime_error("dicefinish name fundingtxid bettxid\n"); |
5d3d3a87 | 5815 | if ( ensure_CCrequirements() < 0 ) |
5816 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 5817 | const CKeyStore& keystore = *pwalletMain; |
5818 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
5d3d3a87 | 5819 | name = (char *)params[0].get_str().c_str(); |
124819ce JDL |
5820 | if (!VALID_PLAN_NAME(name)) { |
5821 | ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); | |
5822 | return(result); | |
5823 | } | |
5d3d3a87 | 5824 | fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); |
5825 | bettxid = Parseuint256((char *)params[2].get_str().c_str()); | |
7e988759 | 5826 | hex = DiceBetFinish(&r,0,name,fundingtxid,bettxid,1); |
a03146b3 JDL |
5827 | if ( CCerror != "" ) |
5828 | { | |
5829 | ERR_RESULT(CCerror); | |
5830 | } else if ( hex.size() > 0 ) | |
5d3d3a87 | 5831 | { |
5832 | result.push_back(Pair("result", "success")); | |
5833 | result.push_back(Pair("hex", hex)); | |
8a3e1884 | 5834 | } else ERR_RESULT( "couldnt create dicefinish transaction"); |
5d3d3a87 | 5835 | return(result); |
5836 | } | |
5837 | ||
7e988759 | 5838 | UniValue dicestatus(const UniValue& params, bool fHelp) |
5d3d3a87 | 5839 | { |
8e0ff2b7 | 5840 | UniValue result(UniValue::VOBJ); char *name; uint256 fundingtxid,bettxid; std::string status; double winnings; |
7b44d4c1 | 5841 | if ( fHelp || (params.size() != 2 && params.size() != 3) ) |
7e988759 | 5842 | throw runtime_error("dicestatus name fundingtxid bettxid\n"); |
544593c6 | 5843 | if ( ensure_CCrequirements() < 0 ) |
5844 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 5845 | const CKeyStore& keystore = *pwalletMain; |
5846 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
544593c6 | 5847 | name = (char *)params[0].get_str().c_str(); |
124819ce JDL |
5848 | if (!VALID_PLAN_NAME(name)) { |
5849 | ERR_RESULT(strprintf("Plan name can be at most %d ASCII characters",PLAN_NAME_MAX)); | |
5850 | return(result); | |
5851 | } | |
544593c6 | 5852 | fundingtxid = Parseuint256((char *)params[1].get_str().c_str()); |
75dbd478 | 5853 | memset(&bettxid,0,sizeof(bettxid)); |
5854 | if ( params.size() == 3 ) | |
5855 | bettxid = Parseuint256((char *)params[2].get_str().c_str()); | |
4e525e48 | 5856 | winnings = DiceStatus(0,name,fundingtxid,bettxid); |
a03146b3 JDL |
5857 | if (CCerror != "") { |
5858 | ERR_RESULT(CCerror); | |
5859 | return result; | |
5860 | } | |
7e988759 | 5861 | result.push_back(Pair("result", "success")); |
d74d791a | 5862 | if ( winnings >= 0. ) |
544593c6 | 5863 | { |
d74d791a | 5864 | if ( winnings > 0. ) |
5865 | { | |
096cfeb8 | 5866 | if ( params.size() == 3 ) |
5867 | { | |
5868 | result.push_back(Pair("status", "win")); | |
5869 | result.push_back(Pair("won", winnings)); | |
5870 | } | |
5871 | else | |
5872 | { | |
5873 | result.push_back(Pair("status", "finalized")); | |
5874 | result.push_back(Pair("n", (int64_t)winnings)); | |
5875 | } | |
3bb6e233 | 5876 | } |
5877 | else | |
5878 | { | |
5879 | if ( params.size() == 3 ) | |
5880 | result.push_back(Pair("status", "loss")); | |
5881 | else result.push_back(Pair("status", "no pending bets")); | |
5882 | } | |
6ca2e998 | 5883 | } else result.push_back(Pair("status", "bet still pending")); |
544593c6 | 5884 | return(result); |
5885 | } | |
5886 | ||
c857567a | 5887 | UniValue dicelist(const UniValue& params, bool fHelp) |
fdd22810 | 5888 | { |
5889 | uint256 tokenid; | |
5890 | if ( fHelp || params.size() > 0 ) | |
c857567a | 5891 | throw runtime_error("dicelist\n"); |
fdd22810 | 5892 | if ( ensure_CCrequirements() < 0 ) |
5893 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
c857567a | 5894 | return(DiceList()); |
fdd22810 | 5895 | } |
5896 | ||
c857567a | 5897 | UniValue diceinfo(const UniValue& params, bool fHelp) |
fdd22810 | 5898 | { |
5899 | uint256 fundingtxid; | |
5900 | if ( fHelp || params.size() != 1 ) | |
c857567a | 5901 | throw runtime_error("diceinfo fundingtxid\n"); |
fdd22810 | 5902 | if ( ensure_CCrequirements() < 0 ) |
5903 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5904 | fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); | |
c857567a | 5905 | return(DiceInfo(fundingtxid)); |
fdd22810 | 5906 | } |
5907 | ||
c66eb36d | 5908 | UniValue tokenlist(const UniValue& params, bool fHelp) |
5909 | { | |
5910 | uint256 tokenid; | |
5911 | if ( fHelp || params.size() > 0 ) | |
5912 | throw runtime_error("tokenlist\n"); | |
5913 | if ( ensure_CCrequirements() < 0 ) | |
5914 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5915 | return(AssetList()); | |
5916 | } | |
5917 | ||
5918 | UniValue tokeninfo(const UniValue& params, bool fHelp) | |
5919 | { | |
5920 | uint256 tokenid; | |
5921 | if ( fHelp || params.size() != 1 ) | |
5922 | throw runtime_error("tokeninfo tokenid\n"); | |
5923 | if ( ensure_CCrequirements() < 0 ) | |
5924 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
5925 | tokenid = Parseuint256((char *)params[0].get_str().c_str()); | |
5926 | return(AssetInfo(tokenid)); | |
5927 | } | |
5928 | ||
143488c8 | 5929 | UniValue tokenorders(const UniValue& params, bool fHelp) |
5930 | { | |
5931 | uint256 tokenid; | |
5932 | if ( fHelp || params.size() > 1 ) | |
5933 | throw runtime_error("tokenorders [tokenid]\n"); | |
e10def86 | 5934 | if ( ensure_CCrequirements() < 0 ) |
5935 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
143488c8 | 5936 | if ( params.size() == 1 ) |
5937 | tokenid = Parseuint256((char *)params[0].get_str().c_str()); | |
5938 | else memset(&tokenid,0,sizeof(tokenid)); | |
5939 | return(AssetOrders(tokenid)); | |
5940 | } | |
5941 | ||
5942 | UniValue tokenbalance(const UniValue& params, bool fHelp) | |
5943 | { | |
e04b5c08 | 5944 | UniValue result(UniValue::VOBJ); char destaddr[64]; uint256 tokenid; uint64_t balance; std::vector<unsigned char> pubkey; struct CCcontract_info *cp,C; |
5945 | cp = CCinit(&C,EVAL_ASSETS); | |
143488c8 | 5946 | if ( fHelp || params.size() > 2 ) |
5947 | throw runtime_error("tokenbalance tokenid [pubkey]\n"); | |
e10def86 | 5948 | if ( ensure_CCrequirements() < 0 ) |
5949 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 5950 | LOCK(cs_main); |
143488c8 | 5951 | tokenid = Parseuint256((char *)params[0].get_str().c_str()); |
5952 | if ( params.size() == 2 ) | |
5953 | pubkey = ParseHex(params[1].get_str().c_str()); | |
5954 | else pubkey = Mypubkey(); | |
5955 | result.push_back(Pair("result", "success")); | |
e04b5c08 | 5956 | if ( GetCCaddress(cp,destaddr,pubkey2pk(pubkey)) != 0 ) |
143488c8 | 5957 | result.push_back(Pair("CCaddress",destaddr)); |
bb0c5133 | 5958 | balance = GetAssetBalance(pubkey2pk(pubkey),tokenid); |
143488c8 | 5959 | result.push_back(Pair("tokenid", params[0].get_str())); |
5960 | result.push_back(Pair("balance", (int64_t)balance)); | |
5961 | return(result); | |
5962 | } | |
5963 | ||
1a02fde9 | 5964 | UniValue tokencreate(const UniValue& params, bool fHelp) |
5965 | { | |
5966 | UniValue result(UniValue::VOBJ); std::string name,description,hex; uint64_t supply; | |
5967 | if ( fHelp || params.size() > 3 || params.size() < 2 ) | |
9a579b30 | 5968 | throw runtime_error("tokencreate name supply description\n"); |
e10def86 | 5969 | if ( ensure_CCrequirements() < 0 ) |
5970 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 5971 | const CKeyStore& keystore = *pwalletMain; |
5972 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
246ea3c3 | 5973 | name = params[0].get_str(); |
1a02fde9 | 5974 | supply = atof(params[1].get_str().c_str()) * COIN; |
124819ce | 5975 | if ( name.size() == 0 || name.size() > 32) |
4d47fcb9 | 5976 | { |
124819ce JDL |
5977 | ERR_RESULT("Token name must not be empty and up to 32 characters"); |
5978 | return(result); | |
5979 | } | |
5980 | if ( supply <= 0 ) | |
5981 | { | |
5982 | ERR_RESULT("Token supply must be positive"); | |
4d47fcb9 | 5983 | return(result); |
5984 | } | |
1a02fde9 | 5985 | if ( params.size() == 3 ) |
6ca2e998 | 5986 | { |
1a02fde9 | 5987 | description = params[2].get_str(); |
6ca2e998 | 5988 | if ( description.size() > 4096 ) |
5989 | { | |
124819ce | 5990 | ERR_RESULT("Token description must be <= 4096 characters"); |
6ca2e998 | 5991 | return(result); |
5992 | } | |
5993 | } | |
5963c84f | 5994 | hex = CreateAsset(0,supply,name,description); |
1a02fde9 | 5995 | if ( hex.size() > 0 ) |
5996 | { | |
5997 | result.push_back(Pair("result", "success")); | |
5998 | result.push_back(Pair("hex", hex)); | |
8a3e1884 | 5999 | } else ERR_RESULT("couldnt create transaction"); |
246ea3c3 | 6000 | return(result); |
1a02fde9 | 6001 | } |
6002 | ||
6003 | UniValue tokentransfer(const UniValue& params, bool fHelp) | |
6004 | { | |
8e0ff2b7 | 6005 | UniValue result(UniValue::VOBJ); std::string hex; int64_t amount; uint256 tokenid; |
e51e9274 | 6006 | if ( fHelp || params.size() != 3 ) |
6007 | throw runtime_error("tokentransfer tokenid destpubkey amount\n"); | |
e10def86 | 6008 | if ( ensure_CCrequirements() < 0 ) |
6009 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 6010 | const CKeyStore& keystore = *pwalletMain; |
6011 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
e51e9274 | 6012 | tokenid = Parseuint256((char *)params[0].get_str().c_str()); |
6013 | std::vector<unsigned char> pubkey(ParseHex(params[1].get_str().c_str())); | |
6014 | amount = atol(params[2].get_str().c_str()); | |
124819ce | 6015 | if ( tokenid == zeroid ) |
f6160f58 | 6016 | { |
124819ce JDL |
6017 | ERR_RESULT("invalid tokenid"); |
6018 | return(result); | |
6019 | } | |
6020 | if ( amount <= 0 ) | |
6021 | { | |
6022 | ERR_RESULT("amount must be positive"); | |
f6160f58 | 6023 | return(result); |
6024 | } | |
e51e9274 | 6025 | hex = AssetTransfer(0,tokenid,pubkey,amount); |
8e0ff2b7 JDL |
6026 | if (amount > 0) { |
6027 | if ( hex.size() > 0 ) | |
6028 | { | |
6029 | result.push_back(Pair("result", "success")); | |
6030 | result.push_back(Pair("hex", hex)); | |
8a3e1884 | 6031 | } else ERR_RESULT("couldnt transfer assets"); |
8e0ff2b7 JDL |
6032 | } else { |
6033 | ERR_RESULT("amount must be positive"); | |
6034 | } | |
1a02fde9 | 6035 | return(result); |
6036 | } | |
6037 | ||
6038 | UniValue tokenbid(const UniValue& params, bool fHelp) | |
6039 | { | |
8e0ff2b7 | 6040 | UniValue result(UniValue::VOBJ); int64_t bidamount,numtokens; std::string hex; double price; uint256 tokenid; |
5963c84f | 6041 | if ( fHelp || params.size() != 3 ) |
6042 | throw runtime_error("tokenbid numtokens tokenid price\n"); | |
e10def86 | 6043 | if ( ensure_CCrequirements() < 0 ) |
6044 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 6045 | const CKeyStore& keystore = *pwalletMain; |
6046 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
5963c84f | 6047 | numtokens = atoi(params[0].get_str().c_str()); |
6a530006 | 6048 | tokenid = Parseuint256((char *)params[1].get_str().c_str()); |
5963c84f | 6049 | price = atof(params[2].get_str().c_str()); |
93e02ad9 | 6050 | bidamount = (price * numtokens) * COIN + 0.0000000049999; |
124819ce | 6051 | if ( price <= 0 ) |
f6160f58 | 6052 | { |
124819ce JDL |
6053 | ERR_RESULT("price must be positive"); |
6054 | return(result); | |
6055 | } | |
6056 | if ( tokenid == zeroid ) | |
6057 | { | |
6058 | ERR_RESULT("invalid tokenid"); | |
6059 | return(result); | |
6060 | } | |
6061 | if ( bidamount <= 0 ) | |
6062 | { | |
6063 | ERR_RESULT("bid amount must be positive"); | |
f6160f58 | 6064 | return(result); |
6065 | } | |
5963c84f | 6066 | hex = CreateBuyOffer(0,bidamount,tokenid,numtokens); |
8e0ff2b7 JDL |
6067 | if (price > 0 && numtokens > 0) { |
6068 | if ( hex.size() > 0 ) | |
6069 | { | |
6070 | result.push_back(Pair("result", "success")); | |
6071 | result.push_back(Pair("hex", hex)); | |
124819ce | 6072 | } else ERR_RESULT("couldnt create bid"); |
8e0ff2b7 JDL |
6073 | } else { |
6074 | ERR_RESULT("price and numtokens must be positive"); | |
6075 | } | |
1a02fde9 | 6076 | return(result); |
6077 | } | |
6078 | ||
6079 | UniValue tokencancelbid(const UniValue& params, bool fHelp) | |
6080 | { | |
437d6328 | 6081 | UniValue result(UniValue::VOBJ); std::string hex; int32_t i; uint256 tokenid,bidtxid; |
6082 | if ( fHelp || params.size() != 2 ) | |
6083 | throw runtime_error("tokencancelbid tokenid bidtxid\n"); | |
e10def86 | 6084 | if ( ensure_CCrequirements() < 0 ) |
6085 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 6086 | const CKeyStore& keystore = *pwalletMain; |
6087 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
437d6328 | 6088 | tokenid = Parseuint256((char *)params[0].get_str().c_str()); |
6089 | bidtxid = Parseuint256((char *)params[1].get_str().c_str()); | |
f6160f58 | 6090 | if ( tokenid == zeroid || bidtxid == zeroid ) |
6091 | { | |
6092 | result.push_back(Pair("error", "invalid parameter")); | |
6093 | return(result); | |
6094 | } | |
437d6328 | 6095 | hex = CancelBuyOffer(0,tokenid,bidtxid); |
cf610814 | 6096 | if ( hex.size() > 0 ) |
6097 | { | |
6098 | result.push_back(Pair("result", "success")); | |
6099 | result.push_back(Pair("hex", hex)); | |
8a3e1884 | 6100 | } else ERR_RESULT("couldnt cancel bid"); |
1a02fde9 | 6101 | return(result); |
6102 | } | |
6103 | ||
6104 | UniValue tokenfillbid(const UniValue& params, bool fHelp) | |
6105 | { | |
a03146b3 | 6106 | UniValue result(UniValue::VOBJ); int64_t fillamount; std::string hex; uint256 tokenid,bidtxid; |
143488c8 | 6107 | if ( fHelp || params.size() != 3 ) |
6108 | throw runtime_error("tokenfillbid tokenid bidtxid fillamount\n"); | |
e10def86 | 6109 | if ( ensure_CCrequirements() < 0 ) |
6110 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 6111 | const CKeyStore& keystore = *pwalletMain; |
6112 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
143488c8 | 6113 | tokenid = Parseuint256((char *)params[0].get_str().c_str()); |
6114 | bidtxid = Parseuint256((char *)params[1].get_str().c_str()); | |
6115 | fillamount = atol(params[2].get_str().c_str()); | |
a03146b3 | 6116 | if ( fillamount <= 0 ) |
f6160f58 | 6117 | { |
a03146b3 JDL |
6118 | ERR_RESULT("fillamount must be positive"); |
6119 | return(result); | |
6120 | } | |
6121 | if ( tokenid == zeroid || bidtxid == zeroid ) | |
6122 | { | |
6123 | ERR_RESULT("must provide tokenid and bidtxid"); | |
f6160f58 | 6124 | return(result); |
6125 | } | |
143488c8 | 6126 | hex = FillBuyOffer(0,tokenid,bidtxid,fillamount); |
6127 | if ( hex.size() > 0 ) | |
6128 | { | |
6129 | result.push_back(Pair("result", "success")); | |
6130 | result.push_back(Pair("hex", hex)); | |
8a3e1884 | 6131 | } else ERR_RESULT("couldnt fill bid"); |
143488c8 | 6132 | return(result); |
6133 | } | |
6134 | ||
6135 | UniValue tokenask(const UniValue& params, bool fHelp) | |
6136 | { | |
8e0ff2b7 | 6137 | UniValue result(UniValue::VOBJ); int64_t askamount,numtokens; std::string hex; double price; uint256 tokenid; |
143488c8 | 6138 | if ( fHelp || params.size() != 3 ) |
6139 | throw runtime_error("tokenask numtokens tokenid price\n"); | |
e10def86 | 6140 | if ( ensure_CCrequirements() < 0 ) |
6141 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 6142 | const CKeyStore& keystore = *pwalletMain; |
6143 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
143488c8 | 6144 | numtokens = atoi(params[0].get_str().c_str()); |
6145 | tokenid = Parseuint256((char *)params[1].get_str().c_str()); | |
6146 | price = atof(params[2].get_str().c_str()); | |
6147 | askamount = (price * numtokens) * COIN + 0.0000000049999; | |
f6160f58 | 6148 | if ( tokenid == zeroid || numtokens <= 0 || price <= 0 || askamount <= 0 ) |
6149 | { | |
a03146b3 | 6150 | ERR_RESULT("invalid parameter"); |
f6160f58 | 6151 | return(result); |
6152 | } | |
fff7c5d2 | 6153 | hex = CreateSell(0,numtokens,tokenid,askamount); |
8e0ff2b7 JDL |
6154 | if (price > 0 && numtokens > 0) { |
6155 | if ( hex.size() > 0 ) | |
6156 | { | |
6157 | result.push_back(Pair("result", "success")); | |
6158 | result.push_back(Pair("hex", hex)); | |
8a3e1884 | 6159 | } else ERR_RESULT("couldnt create ask"); |
8e0ff2b7 JDL |
6160 | } else { |
6161 | ERR_RESULT("price and numtokens must be positive"); | |
6162 | } | |
143488c8 | 6163 | return(result); |
6164 | } | |
6165 | ||
6166 | UniValue tokenswapask(const UniValue& params, bool fHelp) | |
6167 | { | |
6168 | static uint256 zeroid; | |
8e0ff2b7 | 6169 | UniValue result(UniValue::VOBJ); int64_t askamount,numtokens; std::string hex; double price; uint256 tokenid,otherid; |
e78fa189 | 6170 | if ( fHelp || params.size() != 4 ) |
c0198c14 | 6171 | throw runtime_error("tokenswapask numtokens tokenid otherid price\n"); |
e10def86 | 6172 | if ( ensure_CCrequirements() < 0 ) |
6173 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 6174 | const CKeyStore& keystore = *pwalletMain; |
6175 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
143488c8 | 6176 | numtokens = atoi(params[0].get_str().c_str()); |
6177 | tokenid = Parseuint256((char *)params[1].get_str().c_str()); | |
6178 | otherid = Parseuint256((char *)params[2].get_str().c_str()); | |
6179 | price = atof(params[3].get_str().c_str()); | |
fff7c5d2 | 6180 | askamount = (price * numtokens); |
6181 | hex = CreateSwap(0,numtokens,tokenid,otherid,askamount); | |
8e0ff2b7 JDL |
6182 | if (price > 0 && numtokens > 0) { |
6183 | if ( hex.size() > 0 ) | |
6184 | { | |
6185 | result.push_back(Pair("result", "success")); | |
6186 | result.push_back(Pair("hex", hex)); | |
8a3e1884 | 6187 | } else ERR_RESULT("couldnt create swap"); |
8e0ff2b7 JDL |
6188 | } else { |
6189 | ERR_RESULT("price and numtokens must be positive"); | |
6190 | } | |
143488c8 | 6191 | return(result); |
6192 | } | |
6193 | ||
6194 | UniValue tokencancelask(const UniValue& params, bool fHelp) | |
6195 | { | |
ef2c8deb | 6196 | UniValue result(UniValue::VOBJ); std::string hex; int32_t i; uint256 tokenid,asktxid; |
143488c8 | 6197 | if ( fHelp || params.size() != 2 ) |
6198 | throw runtime_error("tokencancelask tokenid asktxid\n"); | |
e10def86 | 6199 | if ( ensure_CCrequirements() < 0 ) |
6200 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 6201 | const CKeyStore& keystore = *pwalletMain; |
6202 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
09c7f7cc | 6203 | tokenid = Parseuint256((char *)params[0].get_str().c_str()); |
14708917 | 6204 | asktxid = Parseuint256((char *)params[1].get_str().c_str()); |
f6160f58 | 6205 | if ( tokenid == zeroid || asktxid == zeroid ) |
6206 | { | |
6207 | result.push_back(Pair("error", "invalid parameter")); | |
6208 | return(result); | |
6209 | } | |
143488c8 | 6210 | hex = CancelSell(0,tokenid,asktxid); |
6211 | if ( hex.size() > 0 ) | |
6212 | { | |
6213 | result.push_back(Pair("result", "success")); | |
6214 | result.push_back(Pair("hex", hex)); | |
e4f4e63b | 6215 | } else ERR_RESULT("couldnt cancel ask"); |
143488c8 | 6216 | return(result); |
6217 | } | |
6218 | ||
6219 | UniValue tokenfillask(const UniValue& params, bool fHelp) | |
6220 | { | |
a03146b3 | 6221 | UniValue result(UniValue::VOBJ); int64_t fillunits; std::string hex; uint256 tokenid,asktxid; |
143488c8 | 6222 | if ( fHelp || params.size() != 3 ) |
ba8a98f2 | 6223 | throw runtime_error("tokenfillask tokenid asktxid fillunits\n"); |
e10def86 | 6224 | if ( ensure_CCrequirements() < 0 ) |
6225 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 6226 | const CKeyStore& keystore = *pwalletMain; |
6227 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
143488c8 | 6228 | tokenid = Parseuint256((char *)params[0].get_str().c_str()); |
6229 | asktxid = Parseuint256((char *)params[1].get_str().c_str()); | |
ba8a98f2 | 6230 | fillunits = atol(params[2].get_str().c_str()); |
a03146b3 JDL |
6231 | if ( fillunits <= 0 ) |
6232 | { | |
6233 | ERR_RESULT("fillunits must be positive"); | |
6234 | return(result); | |
6235 | } | |
6236 | if ( tokenid == zeroid || asktxid == zeroid ) | |
f6160f58 | 6237 | { |
6238 | result.push_back(Pair("error", "invalid parameter")); | |
6239 | return(result); | |
6240 | } | |
ba8a98f2 | 6241 | hex = FillSell(0,tokenid,zeroid,asktxid,fillunits); |
8e0ff2b7 | 6242 | if (fillunits > 0) { |
8a3e1884 JDL |
6243 | if (CCerror != "") { |
6244 | ERR_RESULT(CCerror); | |
6245 | } else if ( hex.size() > 0) { | |
8e0ff2b7 JDL |
6246 | result.push_back(Pair("result", "success")); |
6247 | result.push_back(Pair("hex", hex)); | |
8a3e1884 JDL |
6248 | } else { |
6249 | ERR_RESULT("couldnt fill bid"); | |
6250 | } | |
8e0ff2b7 JDL |
6251 | } else { |
6252 | ERR_RESULT("fillunits must be positive"); | |
6253 | } | |
143488c8 | 6254 | return(result); |
6255 | } | |
6256 | ||
6257 | UniValue tokenfillswap(const UniValue& params, bool fHelp) | |
6258 | { | |
6259 | static uint256 zeroid; | |
8e0ff2b7 | 6260 | UniValue result(UniValue::VOBJ); int64_t fillunits; std::string hex; uint256 tokenid,otherid,asktxid; |
143488c8 | 6261 | if ( fHelp || params.size() != 4 ) |
ba8a98f2 | 6262 | throw runtime_error("tokenfillswap tokenid otherid asktxid fillunits\n"); |
e10def86 | 6263 | if ( ensure_CCrequirements() < 0 ) |
6264 | throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); | |
b47b1743 | 6265 | const CKeyStore& keystore = *pwalletMain; |
6266 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
143488c8 | 6267 | tokenid = Parseuint256((char *)params[0].get_str().c_str()); |
6268 | otherid = Parseuint256((char *)params[1].get_str().c_str()); | |
6269 | asktxid = Parseuint256((char *)params[2].get_str().c_str()); | |
ba8a98f2 | 6270 | fillunits = atol(params[3].get_str().c_str()); |
6271 | hex = FillSell(0,tokenid,otherid,asktxid,fillunits); | |
8e0ff2b7 JDL |
6272 | if (fillunits > 0) { |
6273 | if ( hex.size() > 0 ) { | |
6274 | result.push_back(Pair("result", "success")); | |
6275 | result.push_back(Pair("hex", hex)); | |
6276 | } else ERR_RESULT("couldnt fill bid"); | |
6277 | } else { | |
6278 | ERR_RESULT("fillunits must be positive"); | |
6279 | } | |
1a02fde9 | 6280 | return(result); |
6281 | } | |
6282 | ||
4f02fc40 | 6283 | UniValue getbalance64(const UniValue& params, bool fHelp) |
6284 | { | |
6285 | set<CBitcoinAddress> setAddress; vector<COutput> vecOutputs; | |
5e0b330d | 6286 | UniValue ret(UniValue::VOBJ); UniValue a(UniValue::VARR),b(UniValue::VARR); CTxDestination address; |
4f02fc40 | 6287 | const CKeyStore& keystore = *pwalletMain; |
6288 | CAmount nValues[64],nValues2[64],nValue,total,total2; int32_t i,segid; | |
6289 | assert(pwalletMain != NULL); | |
6290 | if (fHelp || params.size() > 0) | |
6291 | throw runtime_error("getbalance64\n"); | |
6292 | total = total2 = 0; | |
6293 | memset(nValues,0,sizeof(nValues)); | |
6294 | memset(nValues2,0,sizeof(nValues2)); | |
6295 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
6296 | pwalletMain->AvailableCoins(vecOutputs, false, NULL, true); | |
6297 | BOOST_FOREACH(const COutput& out, vecOutputs) | |
6298 | { | |
6299 | nValue = out.tx->vout[out.i].nValue; | |
5e0b330d | 6300 | if ( ExtractDestination(out.tx->vout[out.i].scriptPubKey, address) ) |
4f02fc40 | 6301 | { |
ca76a7df | 6302 | segid = (komodo_segid32((char *)CBitcoinAddress(address).ToString().c_str()) & 0x3f); |
4f02fc40 | 6303 | if ( out.nDepth < 100 ) |
6304 | nValues2[segid] += nValue, total2 += nValue; | |
6305 | else nValues[segid] += nValue, total += nValue; | |
d979952f | 6306 | //fprintf(stderr,"%s %.8f depth.%d segid.%d\n",(char *)CBitcoinAddress(address).ToString().c_str(),(double)nValue/COIN,(int32_t)out.nDepth,segid); |
5e0b330d | 6307 | } else fprintf(stderr,"no destination\n"); |
4f02fc40 | 6308 | } |
5e0b330d | 6309 | ret.push_back(Pair("mature",(double)total/COIN)); |
4f02fc40 | 6310 | ret.push_back(Pair("immature",(double)total2/COIN)); |
6311 | for (i=0; i<64; i++) | |
6312 | { | |
5e0b330d | 6313 | a.push_back((uint64_t)nValues[i]); |
6314 | b.push_back((uint64_t)nValues2[i]); | |
4f02fc40 | 6315 | } |
6316 | ret.push_back(Pair("staking", a)); | |
5e0b330d | 6317 | ret.push_back(Pair("notstaking", b)); |
4f02fc40 | 6318 | return ret; |
6319 | } |