]>
Commit | Line | Data |
---|---|---|
652e1569 | 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 |
bc909a7a | 4 | // file COPYING or https://www.opensource.org/licenses/mit-license.php . |
652e1569 | 5 | |
71697f97 | 6 | #include "clientversion.h" |
652e1569 | 7 | #include "init.h" |
3d31e09c | 8 | #include "key_io.h" |
652e1569 WL |
9 | #include "main.h" |
10 | #include "net.h" | |
11 | #include "netbase.h" | |
4519a766 | 12 | #include "rpc/server.h" |
14f888ca | 13 | #include "timedata.h" |
68e174e2 | 14 | #include "txmempool.h" |
652e1569 WL |
15 | #include "util.h" |
16 | #ifdef ENABLE_WALLET | |
50c72f23 JS |
17 | #include "wallet/wallet.h" |
18 | #include "wallet/walletdb.h" | |
652e1569 WL |
19 | #endif |
20 | ||
21 | #include <stdint.h> | |
22 | ||
23 | #include <boost/assign/list_of.hpp> | |
a10a6e2a JS |
24 | |
25 | #include <univalue.h> | |
652e1569 | 26 | |
4e16a724 S |
27 | #include "zcash/Address.hpp" |
28 | ||
40a158e1 | 29 | using namespace std; |
652e1569 | 30 | |
f9de17ec WL |
31 | /** |
32 | * @note Do not add or change anything in the information returned by this | |
72fb3d29 | 33 | * method. `getinfo` exists for backwards-compatibility only. It combines |
f9de17ec WL |
34 | * information from wildly different sources in the program, which is a mess, |
35 | * and is thus planned to be deprecated eventually. | |
36 | * | |
37 | * Based on the source of the information, new information should be added to: | |
38 | * - `getblockchaininfo`, | |
39 | * - `getnetworkinfo` or | |
40 | * - `getwalletinfo` | |
41 | * | |
42 | * Or alternatively, create a specific query method for the information. | |
43 | **/ | |
d014114d | 44 | UniValue getinfo(const UniValue& params, bool fHelp) |
16bc9aaf WL |
45 | { |
46 | if (fHelp || params.size() != 0) | |
47 | throw runtime_error( | |
48 | "getinfo\n" | |
49 | "Returns an object containing various state info.\n" | |
50 | "\nResult:\n" | |
51 | "{\n" | |
52 | " \"version\": xxxxx, (numeric) the server version\n" | |
53 | " \"protocolversion\": xxxxx, (numeric) the protocol version\n" | |
54 | " \"walletversion\": xxxxx, (numeric) the wallet version\n" | |
c1652849 | 55 | " \"balance\": xxxxxxx, (numeric) the total Zcash balance of the wallet\n" |
16bc9aaf WL |
56 | " \"blocks\": xxxxxx, (numeric) the current number of blocks processed in the server\n" |
57 | " \"timeoffset\": xxxxx, (numeric) the time offset\n" | |
58 | " \"connections\": xxxxx, (numeric) the number of connections\n" | |
59 | " \"proxy\": \"host:port\", (string, optional) the proxy used by the server\n" | |
60 | " \"difficulty\": xxxxxx, (numeric) the current difficulty\n" | |
61 | " \"testnet\": true|false, (boolean) if the server is using testnet or not\n" | |
62 | " \"keypoololdest\": xxxxxx, (numeric) the timestamp (seconds since GMT epoch) of the oldest pre-generated key in the key pool\n" | |
63 | " \"keypoolsize\": xxxx, (numeric) how many new keys are pre-generated\n" | |
16bc9aaf | 64 | " \"unlocked_until\": ttt, (numeric) the timestamp in seconds since epoch (midnight Jan 1 1970 GMT) that the wallet is unlocked for transfers, or 0 if the wallet is locked\n" |
091b2116 RN |
65 | " \"paytxfee\": x.xxxx, (numeric) the transaction fee set in " + CURRENCY_UNIT + "/kB\n" |
66 | " \"relayfee\": x.xxxx, (numeric) minimum relay fee for non-free transactions in " + CURRENCY_UNIT + "/kB\n" | |
16bc9aaf WL |
67 | " \"errors\": \"...\" (string) any error messages\n" |
68 | "}\n" | |
69 | "\nExamples:\n" | |
70 | + HelpExampleCli("getinfo", "") | |
71 | + HelpExampleRpc("getinfo", "") | |
72 | ); | |
73 | ||
4401b2d7 EL |
74 | #ifdef ENABLE_WALLET |
75 | LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); | |
76 | #else | |
77 | LOCK(cs_main); | |
78 | #endif | |
79 | ||
16bc9aaf WL |
80 | proxyType proxy; |
81 | GetProxy(NET_IPV4, proxy); | |
82 | ||
38fc4b70 | 83 | UniValue obj(UniValue::VOBJ); |
faadbe17 PK |
84 | obj.push_back(Pair("version", CLIENT_VERSION)); |
85 | obj.push_back(Pair("protocolversion", PROTOCOL_VERSION)); | |
16bc9aaf WL |
86 | #ifdef ENABLE_WALLET |
87 | if (pwalletMain) { | |
88 | obj.push_back(Pair("walletversion", pwalletMain->GetVersion())); | |
89 | obj.push_back(Pair("balance", ValueFromAmount(pwalletMain->GetBalance()))); | |
90 | } | |
91 | #endif | |
92 | obj.push_back(Pair("blocks", (int)chainActive.Height())); | |
d56e30ca | 93 | obj.push_back(Pair("timeoffset", GetTimeOffset())); |
16bc9aaf | 94 | obj.push_back(Pair("connections", (int)vNodes.size())); |
67a79493 | 95 | obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.proxy.ToStringIPPort() : string()))); |
16bc9aaf | 96 | obj.push_back(Pair("difficulty", (double)GetDifficulty())); |
cc972107 | 97 | obj.push_back(Pair("testnet", Params().TestnetToBeDeprecatedFieldRPC())); |
16bc9aaf WL |
98 | #ifdef ENABLE_WALLET |
99 | if (pwalletMain) { | |
d56e30ca | 100 | obj.push_back(Pair("keypoololdest", pwalletMain->GetOldestKeyPoolTime())); |
16bc9aaf WL |
101 | obj.push_back(Pair("keypoolsize", (int)pwalletMain->GetKeyPoolSize())); |
102 | } | |
16bc9aaf | 103 | if (pwalletMain && pwalletMain->IsCrypted()) |
d56e30ca | 104 | obj.push_back(Pair("unlocked_until", nWalletUnlockTime)); |
c6cb21d1 | 105 | obj.push_back(Pair("paytxfee", ValueFromAmount(payTxFee.GetFeePerK()))); |
16bc9aaf | 106 | #endif |
13fc83c7 | 107 | obj.push_back(Pair("relayfee", ValueFromAmount(::minRelayTxFee.GetFeePerK()))); |
16bc9aaf WL |
108 | obj.push_back(Pair("errors", GetWarnings("statusbar"))); |
109 | return obj; | |
110 | } | |
111 | ||
452955f5 | 112 | #ifdef ENABLE_WALLET |
d014114d | 113 | class DescribeAddressVisitor : public boost::static_visitor<UniValue> |
452955f5 WL |
114 | { |
115 | public: | |
d014114d | 116 | UniValue operator()(const CNoDestination &dest) const { return UniValue(UniValue::VOBJ); } |
452955f5 | 117 | |
851f58f9 | 118 | UniValue operator()(const CKeyID &keyID) const { |
38fc4b70 | 119 | UniValue obj(UniValue::VOBJ); |
452955f5 | 120 | CPubKey vchPubKey; |
452955f5 | 121 | obj.push_back(Pair("isscript", false)); |
56215c77 | 122 | if (pwalletMain && pwalletMain->GetPubKey(keyID, vchPubKey)) { |
c8988460 PW |
123 | obj.push_back(Pair("pubkey", HexStr(vchPubKey))); |
124 | obj.push_back(Pair("iscompressed", vchPubKey.IsCompressed())); | |
125 | } | |
452955f5 WL |
126 | return obj; |
127 | } | |
128 | ||
851f58f9 | 129 | UniValue operator()(const CScriptID &scriptID) const { |
38fc4b70 | 130 | UniValue obj(UniValue::VOBJ); |
9c7167d1 | 131 | CScript subscript; |
452955f5 | 132 | obj.push_back(Pair("isscript", true)); |
56215c77 | 133 | if (pwalletMain && pwalletMain->GetCScript(scriptID, subscript)) { |
c8988460 PW |
134 | std::vector<CTxDestination> addresses; |
135 | txnouttype whichType; | |
136 | int nRequired; | |
137 | ExtractDestinations(subscript, whichType, addresses, nRequired); | |
138 | obj.push_back(Pair("script", GetTxnOutputType(whichType))); | |
139 | obj.push_back(Pair("hex", HexStr(subscript.begin(), subscript.end()))); | |
38fc4b70 | 140 | UniValue a(UniValue::VARR); |
07444da1 PW |
141 | for (const CTxDestination& addr : addresses) { |
142 | a.push_back(EncodeDestination(addr)); | |
143 | } | |
c8988460 PW |
144 | obj.push_back(Pair("addresses", a)); |
145 | if (whichType == TX_MULTISIG) | |
146 | obj.push_back(Pair("sigsrequired", nRequired)); | |
147 | } | |
452955f5 WL |
148 | return obj; |
149 | } | |
150 | }; | |
151 | #endif | |
152 | ||
d014114d | 153 | UniValue validateaddress(const UniValue& params, bool fHelp) |
452955f5 WL |
154 | { |
155 | if (fHelp || params.size() != 1) | |
156 | throw runtime_error( | |
d2c1e4a8 | 157 | "validateaddress \"zcashaddress\"\n" |
c1652849 | 158 | "\nReturn information about the given Zcash address.\n" |
452955f5 | 159 | "\nArguments:\n" |
c1652849 | 160 | "1. \"zcashaddress\" (string, required) The Zcash address to validate\n" |
452955f5 WL |
161 | "\nResult:\n" |
162 | "{\n" | |
163 | " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" | |
c1652849 | 164 | " \"address\" : \"zcashaddress\", (string) The Zcash address validated\n" |
426a74ed | 165 | " \"scriptPubKey\" : \"hex\", (string) The hex encoded scriptPubKey generated by the address\n" |
452955f5 WL |
166 | " \"ismine\" : true|false, (boolean) If the address is yours or not\n" |
167 | " \"isscript\" : true|false, (boolean) If the key is a script\n" | |
168 | " \"pubkey\" : \"publickeyhex\", (string) The hex value of the raw public key\n" | |
169 | " \"iscompressed\" : true|false, (boolean) If the address is compressed\n" | |
7b782f5b | 170 | " \"account\" : \"account\" (string) DEPRECATED. The account associated with the address, \"\" is the default account\n" |
452955f5 WL |
171 | "}\n" |
172 | "\nExamples:\n" | |
173 | + HelpExampleCli("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") | |
174 | + HelpExampleRpc("validateaddress", "\"1PSSGeFHDnKNxiEyFrD1wcEaHr9hrQDDWc\"") | |
175 | ); | |
176 | ||
4401b2d7 EL |
177 | #ifdef ENABLE_WALLET |
178 | LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); | |
179 | #else | |
180 | LOCK(cs_main); | |
181 | #endif | |
182 | ||
07444da1 PW |
183 | CTxDestination dest = DecodeDestination(params[0].get_str()); |
184 | bool isValid = IsValidDestination(dest); | |
452955f5 | 185 | |
38fc4b70 | 186 | UniValue ret(UniValue::VOBJ); |
452955f5 WL |
187 | ret.push_back(Pair("isvalid", isValid)); |
188 | if (isValid) | |
189 | { | |
07444da1 | 190 | std::string currentAddress = EncodeDestination(dest); |
452955f5 | 191 | ret.push_back(Pair("address", currentAddress)); |
426a74ed PT |
192 | |
193 | CScript scriptPubKey = GetScriptForDestination(dest); | |
194 | ret.push_back(Pair("scriptPubKey", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); | |
195 | ||
452955f5 | 196 | #ifdef ENABLE_WALLET |
a3e192a3 J |
197 | isminetype mine = pwalletMain ? IsMine(*pwalletMain, dest) : ISMINE_NO; |
198 | ret.push_back(Pair("ismine", (mine & ISMINE_SPENDABLE) ? true : false)); | |
9c7167d1 | 199 | ret.push_back(Pair("iswatchonly", (mine & ISMINE_WATCH_ONLY) ? true: false)); |
200 | UniValue detail = boost::apply_visitor(DescribeAddressVisitor(), dest); | |
201 | ret.pushKVs(detail); | |
452955f5 WL |
202 | if (pwalletMain && pwalletMain->mapAddressBook.count(dest)) |
203 | ret.push_back(Pair("account", pwalletMain->mapAddressBook[dest].name)); | |
204 | #endif | |
205 | } | |
206 | return ret; | |
207 | } | |
208 | ||
4e16a724 | 209 | |
bea87915 JG |
210 | class DescribePaymentAddressVisitor : public boost::static_visitor<UniValue> |
211 | { | |
212 | public: | |
213 | UniValue operator()(const libzcash::InvalidEncoding &zaddr) const { return UniValue(UniValue::VOBJ); } | |
214 | ||
215 | UniValue operator()(const libzcash::SproutPaymentAddress &zaddr) const { | |
216 | UniValue obj(UniValue::VOBJ); | |
217 | obj.push_back(Pair("type", "sprout")); | |
218 | obj.push_back(Pair("payingkey", zaddr.a_pk.GetHex())); | |
219 | obj.push_back(Pair("transmissionkey", zaddr.pk_enc.GetHex())); | |
220 | #ifdef ENABLE_WALLET | |
221 | if (pwalletMain) { | |
25d5e80c | 222 | obj.push_back(Pair("ismine", pwalletMain->HaveSproutSpendingKey(zaddr))); |
bea87915 JG |
223 | } |
224 | #endif | |
225 | return obj; | |
226 | } | |
227 | ||
228 | UniValue operator()(const libzcash::SaplingPaymentAddress &zaddr) const { | |
229 | UniValue obj(UniValue::VOBJ); | |
230 | obj.push_back(Pair("type", "sapling")); | |
231 | obj.push_back(Pair("diversifier", HexStr(zaddr.d))); | |
232 | obj.push_back(Pair("diversifiedtransmissionkey", zaddr.pk_d.GetHex())); | |
233 | #ifdef ENABLE_WALLET | |
234 | if (pwalletMain) { | |
235 | libzcash::SaplingIncomingViewingKey ivk; | |
236 | libzcash::SaplingFullViewingKey fvk; | |
237 | bool isMine = pwalletMain->GetSaplingIncomingViewingKey(zaddr, ivk) && | |
238 | pwalletMain->GetSaplingFullViewingKey(ivk, fvk) && | |
239 | pwalletMain->HaveSaplingSpendingKey(fvk); | |
240 | obj.push_back(Pair("ismine", isMine)); | |
241 | } | |
242 | #endif | |
243 | return obj; | |
244 | } | |
245 | }; | |
246 | ||
0d37ae3a | 247 | UniValue z_validateaddress(const UniValue& params, bool fHelp) |
4e16a724 S |
248 | { |
249 | if (fHelp || params.size() != 1) | |
250 | throw runtime_error( | |
251 | "z_validateaddress \"zaddr\"\n" | |
252 | "\nReturn information about the given z address.\n" | |
253 | "\nArguments:\n" | |
254 | "1. \"zaddr\" (string, required) The z address to validate\n" | |
255 | "\nResult:\n" | |
256 | "{\n" | |
257 | " \"isvalid\" : true|false, (boolean) If the address is valid or not. If not, this is the only property returned.\n" | |
258 | " \"address\" : \"zaddr\", (string) The z address validated\n" | |
bea87915 | 259 | " \"type\" : \"xxxx\", (string) \"sprout\" or \"sapling\"\n" |
4e16a724 | 260 | " \"ismine\" : true|false, (boolean) If the address is yours or not\n" |
bea87915 JG |
261 | " \"payingkey\" : \"hex\", (string) [sprout] The hex value of the paying key, a_pk\n" |
262 | " \"transmissionkey\" : \"hex\", (string) [sprout] The hex value of the transmission key, pk_enc\n" | |
263 | " \"diversifier\" : \"hex\", (string) [sapling] The hex value of the diversifier, d\n" | |
264 | " \"diversifiedtransmissionkey\" : \"hex\", (string) [sapling] The hex value of pk_d\n" | |
4e16a724 S |
265 | |
266 | "}\n" | |
267 | "\nExamples:\n" | |
337a99a2 JG |
268 | + HelpExampleCli("z_validateaddress", "\"zcWsmqT4X2V4jgxbgiCzyrAfRT1vi1F4sn7M5Pkh66izzw8Uk7LBGAH3DtcSMJeUb2pi3W4SQF8LMKkU2cUuVP68yAGcomL\"") |
269 | + HelpExampleRpc("z_validateaddress", "\"zcWsmqT4X2V4jgxbgiCzyrAfRT1vi1F4sn7M5Pkh66izzw8Uk7LBGAH3DtcSMJeUb2pi3W4SQF8LMKkU2cUuVP68yAGcomL\"") | |
4e16a724 S |
270 | ); |
271 | ||
272 | ||
273 | #ifdef ENABLE_WALLET | |
274 | LOCK2(cs_main, pwalletMain->cs_wallet); | |
275 | #else | |
276 | LOCK(cs_main); | |
277 | #endif | |
278 | ||
4e16a724 | 279 | string strAddress = params[0].get_str(); |
e5eab182 JG |
280 | auto address = DecodePaymentAddress(strAddress); |
281 | bool isValid = IsValidPaymentAddress(address); | |
4e16a724 | 282 | |
0d37ae3a | 283 | UniValue ret(UniValue::VOBJ); |
bea87915 | 284 | ret.push_back(Pair("isvalid", isValid)); |
4e16a724 S |
285 | if (isValid) |
286 | { | |
287 | ret.push_back(Pair("address", strAddress)); | |
bea87915 JG |
288 | UniValue detail = boost::apply_visitor(DescribePaymentAddressVisitor(), address); |
289 | ret.pushKVs(detail); | |
4e16a724 S |
290 | } |
291 | return ret; | |
292 | } | |
293 | ||
294 | ||
72fb3d29 MF |
295 | /** |
296 | * Used by addmultisigaddress / createmultisig: | |
297 | */ | |
d014114d | 298 | CScript _createmultisig_redeemScript(const UniValue& params) |
723a03d2 WL |
299 | { |
300 | int nRequired = params[0].get_int(); | |
d014114d | 301 | const UniValue& keys = params[1].get_array(); |
723a03d2 WL |
302 | |
303 | // Gather public keys | |
304 | if (nRequired < 1) | |
305 | throw runtime_error("a multisignature address must require at least one key to redeem"); | |
306 | if ((int)keys.size() < nRequired) | |
307 | throw runtime_error( | |
308 | strprintf("not enough keys supplied " | |
783b182c | 309 | "(got %u keys, but need at least %d to redeem)", keys.size(), nRequired)); |
e5d9d77d | 310 | if (keys.size() > 16) |
311 | throw runtime_error("Number of addresses involved in the multisignature address creation > 16\nReduce the number"); | |
723a03d2 WL |
312 | std::vector<CPubKey> pubkeys; |
313 | pubkeys.resize(keys.size()); | |
314 | for (unsigned int i = 0; i < keys.size(); i++) | |
315 | { | |
316 | const std::string& ks = keys[i].get_str(); | |
317 | #ifdef ENABLE_WALLET | |
318 | // Case 1: Bitcoin address and we have full public key: | |
07444da1 PW |
319 | CTxDestination dest = DecodeDestination(ks); |
320 | if (pwalletMain && IsValidDestination(dest)) { | |
321 | const CKeyID *keyID = boost::get<CKeyID>(&dest); | |
322 | if (!keyID) { | |
323 | throw std::runtime_error(strprintf("%s does not refer to a key", ks)); | |
324 | } | |
723a03d2 | 325 | CPubKey vchPubKey; |
07444da1 PW |
326 | if (!pwalletMain->GetPubKey(*keyID, vchPubKey)) { |
327 | throw std::runtime_error(strprintf("no full public key for address %s", ks)); | |
328 | } | |
723a03d2 WL |
329 | if (!vchPubKey.IsFullyValid()) |
330 | throw runtime_error(" Invalid public key: "+ks); | |
331 | pubkeys[i] = vchPubKey; | |
332 | } | |
333 | ||
334 | // Case 2: hex public key | |
335 | else | |
336 | #endif | |
337 | if (IsHex(ks)) | |
338 | { | |
339 | CPubKey vchPubKey(ParseHex(ks)); | |
340 | if (!vchPubKey.IsFullyValid()) | |
341 | throw runtime_error(" Invalid public key: "+ks); | |
342 | pubkeys[i] = vchPubKey; | |
343 | } | |
344 | else | |
345 | { | |
346 | throw runtime_error(" Invalid public key: "+ks); | |
347 | } | |
348 | } | |
0be990ba | 349 | CScript result = GetScriptForMultisig(nRequired, pubkeys); |
787ee0c9 PT |
350 | |
351 | if (result.size() > MAX_SCRIPT_ELEMENT_SIZE) | |
352 | throw runtime_error( | |
353 | strprintf("redeemScript exceeds size limit: %d > %d", result.size(), MAX_SCRIPT_ELEMENT_SIZE)); | |
354 | ||
723a03d2 WL |
355 | return result; |
356 | } | |
357 | ||
d014114d | 358 | UniValue createmultisig(const UniValue& params, bool fHelp) |
723a03d2 WL |
359 | { |
360 | if (fHelp || params.size() < 2 || params.size() > 2) | |
361 | { | |
362 | string msg = "createmultisig nrequired [\"key\",...]\n" | |
363 | "\nCreates a multi-signature address with n signature of m keys required.\n" | |
364 | "It returns a json object with the address and redeemScript.\n" | |
365 | ||
366 | "\nArguments:\n" | |
367 | "1. nrequired (numeric, required) The number of required signatures out of the n keys or addresses.\n" | |
c1652849 | 368 | "2. \"keys\" (string, required) A json array of keys which are Zcash addresses or hex-encoded public keys\n" |
723a03d2 | 369 | " [\n" |
c1652849 | 370 | " \"key\" (string) Zcash address or hex-encoded public key\n" |
723a03d2 WL |
371 | " ,...\n" |
372 | " ]\n" | |
373 | ||
374 | "\nResult:\n" | |
375 | "{\n" | |
376 | " \"address\":\"multisigaddress\", (string) The value of the new multisig address.\n" | |
377 | " \"redeemScript\":\"script\" (string) The string value of the hex-encoded redemption script.\n" | |
378 | "}\n" | |
379 | ||
380 | "\nExamples:\n" | |
381 | "\nCreate a multisig address from 2 addresses\n" | |
70454796 | 382 | + HelpExampleCli("createmultisig", "2 \"[\\\"t16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"t171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") + |
723a03d2 | 383 | "\nAs a json rpc call\n" |
70454796 | 384 | + HelpExampleRpc("createmultisig", "2, \"[\\\"t16sSauSf5pF2UkUwvKGq4qjNRzBZYqgEL5\\\",\\\"t171sgjn4YtPu27adkKGrdDwzRTxnRkBfKV\\\"]\"") |
723a03d2 WL |
385 | ; |
386 | throw runtime_error(msg); | |
387 | } | |
388 | ||
389 | // Construct using pay-to-script-hash: | |
787ee0c9 | 390 | CScript inner = _createmultisig_redeemScript(params); |
066e2a14 | 391 | CScriptID innerID(inner); |
723a03d2 | 392 | |
b47f3aea | 393 | UniValue result(UniValue::VOBJ); |
07444da1 | 394 | result.push_back(Pair("address", EncodeDestination(innerID))); |
723a03d2 WL |
395 | result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end()))); |
396 | ||
397 | return result; | |
398 | } | |
399 | ||
d014114d | 400 | UniValue verifymessage(const UniValue& params, bool fHelp) |
c3a7f516 WL |
401 | { |
402 | if (fHelp || params.size() != 3) | |
403 | throw runtime_error( | |
d2c1e4a8 | 404 | "verifymessage \"zcashaddress\" \"signature\" \"message\"\n" |
c3a7f516 WL |
405 | "\nVerify a signed message\n" |
406 | "\nArguments:\n" | |
c1652849 | 407 | "1. \"zcashaddress\" (string, required) The Zcash address to use for the signature.\n" |
c3a7f516 WL |
408 | "2. \"signature\" (string, required) The signature provided by the signer in base 64 encoding (see signmessage).\n" |
409 | "3. \"message\" (string, required) The message that was signed.\n" | |
410 | "\nResult:\n" | |
411 | "true|false (boolean) If the signature is verified or not.\n" | |
412 | "\nExamples:\n" | |
413 | "\nUnlock the wallet for 30 seconds\n" | |
414 | + HelpExampleCli("walletpassphrase", "\"mypassphrase\" 30") + | |
415 | "\nCreate the signature\n" | |
70454796 | 416 | + HelpExampleCli("signmessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" \"my message\"") + |
c3a7f516 | 417 | "\nVerify the signature\n" |
70454796 | 418 | + HelpExampleCli("verifymessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\" \"signature\" \"my message\"") + |
c3a7f516 | 419 | "\nAs json rpc\n" |
70454796 | 420 | + HelpExampleRpc("verifymessage", "\"t14oHp2v54vfmdgQ3v3SNuQga8JKHTNi2a1\", \"signature\", \"my message\"") |
c3a7f516 WL |
421 | ); |
422 | ||
4401b2d7 EL |
423 | LOCK(cs_main); |
424 | ||
c3a7f516 WL |
425 | string strAddress = params[0].get_str(); |
426 | string strSign = params[1].get_str(); | |
427 | string strMessage = params[2].get_str(); | |
428 | ||
07444da1 PW |
429 | CTxDestination destination = DecodeDestination(strAddress); |
430 | if (!IsValidDestination(destination)) { | |
c3a7f516 | 431 | throw JSONRPCError(RPC_TYPE_ERROR, "Invalid address"); |
07444da1 | 432 | } |
c3a7f516 | 433 | |
07444da1 PW |
434 | const CKeyID *keyID = boost::get<CKeyID>(&destination); |
435 | if (!keyID) { | |
c3a7f516 | 436 | throw JSONRPCError(RPC_TYPE_ERROR, "Address does not refer to key"); |
07444da1 | 437 | } |
c3a7f516 WL |
438 | |
439 | bool fInvalid = false; | |
440 | vector<unsigned char> vchSig = DecodeBase64(strSign.c_str(), &fInvalid); | |
441 | ||
442 | if (fInvalid) | |
443 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Malformed base64 encoding"); | |
444 | ||
445 | CHashWriter ss(SER_GETHASH, 0); | |
446 | ss << strMessageMagic; | |
447 | ss << strMessage; | |
448 | ||
449 | CPubKey pubkey; | |
450 | if (!pubkey.RecoverCompact(ss.GetHash(), vchSig)) | |
451 | return false; | |
452 | ||
07444da1 | 453 | return (pubkey.GetID() == *keyID); |
c3a7f516 | 454 | } |
a8b2ce55 | 455 | |
d014114d | 456 | UniValue setmocktime(const UniValue& params, bool fHelp) |
a8b2ce55 GA |
457 | { |
458 | if (fHelp || params.size() != 1) | |
459 | throw runtime_error( | |
460 | "setmocktime timestamp\n" | |
461 | "\nSet the local time to given timestamp (-regtest only)\n" | |
462 | "\nArguments:\n" | |
463 | "1. timestamp (integer, required) Unix seconds-since-epoch timestamp\n" | |
464 | " Pass 0 to go back to using the system time." | |
465 | ); | |
466 | ||
467 | if (!Params().MineBlocksOnDemand()) | |
468 | throw runtime_error("setmocktime for regression testing (-regtest mode) only"); | |
469 | ||
abb0e8cc GA |
470 | // cs_vNodes is locked and node send/receive times are updated |
471 | // atomically with the time change to prevent peers from being | |
472 | // disconnected because we think we haven't communicated with them | |
473 | // in a long time. | |
474 | LOCK2(cs_main, cs_vNodes); | |
4401b2d7 | 475 | |
9756b7bd | 476 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VNUM)); |
a8b2ce55 GA |
477 | SetMockTime(params[0].get_int64()); |
478 | ||
abb0e8cc GA |
479 | uint64_t t = GetTime(); |
480 | BOOST_FOREACH(CNode* pnode, vNodes) { | |
481 | pnode->nLastSend = pnode->nLastRecv = t; | |
482 | } | |
483 | ||
9756b7bd | 484 | return NullUniValue; |
a8b2ce55 | 485 | } |
a9496b08 | 486 | |
86b23f37 | 487 | // insightexplorer |
68e174e2 LR |
488 | static bool getAddressFromIndex( |
489 | int type, const uint160 &hash, std::string &address) | |
490 | { | |
491 | if (type == CScript::P2SH) { | |
492 | address = EncodeDestination(CScriptID(hash)); | |
493 | } else if (type == CScript::P2PKH) { | |
494 | address = EncodeDestination(CKeyID(hash)); | |
495 | } else { | |
496 | return false; | |
497 | } | |
498 | return true; | |
499 | } | |
500 | ||
501 | // This function accepts an address and returns in the output parameters | |
502 | // the version and raw bytes for the RIPEMD-160 hash. | |
503 | static bool getIndexKey( | |
504 | const CTxDestination& dest, uint160& hashBytes, int& type) | |
505 | { | |
506 | if (!IsValidDestination(dest)) { | |
507 | return false; | |
508 | } | |
509 | if (dest.type() == typeid(CKeyID)) { | |
510 | auto x = boost::get<CKeyID>(&dest); | |
511 | memcpy(&hashBytes, x->begin(), 20); | |
512 | type = CScript::P2PKH; | |
513 | return true; | |
514 | } | |
515 | if (dest.type() == typeid(CScriptID)) { | |
516 | auto x = boost::get<CScriptID>(&dest); | |
517 | memcpy(&hashBytes, x->begin(), 20); | |
518 | type = CScript::P2SH; | |
519 | return true; | |
520 | } | |
521 | return false; | |
522 | } | |
523 | ||
86b23f37 | 524 | // insightexplorer |
68e174e2 LR |
525 | static bool getAddressesFromParams( |
526 | const UniValue& params, | |
527 | std::vector<std::pair<uint160, int>> &addresses) | |
528 | { | |
529 | std::vector<std::string> param_addresses; | |
530 | if (params[0].isStr()) { | |
531 | param_addresses.push_back(params[0].get_str()); | |
532 | } else if (params[0].isObject()) { | |
533 | UniValue addressValues = find_value(params[0].get_obj(), "addresses"); | |
534 | if (!addressValues.isArray()) { | |
535 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | |
536 | "Addresses is expected to be an array"); | |
537 | } | |
538 | for (const auto& it : addressValues.getValues()) { | |
539 | param_addresses.push_back(it.get_str()); | |
540 | } | |
541 | ||
542 | } else { | |
543 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); | |
544 | } | |
545 | for (const auto& it : param_addresses) { | |
546 | CTxDestination address = DecodeDestination(it); | |
547 | uint160 hashBytes; | |
548 | int type = 0; | |
549 | if (!getIndexKey(address, hashBytes, type)) { | |
550 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); | |
551 | } | |
552 | addresses.push_back(std::make_pair(hashBytes, type)); | |
553 | } | |
554 | return true; | |
555 | } | |
556 | ||
86b23f37 | 557 | // insightexplorer |
68e174e2 LR |
558 | UniValue getaddressmempool(const UniValue& params, bool fHelp) |
559 | { | |
560 | std::string enableArg = "insightexplorer"; | |
86b23f37 | 561 | bool enabled = fExperimentalMode && fInsightExplorer; |
68e174e2 | 562 | std::string disabledMsg = ""; |
86b23f37 | 563 | if (!enabled) { |
68e174e2 LR |
564 | disabledMsg = experimentalDisabledHelpMsg("getaddressmempool", enableArg); |
565 | } | |
566 | if (fHelp || params.size() != 1) | |
567 | throw runtime_error( | |
568 | "getaddressmempool {\"addresses\": [\"taddr\", ...]}\n" | |
569 | "\nReturns all mempool deltas for an address.\n" | |
570 | + disabledMsg + | |
571 | "\nArguments:\n" | |
572 | "{\n" | |
573 | " \"addresses\":\n" | |
574 | " [\n" | |
575 | " \"address\" (string) The base58check encoded address\n" | |
576 | " ,...\n" | |
577 | " ]\n" | |
578 | "}\n" | |
579 | "(or)\n" | |
580 | "\"address\" (string) The base58check encoded address\n" | |
581 | "\nResult:\n" | |
582 | "[\n" | |
583 | " {\n" | |
584 | " \"address\" (string) The base58check encoded address\n" | |
585 | " \"txid\" (string) The related txid\n" | |
586 | " \"index\" (number) The related input or output index\n" | |
587 | " \"satoshis\" (number) The difference of zatoshis\n" | |
588 | " \"timestamp\" (number) The time the transaction entered the mempool (seconds)\n" | |
589 | " \"prevtxid\" (string) The previous txid (if spending)\n" | |
590 | " \"prevout\" (string) The previous transaction output index (if spending)\n" | |
591 | " }\n" | |
592 | "]\n" | |
593 | "\nExamples:\n" | |
594 | + HelpExampleCli("getaddressmempool", "'{\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"]}'") | |
595 | + HelpExampleRpc("getaddressmempool", "{\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"]}") | |
596 | ); | |
597 | ||
86b23f37 | 598 | if (!enabled) { |
68e174e2 LR |
599 | throw JSONRPCError(RPC_MISC_ERROR, "Error: getaddressmempool is disabled. " |
600 | "Run './zcash-cli help getaddressmempool' for instructions on how to enable this feature."); | |
601 | } | |
602 | ||
603 | std::vector<std::pair<uint160, int>> addresses; | |
604 | ||
605 | if (!getAddressesFromParams(params, addresses)) { | |
606 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); | |
607 | } | |
608 | std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> indexes; | |
609 | mempool.getAddressIndex(addresses, indexes); | |
610 | std::sort(indexes.begin(), indexes.end(), | |
611 | [](const std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>& a, | |
612 | const std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>& b) -> bool { | |
613 | return a.second.time < b.second.time; | |
614 | }); | |
615 | ||
616 | UniValue result(UniValue::VARR); | |
617 | ||
618 | for (const auto& it : indexes) { | |
619 | std::string address; | |
620 | if (!getAddressFromIndex(it.first.type, it.first.addressBytes, address)) { | |
621 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type"); | |
622 | } | |
623 | UniValue delta(UniValue::VOBJ); | |
624 | delta.push_back(Pair("address", address)); | |
625 | delta.push_back(Pair("txid", it.first.txhash.GetHex())); | |
626 | delta.push_back(Pair("index", (int)it.first.index)); | |
627 | delta.push_back(Pair("satoshis", it.second.amount)); | |
628 | delta.push_back(Pair("timestamp", it.second.time)); | |
629 | if (it.second.amount < 0) { | |
630 | delta.push_back(Pair("prevtxid", it.second.prevhash.GetHex())); | |
631 | delta.push_back(Pair("prevout", (int)it.second.prevout)); | |
632 | } | |
633 | result.push_back(delta); | |
634 | } | |
635 | return result; | |
636 | } | |
637 | ||
86b23f37 | 638 | // insightexplorer |
68e174e2 LR |
639 | UniValue getaddressutxos(const UniValue& params, bool fHelp) |
640 | { | |
641 | std::string enableArg = "insightexplorer"; | |
86b23f37 | 642 | bool enabled = fExperimentalMode && fInsightExplorer; |
68e174e2 | 643 | std::string disabledMsg = ""; |
86b23f37 | 644 | if (!enabled) { |
68e174e2 LR |
645 | disabledMsg = experimentalDisabledHelpMsg("getaddressutxos", enableArg); |
646 | } | |
647 | if (fHelp || params.size() != 1) | |
648 | throw runtime_error( | |
649 | "getaddressutxos {\"addresses\": [\"taddr\", ...], (\"chainInfo\": true|false)}\n" | |
650 | "\nReturns all unspent outputs for an address.\n" | |
651 | + disabledMsg + | |
652 | "\nArguments:\n" | |
653 | "{\n" | |
654 | " \"addresses\":\n" | |
655 | " [\n" | |
656 | " \"address\" (string) The base58check encoded address\n" | |
657 | " ,...\n" | |
658 | " ],\n" | |
659 | " \"chainInfo\" (boolean, optional, default=false) Include chain info with results\n" | |
660 | "}\n" | |
661 | "(or)\n" | |
662 | "\"address\" (string) The base58check encoded address\n" | |
663 | "\nResult\n" | |
664 | "[\n" | |
665 | " {\n" | |
666 | " \"address\" (string) The address base58check encoded\n" | |
667 | " \"txid\" (string) The output txid\n" | |
668 | " \"height\" (number) The block height\n" | |
669 | " \"outputIndex\" (number) The output index\n" | |
670 | " \"script\" (string) The script hex encoded\n" | |
671 | " \"satoshis\" (number) The number of zatoshis of the output\n" | |
672 | " }, ...\n" | |
673 | "]\n\n" | |
674 | "(or, if chainInfo is true):\n\n" | |
675 | "{\n" | |
676 | " \"utxos\":\n" | |
677 | " [\n" | |
678 | " {\n" | |
679 | " \"address\" (string) The address base58check encoded\n" | |
680 | " \"txid\" (string) The output txid\n" | |
681 | " \"height\" (number) The block height\n" | |
682 | " \"outputIndex\" (number) The output index\n" | |
683 | " \"script\" (string) The script hex encoded\n" | |
684 | " \"satoshis\" (number) The number of zatoshis of the output\n" | |
685 | " }, ...\n" | |
686 | " ],\n" | |
687 | " \"hash\" (string) The block hash\n" | |
688 | " \"height\" (numeric) The block height\n" | |
689 | "}\n" | |
690 | "\nExamples:\n" | |
691 | + HelpExampleCli("getaddressutxos", "'{\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"], \"chainInfo\": true}'") | |
692 | + HelpExampleRpc("getaddressutxos", "{\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"], \"chainInfo\": true}") | |
693 | ); | |
694 | ||
86b23f37 | 695 | if (!enabled) { |
68e174e2 LR |
696 | throw JSONRPCError(RPC_MISC_ERROR, "Error: getaddressutxos is disabled. " |
697 | "Run './zcash-cli help getaddressutxos' for instructions on how to enable this feature."); | |
698 | } | |
699 | ||
700 | bool includeChainInfo = false; | |
701 | if (params[0].isObject()) { | |
702 | UniValue chainInfo = find_value(params[0].get_obj(), "chainInfo"); | |
703 | if (!chainInfo.isNull()) { | |
704 | includeChainInfo = chainInfo.get_bool(); | |
705 | } | |
706 | } | |
707 | std::vector<std::pair<uint160, int>> addresses; | |
708 | if (!getAddressesFromParams(params, addresses)) { | |
709 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); | |
710 | } | |
711 | std::vector<CAddressUnspentDbEntry> unspentOutputs; | |
712 | for (const auto& it : addresses) { | |
713 | if (!GetAddressUnspent(it.first, it.second, unspentOutputs)) { | |
714 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available for address"); | |
715 | } | |
716 | } | |
717 | std::sort(unspentOutputs.begin(), unspentOutputs.end(), | |
718 | [](const CAddressUnspentDbEntry& a, const CAddressUnspentDbEntry& b) -> bool { | |
719 | return a.second.blockHeight < b.second.blockHeight; | |
720 | }); | |
721 | ||
722 | UniValue utxos(UniValue::VARR); | |
723 | for (const auto& it : unspentOutputs) { | |
724 | UniValue output(UniValue::VOBJ); | |
725 | std::string address; | |
726 | if (!getAddressFromIndex(it.first.type, it.first.hashBytes, address)) { | |
727 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type"); | |
728 | } | |
729 | ||
730 | output.push_back(Pair("address", address)); | |
731 | output.push_back(Pair("txid", it.first.txhash.GetHex())); | |
732 | output.push_back(Pair("outputIndex", (int)it.first.index)); | |
733 | output.push_back(Pair("script", HexStr(it.second.script.begin(), it.second.script.end()))); | |
734 | output.push_back(Pair("satoshis", it.second.satoshis)); | |
735 | output.push_back(Pair("height", it.second.blockHeight)); | |
736 | utxos.push_back(output); | |
737 | } | |
738 | ||
739 | if (!includeChainInfo) | |
740 | return utxos; | |
741 | ||
742 | UniValue result(UniValue::VOBJ); | |
743 | result.push_back(Pair("utxos", utxos)); | |
744 | ||
745 | LOCK(cs_main); // for chainActive | |
746 | result.push_back(Pair("hash", chainActive.Tip()->GetBlockHash().GetHex())); | |
747 | result.push_back(Pair("height", (int)chainActive.Height())); | |
748 | return result; | |
749 | } | |
750 | ||
751 | static void getHeightRange(const UniValue& params, int& start, int& end) | |
752 | { | |
753 | start = 0; | |
754 | end = 0; | |
755 | if (params[0].isObject()) { | |
756 | UniValue startValue = find_value(params[0].get_obj(), "start"); | |
757 | UniValue endValue = find_value(params[0].get_obj(), "end"); | |
758 | // If either is not specified, the other is ignored. | |
759 | if (!startValue.isNull() && !endValue.isNull()) { | |
760 | start = startValue.get_int(); | |
761 | end = endValue.get_int(); | |
762 | if (start <= 0 || end <= 0) { | |
763 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | |
764 | "Start and end are expected to be greater than zero"); | |
765 | } | |
766 | if (end < start) { | |
767 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | |
768 | "End value is expected to be greater than start"); | |
769 | } | |
770 | } | |
771 | } | |
772 | ||
773 | LOCK(cs_main); // for chainActive | |
774 | if (start > chainActive.Height() || end > chainActive.Height()) { | |
775 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Start or end is outside chain range"); | |
776 | } | |
777 | } | |
778 | ||
779 | // Parse an address list then fetch the corresponding addressindex information. | |
780 | static void getAddressesInHeightRange( | |
781 | const UniValue& params, | |
782 | int start, int end, | |
783 | std::vector<std::pair<uint160, int>>& addresses, | |
784 | std::vector<std::pair<CAddressIndexKey, CAmount>> &addressIndex) | |
785 | { | |
786 | if (!getAddressesFromParams(params, addresses)) { | |
787 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address"); | |
788 | } | |
789 | for (const auto& it : addresses) { | |
790 | if (!GetAddressIndex(it.first, it.second, addressIndex, start, end)) { | |
791 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, | |
792 | "No information available for address"); | |
793 | } | |
794 | } | |
795 | } | |
796 | ||
86b23f37 | 797 | // insightexplorer |
68e174e2 LR |
798 | UniValue getaddressdeltas(const UniValue& params, bool fHelp) |
799 | { | |
800 | std::string enableArg = "insightexplorer"; | |
86b23f37 | 801 | bool enabled = fExperimentalMode && fInsightExplorer; |
68e174e2 | 802 | std::string disabledMsg = ""; |
86b23f37 | 803 | if (!enabled) { |
68e174e2 LR |
804 | disabledMsg = experimentalDisabledHelpMsg("getaddressdeltas", enableArg); |
805 | } | |
806 | if (fHelp || params.size() != 1) | |
807 | throw runtime_error( | |
808 | "getaddressdeltas {\"addresses\": [\"taddr\", ...], (\"start\": n), (\"end\": n), (\"chainInfo\": true|false)}\n" | |
809 | "\nReturns all changes for an address.\n" | |
810 | "\nReturns information about all changes to the given transparent addresses within the given (inclusive)\n" | |
811 | "\nblock height range, default is the full blockchain.\n" | |
812 | + disabledMsg + | |
813 | "\nArguments:\n" | |
814 | "{\n" | |
815 | " \"addresses\":\n" | |
816 | " [\n" | |
817 | " \"address\" (string) The base58check encoded address\n" | |
818 | " ,...\n" | |
819 | " ]\n" | |
820 | " \"start\" (number, optional) The start block height\n" | |
821 | " \"end\" (number, optional) The end block height\n" | |
822 | " \"chainInfo\" (boolean, optional, default=false) Include chain info in results, only applies if start and end specified\n" | |
823 | "}\n" | |
824 | "(or)\n" | |
825 | "\"address\" (string) The base58check encoded address\n" | |
826 | "\nResult:\n" | |
827 | "[\n" | |
828 | " {\n" | |
829 | " \"satoshis\" (number) The difference of zatoshis\n" | |
830 | " \"txid\" (string) The related txid\n" | |
831 | " \"index\" (number) The related input or output index\n" | |
832 | " \"height\" (number) The block height\n" | |
833 | " \"address\" (string) The base58check encoded address\n" | |
834 | " }, ...\n" | |
835 | "]\n\n" | |
836 | "(or, if chainInfo is true):\n\n" | |
837 | "{\n" | |
838 | " \"deltas\":\n" | |
839 | " [\n" | |
840 | " {\n" | |
841 | " \"satoshis\" (number) The difference of zatoshis\n" | |
842 | " \"txid\" (string) The related txid\n" | |
843 | " \"index\" (number) The related input or output index\n" | |
844 | " \"height\" (number) The block height\n" | |
845 | " \"address\" (string) The address base58check encoded\n" | |
846 | " }, ...\n" | |
847 | " ],\n" | |
848 | " \"start\":\n" | |
849 | " {\n" | |
850 | " \"hash\" (string) The start block hash\n" | |
851 | " \"height\" (numeric) The height of the start block\n" | |
852 | " }\n" | |
853 | " \"end\":\n" | |
854 | " {\n" | |
855 | " \"hash\" (string) The end block hash\n" | |
856 | " \"height\" (numeric) The height of the end block\n" | |
857 | " }\n" | |
858 | "}\n" | |
859 | "\nExamples:\n" | |
860 | + HelpExampleCli("getaddressdeltas", "'{\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"], \"start\": 1000, \"end\": 2000, \"chainInfo\": true}'") | |
861 | + HelpExampleRpc("getaddressdeltas", "{\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"], \"start\": 1000, \"end\": 2000, \"chainInfo\": true}") | |
862 | ); | |
863 | ||
86b23f37 | 864 | if (!enabled) { |
68e174e2 LR |
865 | throw JSONRPCError(RPC_MISC_ERROR, "Error: getaddressdeltas is disabled. " |
866 | "Run './zcash-cli help getaddressdeltas' for instructions on how to enable this feature."); | |
867 | } | |
868 | ||
869 | int start = 0; | |
870 | int end = 0; | |
871 | getHeightRange(params, start, end); | |
872 | ||
873 | std::vector<std::pair<uint160, int>> addresses; | |
874 | std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex; | |
875 | getAddressesInHeightRange(params, start, end, addresses, addressIndex); | |
876 | ||
877 | bool includeChainInfo = false; | |
878 | if (params[0].isObject()) { | |
879 | UniValue chainInfo = find_value(params[0].get_obj(), "chainInfo"); | |
880 | if (!chainInfo.isNull()) { | |
881 | includeChainInfo = chainInfo.get_bool(); | |
882 | } | |
883 | } | |
884 | ||
885 | UniValue deltas(UniValue::VARR); | |
886 | for (const auto& it : addressIndex) { | |
887 | std::string address; | |
888 | if (!getAddressFromIndex(it.first.type, it.first.hashBytes, address)) { | |
889 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unknown address type"); | |
890 | } | |
891 | ||
892 | UniValue delta(UniValue::VOBJ); | |
893 | delta.push_back(Pair("address", address)); | |
894 | delta.push_back(Pair("blockindex", (int)it.first.txindex)); | |
895 | delta.push_back(Pair("height", it.first.blockHeight)); | |
896 | delta.push_back(Pair("index", (int)it.first.index)); | |
897 | delta.push_back(Pair("satoshis", it.second)); | |
898 | delta.push_back(Pair("txid", it.first.txhash.GetHex())); | |
899 | deltas.push_back(delta); | |
900 | } | |
901 | ||
902 | UniValue result(UniValue::VOBJ); | |
903 | ||
904 | if (!(includeChainInfo && start > 0 && end > 0)) { | |
905 | return deltas; | |
906 | } | |
907 | ||
908 | UniValue startInfo(UniValue::VOBJ); | |
909 | UniValue endInfo(UniValue::VOBJ); | |
910 | { | |
911 | LOCK(cs_main); // for chainActive | |
912 | if (start > chainActive.Height() || end > chainActive.Height()) { | |
913 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Start or end is outside chain range"); | |
914 | } | |
915 | startInfo.push_back(Pair("hash", chainActive[start]->GetBlockHash().GetHex())); | |
916 | endInfo.push_back(Pair("hash", chainActive[end]->GetBlockHash().GetHex())); | |
917 | } | |
918 | startInfo.push_back(Pair("height", start)); | |
919 | endInfo.push_back(Pair("height", end)); | |
920 | ||
921 | result.push_back(Pair("deltas", deltas)); | |
922 | result.push_back(Pair("start", startInfo)); | |
923 | result.push_back(Pair("end", endInfo)); | |
924 | ||
925 | return result; | |
926 | } | |
927 | ||
86b23f37 | 928 | // insightexplorer |
68e174e2 LR |
929 | UniValue getaddressbalance(const UniValue& params, bool fHelp) |
930 | { | |
931 | std::string enableArg = "insightexplorer"; | |
86b23f37 | 932 | bool enabled = fExperimentalMode && fInsightExplorer; |
68e174e2 | 933 | std::string disabledMsg = ""; |
86b23f37 | 934 | if (!enabled) { |
68e174e2 LR |
935 | disabledMsg = experimentalDisabledHelpMsg("getaddressbalance", enableArg); |
936 | } | |
937 | if (fHelp || params.size() != 1) | |
938 | throw runtime_error( | |
939 | "getaddressbalance {\"addresses\": [\"taddr\", ...]}\n" | |
940 | "\nReturns the balance for addresses.\n" | |
941 | + disabledMsg + | |
942 | "\nArguments:\n" | |
943 | "{\n" | |
944 | " \"addresses:\"\n" | |
945 | " [\n" | |
946 | " \"address\" (string) The base58check encoded address\n" | |
947 | " ,...\n" | |
948 | " ]\n" | |
949 | "}\n" | |
950 | "(or)\n" | |
951 | "\"address\" (string) The base58check encoded address\n" | |
952 | "\nResult:\n" | |
953 | "{\n" | |
954 | " \"balance\" (string) The current balance in zatoshis\n" | |
955 | " \"received\" (string) The total number of zatoshis received (including change)\n" | |
956 | "}\n" | |
957 | "\nExamples:\n" | |
958 | + HelpExampleCli("getaddressbalance", "'{\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"]}'") | |
959 | + HelpExampleRpc("getaddressbalance", "{\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"]}") | |
960 | ); | |
961 | ||
86b23f37 | 962 | if (!enabled) { |
68e174e2 LR |
963 | throw JSONRPCError(RPC_MISC_ERROR, "Error: getaddressbalance is disabled. " |
964 | "Run './zcash-cli help getaddressbalance' for instructions on how to enable this feature."); | |
965 | } | |
966 | ||
967 | std::vector<std::pair<uint160, int>> addresses; | |
968 | std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex; | |
969 | // this method doesn't take start and end block height params, so set | |
970 | // to zero (full range, entire blockchain) | |
971 | getAddressesInHeightRange(params, 0, 0, addresses, addressIndex); | |
972 | ||
973 | CAmount balance = 0; | |
974 | CAmount received = 0; | |
975 | for (const auto& it : addressIndex) { | |
976 | if (it.second > 0) { | |
977 | received += it.second; | |
978 | } | |
979 | balance += it.second; | |
980 | } | |
981 | UniValue result(UniValue::VOBJ); | |
982 | result.push_back(Pair("balance", balance)); | |
983 | result.push_back(Pair("received", received)); | |
984 | return result; | |
985 | } | |
986 | ||
86b23f37 | 987 | // insightexplorer |
68e174e2 LR |
988 | UniValue getaddresstxids(const UniValue& params, bool fHelp) |
989 | { | |
990 | std::string enableArg = "insightexplorer"; | |
86b23f37 | 991 | bool enabled = fExperimentalMode && fInsightExplorer; |
68e174e2 | 992 | std::string disabledMsg = ""; |
86b23f37 | 993 | if (!enabled) { |
68e174e2 LR |
994 | disabledMsg = experimentalDisabledHelpMsg("getaddresstxids", enableArg); |
995 | } | |
996 | if (fHelp || params.size() != 1) | |
997 | throw runtime_error( | |
998 | "getaddresstxids {\"addresses\": [\"taddr\", ...], (\"start\": n), (\"end\": n)}\n" | |
999 | "\nReturns the txids for given transparent addresses within the given (inclusive)\n" | |
1000 | "\nblock height range, default is the full blockchain.\n" | |
1001 | + disabledMsg + | |
1002 | "\nArguments:\n" | |
1003 | "{\n" | |
1004 | " \"addresses\":\n" | |
1005 | " [\n" | |
1006 | " \"taddr\" (string) The base58check encoded address\n" | |
1007 | " ,...\n" | |
1008 | " ]\n" | |
1009 | " \"start\" (number, optional) The start block height\n" | |
1010 | " \"end\" (number, optional) The end block height\n" | |
1011 | "}\n" | |
1012 | "(or)\n" | |
1013 | "\"address\" (string) The base58check encoded address\n" | |
1014 | "\nResult:\n" | |
1015 | "[\n" | |
1016 | " \"transactionid\" (string) The transaction id\n" | |
1017 | " ,...\n" | |
1018 | "]\n" | |
1019 | "\nExamples:\n" | |
1020 | + HelpExampleCli("getaddresstxids", "'{\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"], \"start\": 1000, \"end\": 2000}'") | |
1021 | + HelpExampleRpc("getaddresstxids", "{\"addresses\": [\"tmYXBYJj1K7vhejSec5osXK2QsGa5MTisUQ\"], \"start\": 1000, \"end\": 2000}") | |
1022 | ); | |
1023 | ||
86b23f37 | 1024 | if (!enabled) { |
68e174e2 LR |
1025 | throw JSONRPCError(RPC_MISC_ERROR, "Error: getaddresstxids is disabled. " |
1026 | "Run './zcash-cli help getaddresstxids' for instructions on how to enable this feature."); | |
1027 | } | |
1028 | ||
1029 | int start = 0; | |
1030 | int end = 0; | |
1031 | getHeightRange(params, start, end); | |
1032 | ||
1033 | std::vector<std::pair<uint160, int>> addresses; | |
1034 | std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex; | |
1035 | getAddressesInHeightRange(params, start, end, addresses, addressIndex); | |
1036 | ||
1037 | // This is an ordered set, sorted by height, so result also sorted by height. | |
1038 | std::set<std::pair<int, std::string>> txids; | |
1039 | ||
1040 | for (const auto& it : addressIndex) { | |
1041 | const int height = it.first.blockHeight; | |
1042 | const std::string txid = it.first.txhash.GetHex(); | |
1043 | // Duplicate entries (two addresses in same tx) are suppressed | |
1044 | txids.insert(std::make_pair(height, txid)); | |
1045 | } | |
1046 | UniValue result(UniValue::VARR); | |
1047 | for (const auto& it : txids) { | |
1048 | // only push the txid, not the height | |
1049 | result.push_back(it.second); | |
1050 | } | |
1051 | return result; | |
1052 | } | |
1053 | ||
86b23f37 LR |
1054 | // insightexplorer |
1055 | UniValue getspentinfo(const UniValue& params, bool fHelp) | |
1056 | { | |
1057 | std::string enableArg = "insightexplorer"; | |
1058 | bool enabled = fExperimentalMode && fInsightExplorer; | |
1059 | std::string disabledMsg = ""; | |
1060 | if (!enabled) { | |
1061 | disabledMsg = experimentalDisabledHelpMsg("getspentinfo", enableArg); | |
1062 | } | |
1063 | if (fHelp || params.size() != 1 || !params[0].isObject()) | |
1064 | throw runtime_error( | |
1065 | "getspentinfo {\"txid\": \"txidhex\", \"index\": n}\n" | |
1066 | "\nReturns the txid and index where an output is spent.\n" | |
1067 | + disabledMsg + | |
1068 | "\nArguments:\n" | |
1069 | "{\n" | |
1070 | " \"txid\" (string) The hex string of the txid\n" | |
1071 | " \"index\" (number) The vout (output) index\n" | |
1072 | "}\n" | |
1073 | "\nResult:\n" | |
1074 | "{\n" | |
1075 | " \"txid\" (string) The transaction id\n" | |
1076 | " \"index\" (number) The spending (vin, input) index\n" | |
1077 | " ,...\n" | |
1078 | "}\n" | |
1079 | "\nExamples:\n" | |
1080 | + HelpExampleCli("getspentinfo", "'{\"txid\": \"0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9\", \"index\": 0}'") | |
1081 | + HelpExampleRpc("getspentinfo", "{\"txid\": \"0437cd7f8525ceed2324359c2d0ba26006d92d856a9c20fa0241106ee5a597c9\", \"index\": 0}") | |
1082 | ); | |
1083 | ||
1084 | if (!enabled) { | |
1085 | throw JSONRPCError(RPC_MISC_ERROR, "Error: getspentinfo is disabled. " | |
1086 | "Run './zcash-cli help getspentinfo' for instructions on how to enable this feature."); | |
1087 | } | |
1088 | ||
1089 | UniValue txidValue = find_value(params[0].get_obj(), "txid"); | |
1090 | UniValue indexValue = find_value(params[0].get_obj(), "index"); | |
1091 | ||
1092 | if (!txidValue.isStr()) | |
1093 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid txid, must be a string"); | |
1094 | if (!indexValue.isNum()) | |
1095 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid index, must be an integer"); | |
1096 | uint256 txid = ParseHashV(txidValue, "txid"); | |
1097 | int outputIndex = indexValue.get_int(); | |
1098 | ||
1099 | CSpentIndexKey key(txid, outputIndex); | |
1100 | CSpentIndexValue value; | |
1101 | ||
1102 | if (!GetSpentIndex(key, value)) { | |
1103 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Unable to get spent info"); | |
1104 | } | |
1105 | UniValue obj(UniValue::VOBJ); | |
1106 | obj.push_back(Pair("txid", value.txid.GetHex())); | |
1107 | obj.push_back(Pair("index", (int)value.inputIndex)); | |
1108 | obj.push_back(Pair("height", value.blockHeight)); | |
1109 | ||
1110 | return obj; | |
1111 | } | |
68e174e2 | 1112 | |
a9496b08 WL |
1113 | static const CRPCCommand commands[] = |
1114 | { // category name actor (function) okSafeMode | |
1115 | // --------------------- ------------------------ ----------------------- ---------- | |
1116 | { "control", "getinfo", &getinfo, true }, /* uses wallet if enabled */ | |
1117 | { "util", "validateaddress", &validateaddress, true }, /* uses wallet if enabled */ | |
1118 | { "util", "z_validateaddress", &z_validateaddress, true }, /* uses wallet if enabled */ | |
1119 | { "util", "createmultisig", &createmultisig, true }, | |
1120 | { "util", "verifymessage", &verifymessage, true }, | |
1121 | ||
86b23f37 | 1122 | // START insightexplorer |
68e174e2 LR |
1123 | /* Address index */ |
1124 | { "addressindex", "getaddresstxids", &getaddresstxids, false }, /* insight explorer */ | |
1125 | { "addressindex", "getaddressbalance", &getaddressbalance, false }, /* insight explorer */ | |
1126 | { "addressindex", "getaddressdeltas", &getaddressdeltas, false }, /* insight explorer */ | |
1127 | { "addressindex", "getaddressutxos", &getaddressutxos, false }, /* insight explorer */ | |
1128 | { "addressindex", "getaddressmempool", &getaddressmempool, true }, /* insight explorer */ | |
86b23f37 LR |
1129 | { "blockchain", "getspentinfo", &getspentinfo, false }, /* insight explorer */ |
1130 | // END insightexplorer | |
68e174e2 | 1131 | |
a9496b08 WL |
1132 | /* Not shown in help */ |
1133 | { "hidden", "setmocktime", &setmocktime, true }, | |
1134 | }; | |
1135 | ||
1136 | void RegisterMiscRPCCommands(CRPCTable &tableRPC) | |
1137 | { | |
1138 | for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) | |
1139 | tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); | |
1140 | } |