2121
2222#include < univalue.h>
2323
24+ #include < memory>
25+
2426namespace
2527{
2628
@@ -66,6 +68,98 @@ getNamePrevout (const uint256& txid, CTxOut& txOut, CTxIn& txIn)
6668 return false ;
6769}
6870
71+ /* *
72+ * A simple helper class that handles determination of the address to which
73+ * name outputs should be sent. It handles the CReserveKey reservation
74+ * as well as parsing the explicit options given by the user (if any).
75+ */
76+ class DestinationAddressHelper
77+ {
78+
79+ private:
80+
81+ /* * Reference to the wallet that should be used. */
82+ CWallet& wallet;
83+
84+ /* *
85+ * The reserve key that was used if no override is given. When finalising
86+ * (after the sending succeeded), this key needs to be marked as KeepKey().
87+ */
88+ std::unique_ptr<CReserveKey> reserveKey;
89+
90+ /* * Set if a valid override destination was added. */
91+ std::unique_ptr<CTxDestination> overrideDest;
92+
93+ public:
94+
95+ explicit DestinationAddressHelper (CWallet& w)
96+ : wallet(w)
97+ {}
98+
99+ /* *
100+ * Processes the given options object to see if it contains an override
101+ * destination. If it does, remembers it.
102+ */
103+ void setOptions (const UniValue& opt);
104+
105+ /* *
106+ * Returns the script that should be used as destination.
107+ */
108+ CScript getScript ();
109+
110+ /* *
111+ * Marks the key as used if one has been reserved. This should be called
112+ * when sending succeeded.
113+ */
114+ void finalise ();
115+
116+ };
117+
118+ void DestinationAddressHelper::setOptions (const UniValue& opt)
119+ {
120+ RPCTypeCheckObj (opt,
121+ {
122+ {" destAddress" , UniValueType (UniValue::VSTR)},
123+ },
124+ true , false );
125+ if (!opt.exists (" destAddress" ))
126+ return ;
127+
128+ CTxDestination dest = DecodeDestination (opt[" destAddress" ].get_str ());
129+ if (!IsValidDestination (dest))
130+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " invalid address" );
131+ overrideDest.reset (new CTxDestination (std::move (dest)));
132+ }
133+
134+ CScript DestinationAddressHelper::getScript ()
135+ {
136+ if (overrideDest != nullptr )
137+ return GetScriptForDestination (*overrideDest);
138+
139+ reserveKey.reset (new CReserveKey (&wallet));
140+ CPubKey pubKeyReserve;
141+ const bool ok = reserveKey->GetReservedKey (pubKeyReserve, true );
142+ assert (ok);
143+
144+ return GetScriptForDestination (pubKeyReserve.GetID ());
145+ }
146+
147+ void DestinationAddressHelper::finalise ()
148+ {
149+ if (reserveKey != nullptr )
150+ reserveKey->KeepKey ();
151+ }
152+
153+ /* *
154+ * Returns the help text for the options argument for name operations.
155+ */
156+ std::string getNameOpOptionsHelp ()
157+ {
158+ return " {\n "
159+ " \" destAddress\" (string, optional) The address to send the name output to\n "
160+ " }\n " ;
161+ }
162+
69163} // namespace
70164
71165/* ************************************************************************** */
@@ -267,7 +361,7 @@ name_firstupdate (const JSONRPCRequest& request)
267361
268362 if (request.fHelp || request.params .size () < 4 || request.params .size () > 6 )
269363 throw std::runtime_error (
270- " name_firstupdate \" name\" \" rand\" \" tx\" \" value\" (\" toaddress \" )\n "
364+ " name_firstupdate \" name\" \" rand\" \" tx\" \" value\" (\" options \" )\n "
271365 " \n Finish the registration of a name. Depends on name_new being"
272366 " already issued.\n "
273367 + HelpRequiringPassphrase (pwallet) +
@@ -276,18 +370,18 @@ name_firstupdate (const JSONRPCRequest& request)
276370 " 2. \" rand\" (string, required) the rand value of name_new\n "
277371 " 3. \" tx\" (string, required) the name_new txid\n "
278372 " 4. \" value\" (string, required) value for the name\n "
279- " 5. \" toaddress\" (string, optional) address to send the name to\n "
373+ " 5. \" options\" (object, optional)\n "
374+ + getNameOpOptionsHelp () +
280375 " \n Result:\n "
281376 " \" txid\" (string) the name_firstupdate's txid\n "
282377 " \n Examples:\n "
283378 + HelpExampleCli (" name_firstupdate" , " \" myname\" , \" 555844f2db9c7f4b25da6cb8277596de45021ef2\" \" a77ceb22aa03304b7de64ec43328974aeaca211c37dd29dcce4ae461bb80ca84\" , \" my-value\" " )
284- + HelpExampleCli (" name_firstupdate" , " \" myname\" , \" 555844f2db9c7f4b25da6cb8277596de45021ef2\" \" a77ceb22aa03304b7de64ec43328974aeaca211c37dd29dcce4ae461bb80ca84\" , \" my-value\" , \" NEX4nME5p3iyNK3gFh4FUeUriHXxEFemo9\" " )
285379 + HelpExampleRpc (" name_firstupdate" , " \" myname\" , \" 555844f2db9c7f4b25da6cb8277596de45021ef2\" \" a77ceb22aa03304b7de64ec43328974aeaca211c37dd29dcce4ae461bb80ca84\" , \" my-value\" " )
286380 );
287381
288382 RPCTypeCheck (request.params ,
289383 {UniValue::VSTR, UniValue::VSTR, UniValue::VSTR, UniValue::VSTR,
290- UniValue::VSTR });
384+ UniValue::VOBJ });
291385
292386 const std::string nameStr = request.params [0 ].get_str ();
293387 const valtype name = ValtypeFromString (nameStr);
@@ -343,40 +437,20 @@ name_firstupdate (const JSONRPCRequest& request)
343437
344438 EnsureWalletIsUnlocked (pwallet);
345439
346- CReserveKey keyName (pwallet);
347- CPubKey pubKeyReserve;
348- const bool ok = keyName.GetReservedKey (pubKeyReserve, true );
349- assert (ok);
350- bool usedKey = false ;
351-
352- CScript addrName;
440+ DestinationAddressHelper destHelper (*pwallet);
353441 if (request.params .size () >= 5 )
354- {
355- keyName.ReturnKey ();
356- const CTxDestination dest
357- = DecodeDestination (request.params [4 ].get_str ());
358- if (!IsValidDestination (dest))
359- throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " invalid address" );
360-
361- addrName = GetScriptForDestination (dest);
362- }
363- else
364- {
365- usedKey = true ;
366- addrName = GetScriptForDestination (pubKeyReserve.GetID ());
367- }
442+ destHelper.setOptions (request.params [4 ].get_obj ());
368443
369444 const CScript nameScript
370- = CNameScript::buildNameFirstupdate (addrName, name, value, rand);
445+ = CNameScript::buildNameFirstupdate (destHelper.getScript (), name, value,
446+ rand);
371447
372448 CCoinControl coinControl;
373449 CTransactionRef tx = SendMoneyToScript (pwallet, nameScript, &txIn,
374450 NAME_LOCKED_AMOUNT, false ,
375451 coinControl, {}, {});
376452
377- if (usedKey)
378- keyName.KeepKey ();
379-
453+ destHelper.finalise ();
380454 return tx->GetHash ().GetHex ();
381455}
382456
@@ -394,23 +468,23 @@ name_update (const JSONRPCRequest& request)
394468 if (request.fHelp
395469 || (request.params .size () != 2 && request.params .size () != 3 ))
396470 throw std::runtime_error (
397- " name_update \" name\" \" value\" (\" toaddress \" )\n "
471+ " name_update \" name\" \" value\" (\" options \" )\n "
398472 " \n Update a name and possibly transfer it.\n "
399473 + HelpRequiringPassphrase (pwallet) +
400474 " \n Arguments:\n "
401475 " 1. \" name\" (string, required) the name to update\n "
402- " 4. \" value\" (string, required) value for the name\n "
403- " 5. \" toaddress\" (string, optional) address to send the name to\n "
476+ " 2. \" value\" (string, required) value for the name\n "
477+ " 3. \" options\" (object, optional)\n "
478+ + getNameOpOptionsHelp () +
404479 " \n Result:\n "
405480 " \" txid\" (string) the name_update's txid\n "
406481 " \n Examples:\n "
407482 + HelpExampleCli (" name_update" , " \" myname\" , \" new-value\" " )
408- + HelpExampleCli (" name_update" , " \" myname\" , \" new-value\" , \" NEX4nME5p3iyNK3gFh4FUeUriHXxEFemo9\" " )
409483 + HelpExampleRpc (" name_update" , " \" myname\" , \" new-value\" " )
410484 );
411485
412486 RPCTypeCheck (request.params ,
413- {UniValue::VSTR, UniValue::VSTR, UniValue::VSTR });
487+ {UniValue::VSTR, UniValue::VSTR, UniValue::VOBJ });
414488
415489 const std::string nameStr = request.params [0 ].get_str ();
416490 const valtype name = ValtypeFromString (nameStr);
@@ -447,40 +521,19 @@ name_update (const JSONRPCRequest& request)
447521
448522 EnsureWalletIsUnlocked (pwallet);
449523
450- CReserveKey keyName (pwallet);
451- CPubKey pubKeyReserve;
452- const bool ok = keyName.GetReservedKey (pubKeyReserve, true );
453- assert (ok);
454- bool usedKey = false ;
455-
456- CScript addrName;
457- if (request.params .size () == 3 )
458- {
459- keyName.ReturnKey ();
460- const CTxDestination dest
461- = DecodeDestination (request.params [2 ].get_str ());
462- if (!IsValidDestination (dest))
463- throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY, " invalid address" );
464-
465- addrName = GetScriptForDestination (dest);
466- }
467- else
468- {
469- usedKey = true ;
470- addrName = GetScriptForDestination (pubKeyReserve.GetID ());
471- }
524+ DestinationAddressHelper destHelper (*pwallet);
525+ if (request.params .size () >= 3 )
526+ destHelper.setOptions (request.params [2 ].get_obj ());
472527
473528 const CScript nameScript
474- = CNameScript::buildNameUpdate (addrName , name, value);
529+ = CNameScript::buildNameUpdate (destHelper. getScript () , name, value);
475530
476531 CCoinControl coinControl;
477532 CTransactionRef tx = SendMoneyToScript (pwallet, nameScript, &txIn,
478533 NAME_LOCKED_AMOUNT, false ,
479534 coinControl, {}, {});
480535
481- if (usedKey)
482- keyName.KeepKey ();
483-
536+ destHelper.finalise ();
484537 return tx->GetHash ().GetHex ();
485538}
486539
0 commit comments