1 // Copyright (c) 2014-2020 Daniel Kraft
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include <base58.h>
6 #include <coins.h>
7 #include <consensus/validation.h>
8 #include <init.h>
9 #include <interfaces/chain.h>
10 #include <key_io.h>
11 #include <names/common.h>
12 #include <names/encoding.h>
13 #include <names/main.h>
14 #include <names/mempool.h>
15 #include <node/context.h>
16 #include <net.h>
17 #include <primitives/transaction.h>
18 #include <random.h>
19 #include <rpc/blockchain.h>
20 #include <rpc/names.h>
21 #include <rpc/server.h>
22 #include <rpc/util.h>
23 #include <script/names.h>
24 #include <txmempool.h>
25 #include <util/fees.h>
26 #include <util/moneystr.h>
27 #include <util/system.h>
28 #include <util/translation.h>
29 #include <validation.h>
30 #include <wallet/coincontrol.h>
31 #include <wallet/rpcwallet.h>
32 #include <wallet/wallet.h>
33
34 #include <univalue.h>
35
36 #include <algorithm>
37 #include <memory>
38
39 /* ************************************************************************** */
40 namespace
41 {
42
43 /**
44 * A simple helper class that handles determination of the address to which
45 * name outputs should be sent. It handles the CReserveKey reservation
46 * as well as parsing the explicit options given by the user (if any).
47 */
48 class DestinationAddressHelper
49 {
50
51 private:
52
53 /** Reference to the wallet that should be used. */
54 CWallet& wallet;
55
56 /**
57 * The reserve key that was used if no override is given. When finalising
58 * (after the sending succeeded), this key needs to be marked as Keep().
59 */
60 std::unique_ptr<ReserveDestination> rdest;
61
62 /** Set if a valid override destination was added. */
63 std::unique_ptr<CTxDestination> overrideDest;
64
65 public:
66
DestinationAddressHelper(CWallet & w)67 explicit DestinationAddressHelper (CWallet& w)
68 : wallet(w)
69 {}
70
71 /**
72 * Processes the given options object to see if it contains an override
73 * destination. If it does, remembers it.
74 */
75 void setOptions (const UniValue& opt);
76
77 /**
78 * Returns the script that should be used as destination.
79 */
80 CScript getScript ();
81
82 /**
83 * Marks the key as used if one has been reserved. This should be called
84 * when sending succeeded.
85 */
86 void finalise ();
87
88 };
89
setOptions(const UniValue & opt)90 void DestinationAddressHelper::setOptions (const UniValue& opt)
91 {
92 RPCTypeCheckObj (opt,
93 {
94 {"destAddress", UniValueType (UniValue::VSTR)},
95 },
96 true, false);
97 if (!opt.exists ("destAddress"))
98 return;
99
100 CTxDestination dest = DecodeDestination (opt["destAddress"].get_str ());
101 if (!IsValidDestination (dest))
102 throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, "invalid address");
103 overrideDest.reset (new CTxDestination (std::move (dest)));
104 }
105
getScript()106 CScript DestinationAddressHelper::getScript ()
107 {
108 if (overrideDest != nullptr)
109 return GetScriptForDestination (*overrideDest);
110
111 rdest.reset (new ReserveDestination (&wallet, wallet.m_default_address_type));
112 CTxDestination dest;
113 if (!rdest->GetReservedDestination (dest, false))
114 throw JSONRPCError (RPC_WALLET_KEYPOOL_RAN_OUT,
115 "Error: Keypool ran out,"
116 " please call keypoolrefill first");
117
118 return GetScriptForDestination (dest);
119 }
120
finalise()121 void DestinationAddressHelper::finalise ()
122 {
123 if (rdest != nullptr)
124 rdest->KeepDestination ();
125 }
126
127 /**
128 * Sends a name output to the given name script. This is the "final" step that
129 * is common between name_new, name_firstupdate and name_update. This method
130 * also implements the "sendCoins" option, if included.
131 */
132 UniValue
SendNameOutput(const JSONRPCRequest & request,CWallet & wallet,const CScript & nameOutScript,const CTxIn * nameInput,const UniValue & opt)133 SendNameOutput (const JSONRPCRequest& request,
134 CWallet& wallet, const CScript& nameOutScript,
135 const CTxIn* nameInput, const UniValue& opt)
136 {
137 RPCTypeCheckObj (opt,
138 {
139 {"sendCoins", UniValueType (UniValue::VOBJ)},
140 },
141 true, false);
142
143 auto& node = EnsureNodeContext (request.context);
144 if (wallet.GetBroadcastTransactions () && !node.connman)
145 throw JSONRPCError (RPC_CLIENT_P2P_DISABLED,
146 "Error: Peer-to-peer functionality missing"
147 " or disabled");
148
149 std::vector<CRecipient> vecSend;
150 vecSend.push_back ({nameOutScript, NAME_LOCKED_AMOUNT, false});
151
152 if (opt.exists ("sendCoins"))
153 for (const std::string& addr : opt["sendCoins"].getKeys ())
154 {
155 const CTxDestination dest = DecodeDestination (addr);
156 if (!IsValidDestination (dest))
157 throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY,
158 "Invalid address: " + addr);
159
160 const CAmount nAmount = AmountFromValue (opt["sendCoins"][addr]);
161 if (nAmount <= 0)
162 throw JSONRPCError (RPC_TYPE_ERROR, "Invalid amount for send");
163
164 vecSend.push_back ({GetScriptForDestination (dest), nAmount, false});
165 }
166
167 CCoinControl coinControl;
168 return SendMoney (&wallet, coinControl, nameInput, vecSend, {}, false);
169 }
170
171 } // anonymous namespace
172 /* ************************************************************************** */
173
174 RPCHelpMan
name_list()175 name_list ()
176 {
177 NameOptionsHelp optHelp;
178 optHelp
179 .withNameEncoding ()
180 .withValueEncoding ();
181
182 return RPCHelpMan ("name_list",
183 "\nShows the status of all names in the wallet.\n",
184 {
185 {"name", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Only include this name"},
186 optHelp.buildRpcArg (),
187 },
188 RPCResult {RPCResult::Type::ARR, "", "",
189 {
190 NameInfoHelp ()
191 .withExpiration ()
192 .finish ()
193 }
194 },
195 RPCExamples {
196 HelpExampleCli ("name_list", "")
197 + HelpExampleCli ("name_list", "\"myname\"")
198 + HelpExampleRpc ("name_list", "")
199 },
200 [&] (const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
201 {
202 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest (request);
203 if (!wallet)
204 return NullUniValue;
205 CWallet* const pwallet = wallet.get ();
206
207 RPCTypeCheck (request.params, {UniValue::VSTR, UniValue::VOBJ}, true);
208
209 UniValue options(UniValue::VOBJ);
210 if (request.params.size () >= 2)
211 options = request.params[1].get_obj ();
212
213 valtype nameFilter;
214 if (request.params.size () >= 1 && !request.params[0].isNull ())
215 nameFilter = DecodeNameFromRPCOrThrow (request.params[0], options);
216
217 std::map<valtype, int> mapHeights;
218 std::map<valtype, UniValue> mapObjects;
219
220 /* Make sure the results are valid at least up to the most recent block
221 the user could have gotten from another RPC command prior to now. */
222 pwallet->BlockUntilSyncedToCurrentChain ();
223
224 {
225 LOCK2 (pwallet->cs_wallet, cs_main);
226
227 const int tipHeight = ::ChainActive ().Height ();
228 for (const auto& item : pwallet->mapWallet)
229 {
230 const CWalletTx& tx = item.second;
231 if (!tx.tx->IsNamecoin ())
232 continue;
233
234 CNameScript nameOp;
235 int nOut = -1;
236 for (unsigned i = 0; i < tx.tx->vout.size (); ++i)
237 {
238 const CNameScript cur(tx.tx->vout[i].scriptPubKey);
239 if (cur.isNameOp ())
240 {
241 if (nOut != -1)
242 LogPrintf ("ERROR: wallet contains tx with multiple"
243 " name outputs");
244 else
245 {
246 nameOp = cur;
247 nOut = i;
248 }
249 }
250 }
251
252 if (nOut == -1 || !nameOp.isAnyUpdate ())
253 continue;
254
255 const valtype& name = nameOp.getOpName ();
256 if (!nameFilter.empty () && nameFilter != name)
257 continue;
258
259 const int depth = tx.GetDepthInMainChain ();
260 if (depth <= 0)
261 continue;
262 const int height = tipHeight - depth + 1;
263
264 const auto mit = mapHeights.find (name);
265 if (mit != mapHeights.end () && mit->second > height)
266 continue;
267
268 UniValue obj
269 = getNameInfo (options, name, nameOp.getOpValue (),
270 COutPoint (tx.GetHash (), nOut),
271 nameOp.getAddress ());
272 addOwnershipInfo (nameOp.getAddress (), pwallet, obj);
273 addExpirationInfo (height, obj);
274
275 mapHeights[name] = height;
276 mapObjects[name] = obj;
277 }
278 }
279
280 UniValue res(UniValue::VARR);
281 for (const auto& item : mapObjects)
282 res.push_back (item.second);
283
284 return res;
285 }
286 );
287 }
288
289 /* ************************************************************************** */
290
291 RPCHelpMan
name_new()292 name_new ()
293 {
294 NameOptionsHelp optHelp;
295 optHelp
296 .withNameEncoding ()
297 .withWriteOptions ()
298 .withArg ("allowExisting", RPCArg::Type::BOOL, "false",
299 "If set, then the name_new is sent even if the name exists already");
300
301 return RPCHelpMan ("name_new",
302 "\nStarts registration of the given name. Must be followed up with name_firstupdate to finish the registration."
303 + HELP_REQUIRING_PASSPHRASE,
304 {
305 {"name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name to register"},
306 optHelp.buildRpcArg (),
307 },
308 RPCResult {RPCResult::Type::ARR_FIXED, "", "",
309 {
310 {RPCResult::Type::STR_HEX, "txid", "the txid, required for name_firstupdate"},
311 {RPCResult::Type::STR_HEX, "rand", "random value, for name_firstupdate"},
312 },
313 },
314 RPCExamples {
315 HelpExampleCli ("name_new", "\"myname\"")
316 + HelpExampleRpc ("name_new", "\"myname\"")
317 },
318 [&] (const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
319 {
320 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest (request);
321 if (!wallet)
322 return NullUniValue;
323 CWallet* const pwallet = wallet.get ();
324
325 RPCTypeCheck (request.params, {UniValue::VSTR, UniValue::VOBJ});
326
327 UniValue options(UniValue::VOBJ);
328 if (request.params.size () >= 2)
329 options = request.params[1].get_obj ();
330 RPCTypeCheckObj (options,
331 {
332 {"allowExisting", UniValueType (UniValue::VBOOL)},
333 },
334 true, false);
335
336 const valtype name = DecodeNameFromRPCOrThrow (request.params[0], options);
337 if (name.size () > MAX_NAME_LENGTH)
338 throw JSONRPCError (RPC_INVALID_PARAMETER, "the name is too long");
339
340 if (!options["allowExisting"].isTrue ())
341 {
342 LOCK (cs_main);
343 CNameData oldData;
344 const auto& coinsTip = ::ChainstateActive ().CoinsTip ();
345 if (coinsTip.GetName (name, oldData) && !oldData.isExpired ())
346 throw JSONRPCError (RPC_TRANSACTION_ERROR, "this name exists already");
347 }
348
349 valtype rand(20);
350 GetRandBytes (&rand[0], rand.size ());
351
352 /* Make sure the results are valid at least up to the most recent block
353 the user could have gotten from another RPC command prior to now. */
354 pwallet->BlockUntilSyncedToCurrentChain ();
355
356 LOCK (pwallet->cs_wallet);
357
358 EnsureWalletIsUnlocked (pwallet);
359
360 DestinationAddressHelper destHelper(*pwallet);
361 destHelper.setOptions (options);
362
363 const CScript newScript
364 = CNameScript::buildNameNew (destHelper.getScript (), name, rand);
365
366 const UniValue txidVal
367 = SendNameOutput (request, *pwallet, newScript, nullptr, options);
368 destHelper.finalise ();
369
370 const std::string randStr = HexStr (rand);
371 const std::string txid = txidVal.get_str ();
372 LogPrintf ("name_new: name=%s, rand=%s, tx=%s\n",
373 EncodeNameForMessage (name), randStr.c_str (), txid.c_str ());
374
375 UniValue res(UniValue::VARR);
376 res.push_back (txid);
377 res.push_back (randStr);
378
379 return res;
380 }
381 );
382 }
383
384 /* ************************************************************************** */
385
386 namespace
387 {
388
389 /**
390 * Helper routine to fetch the name output of a previous transaction. This
391 * is required for name_firstupdate.
392 * @param txid Previous transaction ID.
393 * @param txOut Set to the corresponding output.
394 * @param txIn Set to the CTxIn to include in the new tx.
395 * @return True if the output could be found.
396 */
397 bool
getNamePrevout(const uint256 & txid,CTxOut & txOut,CTxIn & txIn)398 getNamePrevout (const uint256& txid, CTxOut& txOut, CTxIn& txIn)
399 {
400 AssertLockHeld (cs_main);
401
402 // Maximum number of outputs that are checked for the NAME_NEW prevout.
403 constexpr unsigned MAX_NAME_PREVOUT_TRIALS = 1000;
404
405 // Unfortunately, with the change of the txdb to be based on outputs rather
406 // than full transactions, we can no longer just look up the txid and iterate
407 // over all outputs. Since this is only necessary for a corner case, we just
408 // keep trying with indices until we find the output (up to a maximum number
409 // of trials).
410
411 for (unsigned i = 0; i < MAX_NAME_PREVOUT_TRIALS; ++i)
412 {
413 const COutPoint outp(txid, i);
414
415 Coin coin;
416 if (!::ChainstateActive ().CoinsTip ().GetCoin (outp, coin))
417 continue;
418
419 if (!coin.out.IsNull ()
420 && CNameScript::isNameScript (coin.out.scriptPubKey))
421 {
422 txOut = coin.out;
423 txIn = CTxIn (outp);
424 return true;
425 }
426 }
427
428 return false;
429 }
430
431 } // anonymous namespace
432
433 UniValue
name_firstupdate(const JSONRPCRequest & request)434 name_firstupdate (const JSONRPCRequest& request)
435 {
436 /* There is an undocumented sixth argument that can be used to disable
437 the check for already existing names here (it will still be checked
438 by the mempool and tx validation logic, of course). This is used
439 by the regtests to catch a bug that was previously present but
440 has presumably no other use. */
441
442 NameOptionsHelp optHelp;
443 optHelp
444 .withNameEncoding ()
445 .withValueEncoding ()
446 .withWriteOptions ();
447
448 /* We can not use RPCHelpMan::Check here, as we have that "hidden" sixth
449 argument for "allow active" names (in tests). */
450 if (request.fHelp || request.params.size () < 3 || request.params.size () > 6)
451 throw std::runtime_error (
452 RPCHelpMan ("name_firstupdate",
453 "\nFinishes the registration of a name. Depends on name_new being already issued."
454 + HELP_REQUIRING_PASSPHRASE,
455 {
456 {"name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name to register"},
457 {"rand", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The rand value of name_new"},
458 {"tx", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The name_new txid"},
459 {"value", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Value for the name"},
460 optHelp.buildRpcArg (),
461 },
462 RPCResult {RPCResult::Type::STR_HEX, "", "the transaction ID"},
463 RPCExamples {
464 HelpExampleCli ("name_firstupdate", "\"myname\", \"555844f2db9c7f4b25da6cb8277596de45021ef2\" \"a77ceb22aa03304b7de64ec43328974aeaca211c37dd29dcce4ae461bb80ca84\", \"my-value\"")
465 + HelpExampleRpc ("name_firstupdate", "\"myname\", \"555844f2db9c7f4b25da6cb8277596de45021ef2\" \"a77ceb22aa03304b7de64ec43328974aeaca211c37dd29dcce4ae461bb80ca84\", \"my-value\"")
466 }
467 ).ToString ());
468
469 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest (request);
470 if (!wallet)
471 return NullUniValue;
472 CWallet* const pwallet = wallet.get ();
473
474 RPCTypeCheck (request.params,
475 {UniValue::VSTR, UniValue::VSTR, UniValue::VSTR, UniValue::VSTR,
476 UniValue::VOBJ}, true);
477
478 UniValue options(UniValue::VOBJ);
479 if (request.params.size () >= 5)
480 options = request.params[4].get_obj ();
481
482 const valtype name = DecodeNameFromRPCOrThrow (request.params[0], options);
483 if (name.size () > MAX_NAME_LENGTH)
484 throw JSONRPCError (RPC_INVALID_PARAMETER, "the name is too long");
485
486 const valtype rand = ParseHexV (request.params[1], "rand");
487 if (rand.size () > 20)
488 throw JSONRPCError (RPC_INVALID_PARAMETER, "invalid rand value");
489
490 const uint256 prevTxid = ParseHashV (request.params[2], "txid");
491
492 const bool isDefaultVal = (request.params.size () < 4 || request.params[3].isNull ());
493 const valtype value = isDefaultVal ?
494 valtype ():
495 DecodeValueFromRPCOrThrow (request.params[3], options);
496
497 if (value.size () > MAX_VALUE_LENGTH_UI)
498 throw JSONRPCError (RPC_INVALID_PARAMETER, "the value is too long");
499
500 {
501 auto& mempool = EnsureMemPool (request.context);
502 LOCK (mempool.cs);
503 if (mempool.registersName (name))
504 throw JSONRPCError (RPC_TRANSACTION_ERROR,
505 "this name is already being registered");
506 }
507
508 if (request.params.size () < 6 || !request.params[5].get_bool ())
509 {
510 LOCK (cs_main);
511 CNameData oldData;
512 const auto& coinsTip = ::ChainstateActive ().CoinsTip ();
513 if (coinsTip.GetName (name, oldData) && !oldData.isExpired ())
514 throw JSONRPCError (RPC_TRANSACTION_ERROR,
515 "this name is already active");
516 }
517
518 CTxOut prevOut;
519 CTxIn txIn;
520 {
521 LOCK (cs_main);
522 if (!getNamePrevout (prevTxid, prevOut, txIn))
523 throw JSONRPCError (RPC_TRANSACTION_ERROR, "previous txid not found");
524 }
525
526 const CNameScript prevNameOp(prevOut.scriptPubKey);
527 assert (prevNameOp.isNameOp ());
528 if (prevNameOp.getNameOp () != OP_NAME_NEW)
529 throw JSONRPCError (RPC_TRANSACTION_ERROR, "previous tx is not name_new");
530
531 valtype toHash(rand);
532 toHash.insert (toHash.end (), name.begin (), name.end ());
533 if (uint160 (prevNameOp.getOpHash ()) != Hash160 (toHash))
534 throw JSONRPCError (RPC_TRANSACTION_ERROR, "rand value is wrong");
535
536 /* Make sure the results are valid at least up to the most recent block
537 the user could have gotten from another RPC command prior to now. */
538 pwallet->BlockUntilSyncedToCurrentChain ();
539
540 LOCK (pwallet->cs_wallet);
541
542 EnsureWalletIsUnlocked (pwallet);
543
544 DestinationAddressHelper destHelper(*pwallet);
545 destHelper.setOptions (options);
546
547 const CScript nameScript
548 = CNameScript::buildNameFirstupdate (destHelper.getScript (), name, value,
549 rand);
550
551 const UniValue txidVal
552 = SendNameOutput (request, *pwallet, nameScript, &txIn, options);
553 destHelper.finalise ();
554
555 return txidVal;
556 }
557
558 /* ************************************************************************** */
559
560 RPCHelpMan
name_update()561 name_update ()
562 {
563 NameOptionsHelp optHelp;
564 optHelp
565 .withNameEncoding ()
566 .withValueEncoding ()
567 .withWriteOptions ();
568
569 return RPCHelpMan ("name_update",
570 "\nUpdates a name and possibly transfers it."
571 + HELP_REQUIRING_PASSPHRASE,
572 {
573 {"name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name to update"},
574 {"value", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "Value for the name"},
575 optHelp.buildRpcArg (),
576 },
577 RPCResult {RPCResult::Type::STR_HEX, "", "the transaction ID"},
578 RPCExamples {
579 HelpExampleCli ("name_update", "\"myname\", \"new-value\"")
580 + HelpExampleRpc ("name_update", "\"myname\", \"new-value\"")
581 },
582 [&] (const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
583 {
584 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest (request);
585 if (!wallet)
586 return NullUniValue;
587 CWallet* const pwallet = wallet.get ();
588
589 RPCTypeCheck (request.params,
590 {UniValue::VSTR, UniValue::VSTR, UniValue::VOBJ}, true);
591
592 UniValue options(UniValue::VOBJ);
593 if (request.params.size () >= 3)
594 options = request.params[2].get_obj ();
595
596 const valtype name = DecodeNameFromRPCOrThrow (request.params[0], options);
597 if (name.size () > MAX_NAME_LENGTH)
598 throw JSONRPCError (RPC_INVALID_PARAMETER, "the name is too long");
599
600 const bool isDefaultVal = request.params.size() < 2 || request.params[1].isNull();
601
602 valtype value;
603 if (!isDefaultVal) {
604 value = DecodeValueFromRPCOrThrow (request.params[1], options);
605 if (value.size () > MAX_VALUE_LENGTH_UI)
606 throw JSONRPCError (RPC_INVALID_PARAMETER, "the value is too long");
607 }
608
609 /* For finding the name output to spend and its value, we first check if
610 there are pending operations on the name in the mempool. If there
611 are, then we build upon the last one to get a valid chain. If there
612 are none, then we look up the last outpoint from the name database
613 instead. */
614 // TODO: Use name_show for this instead.
615
616 const unsigned chainLimit = gArgs.GetArg ("-limitnamechains",
617 DEFAULT_NAME_CHAIN_LIMIT);
618 COutPoint outp;
619 {
620 auto& mempool = EnsureMemPool (request.context);
621 LOCK (mempool.cs);
622
623 const unsigned pendingOps = mempool.pendingNameChainLength (name);
624 if (pendingOps >= chainLimit)
625 throw JSONRPCError (RPC_TRANSACTION_ERROR,
626 "there are already too many pending operations"
627 " on this name");
628
629 if (pendingOps > 0)
630 {
631 outp = mempool.lastNameOutput (name);
632 if (isDefaultVal)
633 {
634 const auto& tx = mempool.mapTx.find(outp.hash)->GetTx();
635 value = CNameScript(tx.vout[outp.n].scriptPubKey).getOpValue();
636 }
637 }
638 }
639
640 if (outp.IsNull ())
641 {
642 LOCK (cs_main);
643
644 CNameData oldData;
645 const auto& coinsTip = ::ChainstateActive ().CoinsTip ();
646 if (!coinsTip.GetName (name, oldData) || oldData.isExpired ())
647 throw JSONRPCError (RPC_TRANSACTION_ERROR,
648 "this name can not be updated");
649 if (isDefaultVal)
650 value = oldData.getValue();
651 outp = oldData.getUpdateOutpoint ();
652 }
653 assert (!outp.IsNull ());
654 const CTxIn txIn(outp);
655
656 /* Make sure the results are valid at least up to the most recent block
657 the user could have gotten from another RPC command prior to now. */
658 pwallet->BlockUntilSyncedToCurrentChain ();
659
660 LOCK (pwallet->cs_wallet);
661
662 EnsureWalletIsUnlocked (pwallet);
663
664 DestinationAddressHelper destHelper(*pwallet);
665 destHelper.setOptions (options);
666
667 const CScript nameScript
668 = CNameScript::buildNameUpdate (destHelper.getScript (), name, value);
669
670 const UniValue txidVal
671 = SendNameOutput (request, *pwallet, nameScript, &txIn, options);
672 destHelper.finalise ();
673
674 return txidVal;
675 }
676 );
677 }
678
679 /* ************************************************************************** */
680
681 RPCHelpMan
sendtoname()682 sendtoname ()
683 {
684 return RPCHelpMan{"sendtoname",
685 "\nSend an amount to the owner of a name.\n"
686 "\nIt is an error if the name is expired."
687 + HELP_REQUIRING_PASSPHRASE,
688 {
689 {"name", RPCArg::Type::STR, RPCArg::Optional::NO, "The name to send to."},
690 {"amount", RPCArg::Type::AMOUNT, RPCArg::Optional::NO, "The amount in " + CURRENCY_UNIT + " to send. eg 0.1"},
691 {"comment", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment used to store what the transaction is for.\n"
692 " This is not part of the transaction, just kept in your wallet."},
693 {"comment_to", RPCArg::Type::STR, RPCArg::Optional::OMITTED_NAMED_ARG, "A comment to store the name of the person or organization\n"
694 " to which you're sending the transaction. This is not part of the \n"
695 " transaction, just kept in your wallet."},
696 {"subtractfeefromamount", RPCArg::Type::BOOL, /* default */ "false", "The fee will be deducted from the amount being sent.\n"
697 " The recipient will receive less coins than you enter in the amount field."},
698 {"replaceable", RPCArg::Type::BOOL, /* default */ "fallback to wallet's default", "Allow this transaction to be replaced by a transaction with higher fees via BIP 125"},
699 },
700 RPCResult {RPCResult::Type::STR_HEX, "", "the transaction ID"},
701 RPCExamples{
702 HelpExampleCli ("sendtoname", "\"id/foobar\" 0.1")
703 + HelpExampleCli ("sendtoname", "\"id/foobar\" 0.1 \"donation\" \"seans outpost\"")
704 + HelpExampleCli ("sendtoname", "\"id/foobar\" 0.1 \"\" \"\" true")
705 + HelpExampleRpc ("sendtoname", "\"id/foobar\", 0.1, \"donation\", \"seans outpost\"")
706 },
707 [&] (const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
708 {
709 std::shared_ptr<CWallet> const wallet = GetWalletForJSONRPCRequest (request);
710 if (!wallet)
711 return NullUniValue;
712 CWallet* const pwallet = wallet.get ();
713
714 if (::ChainstateActive ().IsInitialBlockDownload ())
715 throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD,
716 "Namecoin is downloading blocks...");
717
718 /* Make sure the results are valid at least up to the most recent block
719 the user could have gotten from another RPC command prior to now. */
720 pwallet->BlockUntilSyncedToCurrentChain();
721
722 LOCK(pwallet->cs_wallet);
723
724 /* sendtoname does not support an options argument (e.g. to override the
725 configured name/value encodings). That would just add to the already
726 long list of rarely used arguments. Also, this function is inofficially
727 deprecated anyway, see
728 https://github.com/namecoin/namecoin-core/issues/12. */
729 const UniValue NO_OPTIONS(UniValue::VOBJ);
730
731 const valtype name = DecodeNameFromRPCOrThrow (request.params[0], NO_OPTIONS);
732
733 CNameData data;
734 if (!::ChainstateActive ().CoinsTip ().GetName (name, data))
735 {
736 std::ostringstream msg;
737 msg << "name not found: " << EncodeNameForMessage (name);
738 throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, msg.str ());
739 }
740 if (data.isExpired ())
741 throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, "the name is expired");
742
743 /* The code below is strongly based on sendtoaddress. Make sure to
744 keep it in sync. */
745
746 // Wallet comments
747 mapValue_t mapValue;
748 if (request.params.size() > 2 && !request.params[2].isNull()
749 && !request.params[2].get_str().empty())
750 mapValue["comment"] = request.params[2].get_str();
751 if (request.params.size() > 3 && !request.params[3].isNull()
752 && !request.params[3].get_str().empty())
753 mapValue["to"] = request.params[3].get_str();
754
755 bool fSubtractFeeFromAmount = false;
756 if (!request.params[4].isNull())
757 fSubtractFeeFromAmount = request.params[4].get_bool();
758
759 CCoinControl coin_control;
760 if (!request.params[5].isNull()) {
761 coin_control.m_signal_bip125_rbf = request.params[5].get_bool();
762 }
763
764 EnsureWalletIsUnlocked(pwallet);
765
766 std::vector<CRecipient> recipients;
767 const CAmount amount = AmountFromValue (request.params[1]);
768 recipients.push_back ({data.getAddress (), amount, fSubtractFeeFromAmount});
769
770 return SendMoney(pwallet, coin_control, nullptr, recipients, mapValue, false);
771 }
772 };
773 }
774