1 // Copyright (c) 2014-2020 Daniel Kraft
2 // Copyright (c) 2020 yanmaani
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include <base58.h>
7 #include <chainparams.h>
8 #include <core_io.h>
9 #include <init.h>
10 #include <index/namehash.h>
11 #include <key_io.h>
12 #include <names/common.h>
13 #include <names/main.h>
14 #include <node/context.h>
15 #include <primitives/transaction.h>
16 #include <psbt.h>
17 #include <rpc/blockchain.h>
18 #include <rpc/names.h>
19 #include <rpc/server.h>
20 #include <script/names.h>
21 #include <txmempool.h>
22 #include <util/strencodings.h>
23 #include <validation.h>
24 #ifdef ENABLE_WALLET
25 # include <wallet/rpcwallet.h>
26 # include <wallet/wallet.h>
27 #endif
28
29 #include <univalue.h>
30
31 #include <boost/xpressive/xpressive_dynamic.hpp>
32
33 #include <algorithm>
34 #include <cassert>
35 #include <memory>
36 #include <stdexcept>
37
38 namespace
39 {
40
41 NameEncoding
EncodingFromOptionsJson(const UniValue & options,const std::string & field,const NameEncoding defaultValue)42 EncodingFromOptionsJson (const UniValue& options, const std::string& field,
43 const NameEncoding defaultValue)
44 {
45 NameEncoding res = defaultValue;
46 RPCTypeCheckObj (options,
47 {
48 {field, UniValueType (UniValue::VSTR)},
49 },
50 true, false);
51 if (options.exists (field))
52 try
53 {
54 res = EncodingFromString (options[field].get_str ());
55 }
56 catch (const std::invalid_argument& exc)
57 {
58 LogPrintf ("Invalid value for %s in options: %s\n using default %s\n",
59 field, exc.what (), EncodingToString (defaultValue));
60 }
61
62 return res;
63 }
64
65 } // anonymous namespace
66
67 /**
68 * Utility routine to construct a "name info" object to return. This is used
69 * for name_show and also name_list.
70 */
71 UniValue
getNameInfo(const UniValue & options,const valtype & name,const valtype & value,const COutPoint & outp,const CScript & addr)72 getNameInfo (const UniValue& options,
73 const valtype& name, const valtype& value,
74 const COutPoint& outp, const CScript& addr)
75 {
76 UniValue obj(UniValue::VOBJ);
77 AddEncodedNameToUniv (obj, "name", name,
78 EncodingFromOptionsJson (options, "nameEncoding",
79 ConfiguredNameEncoding ()));
80 AddEncodedNameToUniv (obj, "value", value,
81 EncodingFromOptionsJson (options, "valueEncoding",
82 ConfiguredValueEncoding ()));
83 obj.pushKV ("txid", outp.hash.GetHex ());
84 obj.pushKV ("vout", static_cast<int> (outp.n));
85
86 /* Try to extract the address. May fail if we can't parse the script
87 as a "standard" script. */
88 CTxDestination dest;
89 std::string addrStr;
90 if (ExtractDestination (addr, dest))
91 addrStr = EncodeDestination (dest);
92 else
93 addrStr = "<nonstandard>";
94 obj.pushKV ("address", addrStr);
95
96 return obj;
97 }
98
99 /**
100 * Return name info object for a CNameData object.
101 */
102 UniValue
getNameInfo(const UniValue & options,const valtype & name,const CNameData & data)103 getNameInfo (const UniValue& options,
104 const valtype& name, const CNameData& data)
105 {
106 UniValue result = getNameInfo (options,
107 name, data.getValue (),
108 data.getUpdateOutpoint (),
109 data.getAddress ());
110 addExpirationInfo (data.getHeight (), result);
111 return result;
112 }
113
114 /**
115 * Adds expiration information to the JSON object, based on the last-update
116 * height for the name given.
117 */
118 void
addExpirationInfo(const int height,UniValue & data)119 addExpirationInfo (const int height, UniValue& data)
120 {
121 const int curHeight = ::ChainActive ().Height ();
122 const Consensus::Params& params = Params ().GetConsensus ();
123 const int expireDepth = params.rules->NameExpirationDepth (curHeight);
124 const int expireHeight = height + expireDepth;
125 const int expiresIn = expireHeight - curHeight;
126 const bool expired = (expiresIn <= 0);
127 data.pushKV ("height", height);
128 data.pushKV ("expires_in", expiresIn);
129 data.pushKV ("expired", expired);
130 }
131
132 #ifdef ENABLE_WALLET
133 /**
134 * Adds the "ismine" field giving ownership info to the JSON object.
135 */
136 void
addOwnershipInfo(const CScript & addr,const CWallet * pwallet,UniValue & data)137 addOwnershipInfo (const CScript& addr, const CWallet* pwallet,
138 UniValue& data)
139 {
140 if (pwallet == nullptr)
141 return;
142
143 AssertLockHeld (pwallet->cs_wallet);
144 const isminetype mine = pwallet->IsMine (addr);
145 const bool isMine = (mine & ISMINE_SPENDABLE);
146 data.pushKV ("ismine", isMine);
147 }
148 #endif
149
150 namespace
151 {
152
153 valtype
DecodeNameValueFromRPCOrThrow(const UniValue & val,const UniValue & opt,const std::string & optKey,const NameEncoding defaultEnc)154 DecodeNameValueFromRPCOrThrow (const UniValue& val, const UniValue& opt,
155 const std::string& optKey,
156 const NameEncoding defaultEnc)
157 {
158 const NameEncoding enc = EncodingFromOptionsJson (opt, optKey, defaultEnc);
159 try
160 {
161 return DecodeName (val.get_str (), enc);
162 }
163 catch (const InvalidNameString& exc)
164 {
165 std::ostringstream msg;
166 msg << "Name/value is invalid for encoding " << EncodingToString (enc);
167 throw JSONRPCError (RPC_NAME_INVALID_ENCODING, msg.str ());
168 }
169 }
170
171 } // anonymous namespace
172
173 valtype
DecodeNameFromRPCOrThrow(const UniValue & val,const UniValue & opt)174 DecodeNameFromRPCOrThrow (const UniValue& val, const UniValue& opt)
175 {
176 return DecodeNameValueFromRPCOrThrow (val, opt, "nameEncoding",
177 ConfiguredNameEncoding ());
178 }
179
180 valtype
DecodeValueFromRPCOrThrow(const UniValue & val,const UniValue & opt)181 DecodeValueFromRPCOrThrow (const UniValue& val, const UniValue& opt)
182 {
183 return DecodeNameValueFromRPCOrThrow (val, opt, "valueEncoding",
184 ConfiguredValueEncoding ());
185 }
186
187 namespace
188 {
189
190 /**
191 * Decodes the identifier for a name lookup according to the nameEncoding,
192 * and also looks up the preimage if we look up by hash.
193 */
194 valtype
GetNameForLookup(const UniValue & val,const UniValue & opt)195 GetNameForLookup (const UniValue& val, const UniValue& opt)
196 {
197 const valtype identifier = DecodeNameFromRPCOrThrow (val, opt);
198
199 RPCTypeCheckObj (opt,
200 {
201 {"byHash", UniValueType (UniValue::VSTR)},
202 },
203 true, false);
204
205 if (!opt.exists ("byHash"))
206 return identifier;
207
208 const std::string byHashType = opt["byHash"].get_str ();
209 if (byHashType == "direct")
210 return identifier;
211
212 if (g_name_hash_index == nullptr)
213 throw std::runtime_error ("-namehashindex is not enabled");
214 if (!g_name_hash_index->BlockUntilSyncedToCurrentChain ())
215 throw std::runtime_error ("The name-hash index is not caught up yet");
216
217 if (byHashType != "sha256d")
218 {
219 std::ostringstream msg;
220 msg << "Invalid value for byHash: " << byHashType;
221 throw JSONRPCError (RPC_INVALID_PARAMETER, msg.str ());
222 }
223
224 if (identifier.size () != 32)
225 throw JSONRPCError (RPC_INVALID_PARAMETER,
226 "SHA-256d hash must be 32 bytes long");
227
228 const uint256 hash(identifier);
229 valtype name;
230 if (!g_name_hash_index->FindNamePreimage (hash, name))
231 {
232 std::ostringstream msg;
233 msg << "name hash not found: " << hash.GetHex ();
234 throw JSONRPCError (RPC_WALLET_ERROR, msg.str ());
235 }
236
237 return name;
238 }
239
240 /**
241 * Helper class that extracts the wallet for the current RPC request, if any.
242 * It handles the case of disabled wallet support or no wallet being present,
243 * so that it is suitable for the non-wallet RPCs here where we just want to
244 * provide optional extra features (like the "ismine" field).
245 *
246 * The main benefit of having this class is that we can easily LOCK2 with the
247 * wallet and another lock we need, without having to care about the special
248 * cases where no wallet is present or wallet support is disabled.
249 */
250 class MaybeWalletForRequest
251 {
252
253 private:
254
255 #ifdef ENABLE_WALLET
256 std::shared_ptr<CWallet> wallet;
257 #endif
258
259 public:
260
MaybeWalletForRequest(const JSONRPCRequest & request)261 explicit MaybeWalletForRequest (const JSONRPCRequest& request)
262 {
263 #ifdef ENABLE_WALLET
264 try
265 {
266 wallet = GetWalletForJSONRPCRequest (request);
267 }
268 catch (const UniValue& exc)
269 {
270 const auto& code = exc["code"];
271 if (!code.isNum () || code.get_int () != RPC_WALLET_NOT_SPECIFIED)
272 throw;
273
274 /* If the wallet is not set, that's fine, and we just indicate it to
275 other code (by having a null wallet). */
276 wallet = nullptr;
277 }
278 #endif
279 }
280
281 RecursiveMutex*
getLock() const282 getLock () const
283 {
284 #ifdef ENABLE_WALLET
285 return (wallet != nullptr ? &wallet->cs_wallet : nullptr);
286 #else
287 return nullptr;
288 #endif
289 }
290
291 #ifdef ENABLE_WALLET
292 CWallet*
getWallet()293 getWallet ()
294 {
295 return wallet.get ();
296 }
297
298 const CWallet*
getWallet() const299 getWallet () const
300 {
301 return wallet.get ();
302 }
303 #endif
304
305 };
306
307 /**
308 * Variant of addOwnershipInfo that uses a MaybeWalletForRequest. This takes
309 * care of disabled wallet support.
310 */
311 void
addOwnershipInfo(const CScript & addr,const MaybeWalletForRequest & wallet,UniValue & data)312 addOwnershipInfo (const CScript& addr, const MaybeWalletForRequest& wallet,
313 UniValue& data)
314 {
315 #ifdef ENABLE_WALLET
316 addOwnershipInfo (addr, wallet.getWallet (), data);
317 #endif
318 }
319
320 /**
321 * Utility variant of getNameInfo that already includes ownership information.
322 * This is the most common call for methods in this file.
323 */
324 UniValue
getNameInfo(const UniValue & options,const valtype & name,const CNameData & data,const MaybeWalletForRequest & wallet)325 getNameInfo (const UniValue& options,
326 const valtype& name, const CNameData& data,
327 const MaybeWalletForRequest& wallet)
328 {
329 UniValue res = getNameInfo (options, name, data);
330 addOwnershipInfo (data.getAddress (), wallet, res);
331 return res;
332 }
333
334 } // anonymous namespace
335
336 /* ************************************************************************** */
337
NameInfoHelp()338 NameInfoHelp::NameInfoHelp ()
339 {
340 withField ({RPCResult::Type::STR, "name", "the requested name"});
341 withField ({RPCResult::Type::STR, "name_encoding", "the encoding of \"name\""});
342 withField ({RPCResult::Type::STR, "name_error",
343 "replaces \"name\" in case there is an error"});
344 withField ({RPCResult::Type::STR, "value", "the name's current value"});
345 withField ({RPCResult::Type::STR, "value_encoding", "the encoding of \"value\""});
346 withField ({RPCResult::Type::STR, "value_error",
347 "replaces \"value\" in case there is an error"});
348
349 withField ({RPCResult::Type::STR_HEX, "txid", "the name's last update tx"});
350 withField ({RPCResult::Type::NUM, "vout",
351 "the index of the name output in the last update"});
352 withField ({RPCResult::Type::STR, "address", "the address holding the name"});
353 #ifdef ENABLE_WALLET
354 withField ({RPCResult::Type::BOOL, "ismine",
355 "whether the name is owned by the wallet"});
356 #endif
357 }
358
359 NameInfoHelp&
withExpiration()360 NameInfoHelp::withExpiration ()
361 {
362 withField ({RPCResult::Type::NUM, "height", "the name's last update height"});
363 withField ({RPCResult::Type::NUM, "expires_in", "expire counter for the name"});
364 withField ({RPCResult::Type::BOOL, "expired", "whether the name is expired"});
365 return *this;
366 }
367
NameOptionsHelp()368 NameOptionsHelp::NameOptionsHelp ()
369 {}
370
371 NameOptionsHelp&
withArg(const std::string & name,const RPCArg::Type type,const std::string & doc)372 NameOptionsHelp::withArg (const std::string& name, const RPCArg::Type type,
373 const std::string& doc)
374 {
375 return withArg (name, type, "", doc);
376 }
377
378 NameOptionsHelp&
withArg(const std::string & name,const RPCArg::Type type,const std::string & defaultValue,const std::string & doc)379 NameOptionsHelp::withArg (const std::string& name, const RPCArg::Type type,
380 const std::string& defaultValue,
381 const std::string& doc)
382 {
383 if (defaultValue.empty ())
384 innerArgs.push_back (RPCArg (name, type, RPCArg::Optional::OMITTED, doc));
385 else
386 innerArgs.push_back (RPCArg (name, type, defaultValue, doc));
387
388 return *this;
389 }
390
391 NameOptionsHelp&
withWriteOptions()392 NameOptionsHelp::withWriteOptions ()
393 {
394 withArg ("destAddress", RPCArg::Type::STR,
395 "The address to send the name output to");
396
397 withArg ("sendCoins", RPCArg::Type::OBJ_USER_KEYS,
398 "Addresses to which coins should be sent additionally");
399
400 return *this;
401 }
402
403 NameOptionsHelp&
withNameEncoding()404 NameOptionsHelp::withNameEncoding ()
405 {
406 withArg ("nameEncoding", RPCArg::Type::STR,
407 "Encoding (\"ascii\", \"utf8\" or \"hex\") of the name argument");
408 return *this;
409 }
410
411 NameOptionsHelp&
withValueEncoding()412 NameOptionsHelp::withValueEncoding ()
413 {
414 withArg ("valueEncoding", RPCArg::Type::STR,
415 "Encoding (\"ascii\", \"utf8\" or \"hex\") of the value argument");
416 return *this;
417 }
418
419 NameOptionsHelp&
withByHash()420 NameOptionsHelp::withByHash ()
421 {
422 withArg ("byHash", RPCArg::Type::STR,
423 "Interpret \"name\" as hash (\"direct\" or \"sha256d\")");
424 return *this;
425 }
426
427 RPCArg
buildRpcArg() const428 NameOptionsHelp::buildRpcArg () const
429 {
430 return RPCArg ("options", RPCArg::Type::OBJ,
431 RPCArg::Optional::OMITTED_NAMED_ARG,
432 "Options for this RPC call",
433 innerArgs, "options");
434 }
435
436 /* ************************************************************************** */
437 namespace
438 {
439
440 RPCHelpMan
name_show()441 name_show ()
442 {
443 const bool allow_expired_default = gArgs.GetBoolArg("-allowexpired", DEFAULT_ALLOWEXPIRED);
444
445 NameOptionsHelp optHelp;
446 optHelp
447 .withNameEncoding ()
448 .withValueEncoding ()
449 .withByHash ()
450 .withArg ("allowExpired", RPCArg::Type::BOOL, allow_expired_default ? "true" : "false",
451 "Whether to throw error for expired names");
452
453 return RPCHelpMan ("name_show",
454 "\nLooks up the current data for the given name. Fails if the name doesn't exist.\n",
455 {
456 {"name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name to query for"},
457 optHelp.buildRpcArg (),
458 },
459 NameInfoHelp ()
460 .withExpiration ()
461 .finish (),
462 RPCExamples {
463 HelpExampleCli ("name_show", "\"myname\"")
464 + HelpExampleCli ("name_show", R"("myname" '{"allowExpired": false}')")
465 + HelpExampleRpc ("name_show", "\"myname\"")
466 },
467 [&] (const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
468 {
469 RPCTypeCheck (request.params, {UniValue::VSTR, UniValue::VOBJ});
470
471 if (::ChainstateActive ().IsInitialBlockDownload ())
472 throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD,
473 "Namecoin is downloading blocks...");
474
475 UniValue options(UniValue::VOBJ);
476 if (request.params.size () >= 2)
477 options = request.params[1].get_obj ();
478
479 /* Parse and interpret the name_show-specific options. */
480 RPCTypeCheckObj(options,
481 {
482 {"allowExpired", UniValueType(UniValue::VBOOL)},
483 },
484 true, false);
485
486 bool allow_expired = allow_expired_default;
487 if (options.exists("allowExpired"))
488 allow_expired = options["allowExpired"].get_bool();
489
490 const valtype name = GetNameForLookup (request.params[0], options);
491
492 CNameData data;
493 {
494 LOCK (cs_main);
495 if (!::ChainstateActive ().CoinsTip ().GetName (name, data))
496 {
497 std::ostringstream msg;
498 msg << "name not found: " << EncodeNameForMessage (name);
499 throw JSONRPCError (RPC_WALLET_ERROR, msg.str ());
500 }
501 }
502
503 MaybeWalletForRequest wallet(request);
504 LOCK2 (wallet.getLock (), cs_main);
505 UniValue name_object = getNameInfo(options, name, data, wallet);
506 assert(!name_object["expired"].isNull());
507 const bool is_expired = name_object["expired"].get_bool();
508 if (is_expired && !allow_expired)
509 {
510 std::ostringstream msg;
511 msg << "name not found: " << EncodeNameForMessage(name);
512 throw JSONRPCError(RPC_WALLET_ERROR, msg.str());
513 }
514 return name_object;
515 }
516 );
517 }
518
519 /* ************************************************************************** */
520
521 RPCHelpMan
name_history()522 name_history ()
523 {
524 NameOptionsHelp optHelp;
525 optHelp
526 .withNameEncoding ()
527 .withValueEncoding ()
528 .withByHash ();
529
530 return RPCHelpMan ("name_history",
531 "\nLooks up the current and all past data for the given name. -namehistory must be enabled.\n",
532 {
533 {"name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name to query for"},
534 optHelp.buildRpcArg (),
535 },
536 RPCResult {RPCResult::Type::ARR, "", "",
537 {
538 NameInfoHelp ()
539 .withExpiration ()
540 .finish ()
541 }
542 },
543 RPCExamples {
544 HelpExampleCli ("name_history", "\"myname\"")
545 + HelpExampleRpc ("name_history", "\"myname\"")
546 },
547 [&] (const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
548 {
549 RPCTypeCheck (request.params, {UniValue::VSTR, UniValue::VOBJ});
550
551 if (!fNameHistory)
552 throw std::runtime_error ("-namehistory is not enabled");
553
554 if (::ChainstateActive ().IsInitialBlockDownload ())
555 throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD,
556 "Namecoin is downloading blocks...");
557
558 UniValue options(UniValue::VOBJ);
559 if (request.params.size () >= 2)
560 options = request.params[1].get_obj ();
561
562 const valtype name = GetNameForLookup (request.params[0], options);
563
564 CNameData data;
565 CNameHistory history;
566
567 {
568 LOCK (cs_main);
569
570 const auto& coinsTip = ::ChainstateActive ().CoinsTip ();
571 if (!coinsTip.GetName (name, data))
572 {
573 std::ostringstream msg;
574 msg << "name not found: " << EncodeNameForMessage (name);
575 throw JSONRPCError (RPC_WALLET_ERROR, msg.str ());
576 }
577
578 if (!coinsTip.GetNameHistory (name, history))
579 assert (history.empty ());
580 }
581
582 MaybeWalletForRequest wallet(request);
583 LOCK2 (wallet.getLock (), cs_main);
584
585 UniValue res(UniValue::VARR);
586 for (const auto& entry : history.getData ())
587 res.push_back (getNameInfo (options, name, entry, wallet));
588 res.push_back (getNameInfo (options, name, data, wallet));
589
590 return res;
591 }
592 );
593 }
594
595 /* ************************************************************************** */
596
597 RPCHelpMan
name_scan()598 name_scan ()
599 {
600 NameOptionsHelp optHelp;
601 optHelp
602 .withNameEncoding ()
603 .withValueEncoding ()
604 .withArg ("minConf", RPCArg::Type::NUM, "1",
605 "Minimum number of confirmations")
606 .withArg ("maxConf", RPCArg::Type::NUM,
607 "Maximum number of confirmations")
608 .withArg ("prefix", RPCArg::Type::STR,
609 "Filter for names with the given prefix")
610 .withArg ("regexp", RPCArg::Type::STR,
611 "Filter for names matching the regexp");
612
613 return RPCHelpMan ("name_scan",
614 "\nLists names in the database.\n",
615 {
616 {"start", RPCArg::Type::STR, "", "Skip initially to this name"},
617 {"count", RPCArg::Type::NUM, "500", "Stop after this many names"},
618 optHelp.buildRpcArg (),
619 },
620 RPCResult {RPCResult::Type::ARR, "", "",
621 {
622 NameInfoHelp ()
623 .withExpiration ()
624 .finish ()
625 }
626 },
627 RPCExamples {
628 HelpExampleCli ("name_scan", "")
629 + HelpExampleCli ("name_scan", "\"d/abc\"")
630 + HelpExampleCli ("name_scan", "\"d/abc\" 10")
631 + HelpExampleRpc ("name_scan", "\"d/abc\"")
632 },
633 [&] (const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
634 {
635 RPCTypeCheck (request.params,
636 {UniValue::VSTR, UniValue::VNUM, UniValue::VOBJ});
637
638 if (::ChainstateActive ().IsInitialBlockDownload ())
639 throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD,
640 "Namecoin is downloading blocks...");
641
642 UniValue options(UniValue::VOBJ);
643 if (request.params.size () >= 3)
644 options = request.params[2].get_obj ();
645
646 valtype start;
647 if (request.params.size () >= 1)
648 start = DecodeNameFromRPCOrThrow (request.params[0], options);
649
650 int count = 500;
651 if (request.params.size () >= 2)
652 count = request.params[1].get_int ();
653
654 /* Parse and interpret the name_scan-specific options. */
655 RPCTypeCheckObj (options,
656 {
657 {"minConf", UniValueType (UniValue::VNUM)},
658 {"maxConf", UniValueType (UniValue::VNUM)},
659 {"prefix", UniValueType (UniValue::VSTR)},
660 {"regexp", UniValueType (UniValue::VSTR)},
661 },
662 true, false);
663
664 int minConf = 1;
665 if (options.exists ("minConf"))
666 minConf = options["minConf"].get_int ();
667 if (minConf < 1)
668 throw JSONRPCError (RPC_INVALID_PARAMETER, "minConf must be >= 1");
669
670 int maxConf = -1;
671 if (options.exists ("maxConf"))
672 {
673 maxConf = options["maxConf"].get_int ();
674 if (maxConf < 0)
675 throw JSONRPCError (RPC_INVALID_PARAMETER,
676 "maxConf must not be negative");
677 }
678
679 valtype prefix;
680 if (options.exists ("prefix"))
681 prefix = DecodeNameFromRPCOrThrow (options["prefix"], options);
682
683 bool haveRegexp = false;
684 boost::xpressive::sregex regexp;
685 if (options.exists ("regexp"))
686 {
687 haveRegexp = true;
688 regexp = boost::xpressive::sregex::compile (options["regexp"].get_str ());
689 }
690
691 /* Iterate over names and produce the result. */
692 UniValue res(UniValue::VARR);
693 if (count <= 0)
694 return res;
695
696 MaybeWalletForRequest wallet(request);
697 LOCK2 (wallet.getLock (), cs_main);
698
699 const int maxHeight = ::ChainActive ().Height () - minConf + 1;
700 int minHeight = -1;
701 if (maxConf >= 0)
702 minHeight = ::ChainActive ().Height () - maxConf + 1;
703
704 valtype name;
705 CNameData data;
706 const auto& coinsTip = ::ChainstateActive ().CoinsTip ();
707 std::unique_ptr<CNameIterator> iter(coinsTip.IterateNames ());
708 for (iter->seek (start); count > 0 && iter->next (name, data); )
709 {
710 const int height = data.getHeight ();
711 if (height > maxHeight)
712 continue;
713 if (minHeight >= 0 && height < minHeight)
714 continue;
715
716 if (name.size () < prefix.size ())
717 continue;
718 if (!std::equal (prefix.begin (), prefix.end (), name.begin ()))
719 continue;
720
721 if (haveRegexp)
722 {
723 try
724 {
725 const std::string nameStr = EncodeName (name, NameEncoding::UTF8);
726 boost::xpressive::smatch matches;
727 if (!boost::xpressive::regex_search (nameStr, matches, regexp))
728 continue;
729 }
730 catch (const InvalidNameString& exc)
731 {
732 continue;
733 }
734 }
735
736 res.push_back (getNameInfo (options, name, data, wallet));
737 --count;
738 }
739
740 return res;
741 }
742 );
743 }
744
745 /* ************************************************************************** */
746
747 RPCHelpMan
name_pending()748 name_pending ()
749 {
750 NameOptionsHelp optHelp;
751 optHelp
752 .withNameEncoding ()
753 .withValueEncoding ();
754
755 return RPCHelpMan ("name_pending",
756 "\nLists unconfirmed name operations in the mempool.\n"
757 "\nIf a name is given, only check for operations on this name.\n",
758 {
759 {"name", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Only look for this name"},
760 optHelp.buildRpcArg (),
761 },
762 RPCResult {RPCResult::Type::ARR, "", "",
763 {
764 NameInfoHelp ()
765 .withField ({RPCResult::Type::STR, "op", "the operation being performed"})
766 .withExpiration ()
767 .finish ()
768 }
769 },
770 RPCExamples {
771 HelpExampleCli ("name_pending", "")
772 + HelpExampleCli ("name_pending", "\"d/domob\"")
773 + HelpExampleRpc ("name_pending", "")
774 },
775 [&] (const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
776 {
777 RPCTypeCheck (request.params, {UniValue::VSTR, UniValue::VOBJ}, true);
778
779 MaybeWalletForRequest wallet(request);
780 auto& mempool = EnsureMemPool (request.context);
781 LOCK2 (wallet.getLock (), mempool.cs);
782
783 UniValue options(UniValue::VOBJ);
784 if (request.params.size () >= 2)
785 options = request.params[1].get_obj ();
786
787 std::vector<uint256> txHashes;
788 mempool.queryHashes (txHashes);
789
790 const bool hasNameFilter = !request.params[0].isNull ();
791 valtype nameFilter;
792 if (hasNameFilter)
793 nameFilter = DecodeNameFromRPCOrThrow (request.params[0], options);
794
795 UniValue arr(UniValue::VARR);
796 for (const auto& txHash : txHashes)
797 {
798 std::shared_ptr<const CTransaction> tx = mempool.get (txHash);
799 if (!tx || !tx->IsNamecoin ())
800 continue;
801
802 for (size_t n = 0; n < tx->vout.size (); ++n)
803 {
804 const auto& txOut = tx->vout[n];
805 const CNameScript op(txOut.scriptPubKey);
806 if (!op.isNameOp () || !op.isAnyUpdate ())
807 continue;
808 if (hasNameFilter && op.getOpName () != nameFilter)
809 continue;
810
811 UniValue obj = getNameInfo (options,
812 op.getOpName (), op.getOpValue (),
813 COutPoint (tx->GetHash (), n),
814 op.getAddress ());
815 addOwnershipInfo (op.getAddress (), wallet, obj);
816 switch (op.getNameOp ())
817 {
818 case OP_NAME_FIRSTUPDATE:
819 obj.pushKV ("op", "name_firstupdate");
820 break;
821 case OP_NAME_UPDATE:
822 obj.pushKV ("op", "name_update");
823 break;
824 default:
825 assert (false);
826 }
827
828 arr.push_back (obj);
829 }
830 }
831
832 return arr;
833 }
834 );
835 }
836
837 /* ************************************************************************** */
838
839 namespace
840 {
841
842 /**
843 * Performs the action of namerawtransaction and namepsbt on a given
844 * CMutableTransaction. This is used to share the code between the two
845 * RPC methods.
846 *
847 * If a name_new is created and a rand value chosen, it will be placed
848 * into the JSON output "result" already.
849 */
850 void
PerformNameRawtx(const int nOut,const UniValue & nameOp,CMutableTransaction & mtx,UniValue & result)851 PerformNameRawtx (const int nOut, const UniValue& nameOp,
852 CMutableTransaction& mtx, UniValue& result)
853 {
854 mtx.SetNamecoin ();
855
856 if (nOut < 0 || nOut >= mtx.vout.size ())
857 throw JSONRPCError (RPC_INVALID_PARAMETER, "vout is out of range");
858 auto& script = mtx.vout[nOut].scriptPubKey;
859
860 RPCTypeCheckObj (nameOp,
861 {
862 {"op", UniValueType (UniValue::VSTR)},
863 }
864 );
865 const std::string op = find_value (nameOp, "op").get_str ();
866
867 /* namerawtransaction does not have an options argument. This would just
868 make the already long list of arguments longer. Instead of using
869 namerawtransaction, namecoin-tx can be used anyway to create name
870 operations with arbitrary hex data. */
871 const UniValue NO_OPTIONS(UniValue::VOBJ);
872
873 if (op == "name_new")
874 {
875 RPCTypeCheckObj (nameOp,
876 {
877 {"name", UniValueType (UniValue::VSTR)},
878 {"rand", UniValueType (UniValue::VSTR)},
879 },
880 true);
881
882 valtype rand;
883 if (nameOp.exists ("rand"))
884 {
885 const std::string randStr = find_value (nameOp, "rand").get_str ();
886 if (!IsHex (randStr))
887 throw JSONRPCError (RPC_DESERIALIZATION_ERROR, "rand must be hex");
888 rand = ParseHex (randStr);
889 }
890 else
891 {
892 rand.resize (20);
893 GetRandBytes (&rand[0], rand.size ());
894 }
895
896 const valtype name
897 = DecodeNameFromRPCOrThrow (find_value (nameOp, "name"), NO_OPTIONS);
898
899 script = CNameScript::buildNameNew (script, name, rand);
900 result.pushKV ("rand", HexStr (rand));
901 }
902 else if (op == "name_firstupdate")
903 {
904 RPCTypeCheckObj (nameOp,
905 {
906 {"name", UniValueType (UniValue::VSTR)},
907 {"value", UniValueType (UniValue::VSTR)},
908 {"rand", UniValueType (UniValue::VSTR)},
909 }
910 );
911
912 const std::string randStr = find_value (nameOp, "rand").get_str ();
913 if (!IsHex (randStr))
914 throw JSONRPCError (RPC_DESERIALIZATION_ERROR, "rand must be hex");
915 const valtype rand = ParseHex (randStr);
916
917 const valtype name
918 = DecodeNameFromRPCOrThrow (find_value (nameOp, "name"), NO_OPTIONS);
919 const valtype value
920 = DecodeValueFromRPCOrThrow (find_value (nameOp, "value"),
921 NO_OPTIONS);
922
923 script = CNameScript::buildNameFirstupdate (script, name, value, rand);
924 }
925 else if (op == "name_update")
926 {
927 RPCTypeCheckObj (nameOp,
928 {
929 {"name", UniValueType (UniValue::VSTR)},
930 {"value", UniValueType (UniValue::VSTR)},
931 }
932 );
933
934 const valtype name
935 = DecodeNameFromRPCOrThrow (find_value (nameOp, "name"), NO_OPTIONS);
936 const valtype value
937 = DecodeValueFromRPCOrThrow (find_value (nameOp, "value"),
938 NO_OPTIONS);
939
940 script = CNameScript::buildNameUpdate (script, name, value);
941 }
942 else
943 throw JSONRPCError (RPC_INVALID_PARAMETER, "Invalid name operation");
944 }
945
946 } // anonymous namespace
947
948 RPCHelpMan
namerawtransaction()949 namerawtransaction ()
950 {
951 return RPCHelpMan ("namerawtransaction",
952 "\nAdds a name operation to an existing raw transaction.\n"
953 "\nUse createrawtransaction first to create the basic transaction, including the required inputs and outputs also for the name.\n",
954 {
955 {"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The transaction hex string"},
956 {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The vout of the desired name output"},
957 {"nameop", RPCArg::Type::OBJ, RPCArg::Optional::NO, "The name operation to create",
958 {
959 {"op", RPCArg::Type::STR, RPCArg::Optional::NO, "The operation to perform, can be \"name_new\", \"name_firstupdate\" and \"name_update\""},
960 {"name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name to operate on"},
961 {"value", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The new value for the name"},
962 {"rand", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The nonce value to use for registrations"},
963 },
964 "nameop"},
965 },
966 RPCResult {RPCResult::Type::OBJ, "", "",
967 {
968 {RPCResult::Type::STR_HEX, "hex", "Hex string of the updated transaction"},
969 {RPCResult::Type::STR_HEX, "rand", /* optional */ true, "If this is a name_new, the nonce used to create it"},
970 },
971 },
972 RPCExamples {
973 HelpExampleCli ("namerawtransaction", R"("raw tx hex" 1 "{\"op\":\"name_new\",\"name\":\"my-name\")")
974 + HelpExampleCli ("namerawtransaction", R"("raw tx hex" 1 "{\"op\":\"name_firstupdate\",\"name\":\"my-name\",\"value\":\"new value\",\"rand\":\"00112233\")")
975 + HelpExampleCli ("namerawtransaction", R"("raw tx hex" 1 "{\"op\":\"name_update\",\"name\":\"my-name\",\"value\":\"new value\")")
976 + HelpExampleRpc ("namerawtransaction", R"("raw tx hex", 1, "{\"op\":\"name_update\",\"name\":\"my-name\",\"value\":\"new value\")")
977 },
978 [&] (const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
979 {
980 RPCTypeCheck (request.params,
981 {UniValue::VSTR, UniValue::VNUM, UniValue::VOBJ});
982
983 CMutableTransaction mtx;
984 if (!DecodeHexTx (mtx, request.params[0].get_str (), true))
985 throw JSONRPCError (RPC_DESERIALIZATION_ERROR, "TX decode failed");
986
987 UniValue result(UniValue::VOBJ);
988
989 PerformNameRawtx (request.params[1].get_int (), request.params[2].get_obj (),
990 mtx, result);
991
992 result.pushKV ("hex", EncodeHexTx (CTransaction (mtx)));
993 return result;
994 }
995 );
996 }
997
998 RPCHelpMan
999 namepsbt ()
1000 {
1001 return RPCHelpMan ("namepsbt",
1002 "\nAdds a name operation to an existing PSBT.\n"
1003 "\nUse createpsbt first to create the basic transaction, including the required inputs and outputs also for the name.\n",
1004 {
1005 {"psbt", RPCArg::Type::STR, RPCArg::Optional::NO, "A base64 string of a PSBT"},
1006 {"vout", RPCArg::Type::NUM, RPCArg::Optional::NO, "The vout of the desired name output"},
1007 {"nameop", RPCArg::Type::OBJ, RPCArg::Optional::NO, "The name operation to create",
1008 {
1009 {"op", RPCArg::Type::STR, RPCArg::Optional::NO, "The operation to perform, can be \"name_new\", \"name_firstupdate\" and \"name_update\""},
1010 {"name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name to operate on"},
1011 {"value", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The new value for the name"},
1012 {"rand", RPCArg::Type::STR, RPCArg::Optional::OMITTED, "The nonce value to use for registrations"},
1013 },
1014 "nameop"},
1015 },
1016 RPCResult {RPCResult::Type::OBJ, "", "",
1017 {
1018 {RPCResult::Type::STR_HEX, "psbt", "The serialised, updated PSBT"},
1019 {RPCResult::Type::STR_HEX, "rand", /* optional */ true, "If this is a name_new, the nonce used to create it"},
1020 },
1021 },
1022 RPCExamples {
1023 HelpExampleCli ("namepsbt", R"("psbt" 1 "{\"op\":\"name_new\",\"name\":\"my-name\")")
1024 + HelpExampleCli ("namepsbt", R"("psbt" 1 "{\"op\":\"name_firstupdate\",\"name\":\"my-name\",\"value\":\"new value\",\"rand\":\"00112233\")")
1025 + HelpExampleCli ("namepsbt", R"("psbt" 1 "{\"op\":\"name_update\",\"name\":\"my-name\",\"value\":\"new value\")")
1026 + HelpExampleRpc ("namepsbt", R"("psbt", 1, "{\"op\":\"name_update\",\"name\":\"my-name\",\"value\":\"new value\")")
1027 },
1028 [&] (const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1029 {
1030 RPCTypeCheck (request.params,
1031 {UniValue::VSTR, UniValue::VNUM, UniValue::VOBJ});
1032
1033 PartiallySignedTransaction psbtx;
1034 std::string error;
1035 if (!DecodeBase64PSBT (psbtx, request.params[0].get_str (), error))
1036 throw JSONRPCError (RPC_DESERIALIZATION_ERROR,
1037 strprintf ("TX decode failed %s", error));
1038
1039 UniValue result(UniValue::VOBJ);
1040
1041 PerformNameRawtx (request.params[1].get_int (), request.params[2].get_obj (),
1042 *psbtx.tx, result);
1043
1044 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
1045 ssTx << psbtx;
1046 const auto* data = reinterpret_cast<const unsigned char*> (ssTx.data ());
1047 result.pushKV ("psbt", EncodeBase64 (MakeUCharSpan (ssTx)));
1048
1049 return result;
1050 }
1051 );
1052 }
1053
1054 /* ************************************************************************** */
1055
1056 RPCHelpMan
1057 name_checkdb ()
1058 {
1059 return RPCHelpMan ("name_checkdb",
1060 "\nValidates the name DB's consistency.\n"
1061 "\nRoughly between blocks 139,000 and 180,000, this call is expected to fail due to the historic 'name stealing' bug.\n",
1062 {},
1063 RPCResult {RPCResult::Type::BOOL, "", "whether the state is valid"},
1064 RPCExamples {
1065 HelpExampleCli ("name_checkdb", "")
1066 + HelpExampleRpc ("name_checkdb", "")
1067 },
1068 [&] (const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
1069 {
1070 ChainstateManager& chainman = EnsureChainman (request.context);
1071 NodeContext& node = EnsureNodeContext (request.context);
1072
1073 LOCK (cs_main);
1074 auto& coinsTip = chainman.ActiveChainstate ().CoinsTip ();
1075 coinsTip.Flush ();
1076 return coinsTip.ValidateNameDB (chainman, node.rpc_interruption_point);
1077 }
1078 );
1079 }
1080
1081 } // namespace
1082 /* ************************************************************************** */
1083
1084 void RegisterNameRPCCommands(CRPCTable &t)
1085 {
1086 static const CRPCCommand commands[] =
1087 { // category name actor (function) argNames
1088 // --------------------- ------------------------ ----------------------- ----------
1089 { "names", "name_show", &name_show, {"name", "options"} },
1090 { "names", "name_history", &name_history, {"name", "options"} },
1091 { "names", "name_scan", &name_scan, {"start", "count", "options"} },
1092 { "names", "name_pending", &name_pending, {"name", "options"} },
1093 { "names", "name_checkdb", &name_checkdb, {} },
1094 { "rawtransactions", "namerawtransaction", &namerawtransaction, {"hexstring", "vout", "nameop"} },
1095 { "rawtransactions", "namepsbt", &namepsbt, {"psbt", "vout", "nameop"} },
1096 };
1097
1098 for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++)
1099 t.appendCommand(commands[vcidx].name, &commands[vcidx]);
1100 }
1101