44
55#include < base58.h>
66#include < coins.h>
7+ #include < consensus/validation.h>
78#include < init.h>
89#include < key_io.h>
910#include < names/common.h>
1011#include < names/main.h>
12+ #include < net.h>
1113#include < primitives/transaction.h>
1214#include < random.h>
1315#include < rpc/mining.h>
1416#include < rpc/server.h>
1517#include < script/names.h>
1618#include < txmempool.h>
1719#include < util.h>
20+ #include < utilmoneystr.h>
1821#include < validation.h>
1922#include < wallet/coincontrol.h>
2023#include < wallet/wallet.h>
2124
2225#include < univalue.h>
2326
27+ #include < algorithm>
2428#include < memory>
2529
2630/* ************************************************************************** */
@@ -116,9 +120,110 @@ std::string getNameOpOptionsHelp ()
116120{
117121 return " {\n "
118122 " \" destAddress\" (string, optional) The address to send the name output to\n "
123+ " \" sendCoins\" (object, optional) Addresses to which coins should be sent additionally\n "
124+ " {\n "
125+ " \" addr1\" : x,\n "
126+ " \" addr2\" : y,\n "
127+ " ...\n "
128+ " }\n "
119129 " }\n " ;
120130}
121131
132+ /* *
133+ * Sends a name output to the given name script. This is the "final" step that
134+ * is common between name_new, name_firstupdate and name_update. This method
135+ * also implements the "sendCoins" option, if included.
136+ */
137+ CTransactionRef
138+ SendNameOutput (CWallet& wallet, const CScript& nameOutScript,
139+ const CTxIn* nameInput, const UniValue& opt)
140+ {
141+ RPCTypeCheckObj (opt,
142+ {
143+ {" sendCoins" , UniValueType (UniValue::VOBJ)},
144+ },
145+ true , false );
146+
147+ if (wallet.GetBroadcastTransactions () && !g_connman)
148+ throw JSONRPCError (RPC_CLIENT_P2P_DISABLED,
149+ " Error: Peer-to-peer functionality missing"
150+ " or disabled" );
151+
152+ std::vector<CRecipient> vecSend;
153+ vecSend.push_back ({nameOutScript, NAME_LOCKED_AMOUNT, false });
154+
155+ if (opt.exists (" sendCoins" ))
156+ for (const std::string& addr : opt[" sendCoins" ].getKeys ())
157+ {
158+ const CTxDestination dest = DecodeDestination (addr);
159+ if (!IsValidDestination (dest))
160+ throw JSONRPCError (RPC_INVALID_ADDRESS_OR_KEY,
161+ " Invalid address: " + addr);
162+
163+ const CAmount nAmount = AmountFromValue (opt[" sendCoins" ][addr]);
164+ if (nAmount <= 0 )
165+ throw JSONRPCError (RPC_TYPE_ERROR, " Invalid amount for send" );
166+
167+ vecSend.push_back ({GetScriptForDestination (dest), nAmount, false });
168+ }
169+
170+ /* Shuffle the recipient list for privacy. */
171+ std::shuffle (vecSend.begin (), vecSend.end (), FastRandomContext ());
172+
173+ /* Check balance against total amount sent. If we have a name input, we have
174+ to take its value into account. */
175+
176+ const CAmount curBalance = wallet.GetBalance ();
177+
178+ CAmount totalSpend = 0 ;
179+ for (const auto & recv : vecSend)
180+ totalSpend += recv.nAmount ;
181+
182+ CAmount lockedValue = 0 ;
183+ std::string strError;
184+ if (nameInput != nullptr )
185+ {
186+ const CWalletTx* dummyWalletTx;
187+ if (!wallet.FindValueInNameInput (*nameInput, lockedValue,
188+ dummyWalletTx, strError))
189+ throw JSONRPCError (RPC_WALLET_ERROR, strError);
190+ }
191+
192+ if (totalSpend > curBalance + lockedValue)
193+ throw JSONRPCError (RPC_WALLET_INSUFFICIENT_FUNDS, " Insufficient funds" );
194+
195+ /* Create and send the transaction. This code is based on the corresponding
196+ part of SendMoneyToScript and should stay in sync. */
197+
198+ CCoinControl coinControl;
199+ CReserveKey keyChange (&wallet);
200+ CAmount nFeeRequired;
201+ int nChangePosRet = -1 ;
202+
203+ CTransactionRef tx;
204+ if (!wallet.CreateTransaction (vecSend, nameInput, tx, keyChange,
205+ nFeeRequired, nChangePosRet, strError,
206+ coinControl))
207+ {
208+ if (totalSpend + nFeeRequired > curBalance)
209+ strError = strprintf (" Error: This transaction requires a transaction"
210+ " fee of at least %s" ,
211+ FormatMoney (nFeeRequired));
212+ throw JSONRPCError (RPC_WALLET_ERROR, strError);
213+ }
214+
215+ CValidationState state;
216+ if (!wallet.CommitTransaction (tx, {}, {}, {}, keyChange, g_connman.get (),
217+ state))
218+ {
219+ strError = strprintf (" Error: The transaction was rejected!"
220+ " Reason given: %s" , FormatStateMessage (state));
221+ throw JSONRPCError (RPC_WALLET_ERROR, strError);
222+ }
223+
224+ return tx;
225+ }
226+
122227} // anonymous namespace
123228/* ************************************************************************** */
124229
@@ -263,6 +368,10 @@ name_new (const JSONRPCRequest& request)
263368 if (name.size () > MAX_NAME_LENGTH)
264369 throw JSONRPCError (RPC_INVALID_PARAMETER, " the name is too long" );
265370
371+ UniValue options (UniValue::VOBJ);
372+ if (request.params .size () >= 2 )
373+ options = request.params [1 ].get_obj ();
374+
266375 valtype rand (20 );
267376 GetRandBytes (&rand[0 ], rand.size ());
268377
@@ -277,16 +386,13 @@ name_new (const JSONRPCRequest& request)
277386 EnsureWalletIsUnlocked (pwallet);
278387
279388 DestinationAddressHelper destHelper (*pwallet);
280- if (request.params .size () >= 2 )
281- destHelper.setOptions (request.params [1 ].get_obj ());
389+ destHelper.setOptions (options);
282390
283391 const CScript newScript
284392 = CNameScript::buildNameNew (destHelper.getScript (), hash);
285393
286394 CCoinControl coinControl;
287- CTransactionRef tx = SendMoneyToScript (pwallet, newScript, nullptr ,
288- NAME_LOCKED_AMOUNT, false ,
289- coinControl, {}, {});
395+ CTransactionRef tx = SendNameOutput (*pwallet, newScript, nullptr , options);
290396 destHelper.finalise ();
291397
292398 const std::string randStr = HexStr (rand);
@@ -405,6 +511,10 @@ name_firstupdate (const JSONRPCRequest& request)
405511 if (value.size () > MAX_VALUE_LENGTH_UI)
406512 throw JSONRPCError (RPC_INVALID_PARAMETER, " the value is too long" );
407513
514+ UniValue options (UniValue::VOBJ);
515+ if (request.params .size () >= 5 )
516+ options = request.params [4 ].get_obj ();
517+
408518 {
409519 LOCK (mempool.cs );
410520 if (mempool.registersName (name))
@@ -444,17 +554,14 @@ name_firstupdate (const JSONRPCRequest& request)
444554 EnsureWalletIsUnlocked (pwallet);
445555
446556 DestinationAddressHelper destHelper (*pwallet);
447- if (request.params .size () >= 5 )
448- destHelper.setOptions (request.params [4 ].get_obj ());
557+ destHelper.setOptions (options);
449558
450559 const CScript nameScript
451560 = CNameScript::buildNameFirstupdate (destHelper.getScript (), name, value,
452561 rand);
453562
454563 CCoinControl coinControl;
455- CTransactionRef tx = SendMoneyToScript (pwallet, nameScript, &txIn,
456- NAME_LOCKED_AMOUNT, false ,
457- coinControl, {}, {});
564+ CTransactionRef tx = SendNameOutput (*pwallet, nameScript, &txIn, options);
458565 destHelper.finalise ();
459566
460567 return tx->GetHash ().GetHex ();
@@ -501,6 +608,10 @@ name_update (const JSONRPCRequest& request)
501608 if (value.size () > MAX_VALUE_LENGTH_UI)
502609 throw JSONRPCError (RPC_INVALID_PARAMETER, " the value is too long" );
503610
611+ UniValue options (UniValue::VOBJ);
612+ if (request.params .size () >= 3 )
613+ options = request.params [2 ].get_obj ();
614+
504615 /* Reject updates to a name for which the mempool already has
505616 a pending update. This is not a hard rule enforced by network
506617 rules, but it is necessary with the current mempool implementation. */
@@ -527,16 +638,13 @@ name_update (const JSONRPCRequest& request)
527638 EnsureWalletIsUnlocked (pwallet);
528639
529640 DestinationAddressHelper destHelper (*pwallet);
530- if (request.params .size () >= 3 )
531- destHelper.setOptions (request.params [2 ].get_obj ());
641+ destHelper.setOptions (options);
532642
533643 const CScript nameScript
534644 = CNameScript::buildNameUpdate (destHelper.getScript (), name, value);
535645
536646 CCoinControl coinControl;
537- CTransactionRef tx = SendMoneyToScript (pwallet, nameScript, &txIn,
538- NAME_LOCKED_AMOUNT, false ,
539- coinControl, {}, {});
647+ CTransactionRef tx = SendNameOutput (*pwallet, nameScript, &txIn, options);
540648 destHelper.finalise ();
541649
542650 return tx->GetHash ().GetHex ();
0 commit comments