@@ -154,103 +154,207 @@ static void RegisterLoad(const std::string& strInput)
154154 RegisterSetJson (key, valStr);
155155}
156156
157+ static CAmount ExtractAndValidateValue (const std::string& strValue)
158+ {
159+ CAmount value;
160+ if (!ParseMoney (strValue, value))
161+ throw std::runtime_error (" invalid TX output value" );
162+ return value;
163+ }
164+
157165static void MutateTxVersion (CMutableTransaction& tx, const std::string& cmdVal)
158166{
159- int64_t newVersion = atoi64 (cmdVal) ;
160- if (newVersion < 1 || newVersion >= CTransaction::TxVersion::TOOHIGH)
161- throw std::runtime_error (" Invalid TX version requested" );
167+ int64_t newVersion;
168+ if (! ParseInt64 (cmdVal, &newVersion) || newVersion < 1 || newVersion >= CTransaction::TxVersion::TOOHIGH)
169+ throw std::runtime_error (" Invalid TX version requested: ' " + cmdVal + " ' " );
162170
163171 tx.nVersion = (int )newVersion;
164172}
165173
166174static void MutateTxLocktime (CMutableTransaction& tx, const std::string& cmdVal)
167175{
168- int64_t newLocktime = atoi64 (cmdVal) ;
169- if (newLocktime < 0LL || newLocktime > 0xffffffffLL )
170- throw std::runtime_error (" Invalid TX locktime requested" );
176+ int64_t newLocktime;
177+ if (! ParseInt64 (cmdVal, &newLocktime) || newLocktime < 0LL || newLocktime > 0xffffffffLL )
178+ throw std::runtime_error (" Invalid TX locktime requested: ' " + cmdVal + " ' " );
171179
172180 tx.nLockTime = (unsigned int )newLocktime;
173181}
174182
175183static void MutateTxAddInput (CMutableTransaction& tx, const std::string& strInput)
176184{
185+ std::vector<std::string> vStrInputParts;
186+ boost::split (vStrInputParts, strInput, boost::is_any_of (" :" ));
187+
177188 // separate TXID:VOUT in string
178- size_t pos = strInput.find (' :' );
179- if ((pos == std::string::npos) ||
180- (pos == 0 ) ||
181- (pos == (strInput.size () - 1 )))
189+ if (vStrInputParts.size ()<2 )
182190 throw std::runtime_error (" TX input missing separator" );
183191
184192 // extract and validate TXID
185- std::string strTxid = strInput. substr ( 0 , pos) ;
186- if ((strTxid. size () != 64 ) || ! IsHex (strTxid))
193+ uint256 txid ;
194+ if (! ParseHashStr (vStrInputParts[ 0 ], txid)) {
187195 throw std::runtime_error (" invalid TX input txid" );
188- uint256 txid (strTxid);
196+ }
189197
190198 static const unsigned int minTxOutSz = 9 ;
191199 unsigned int nMaxSize = MAX_BLOCK_SIZE_LEGACY;
192200 static const unsigned int maxVout = nMaxSize / minTxOutSz;
193201
194202 // extract and validate vout
195- std::string strVout = strInput.substr (pos + 1 , std::string::npos);
196- int vout = atoi (strVout);
197- if ((vout < 0 ) || (vout > (int )maxVout))
198- throw std::runtime_error (" invalid TX input vout" );
203+ const std::string& strVout = vStrInputParts[1 ];
204+ int64_t vout;
205+ if (!ParseInt64 (strVout, &vout) || vout < 0 || vout > static_cast <int64_t >(maxVout))
206+ throw std::runtime_error (" invalid TX input vout '" + strVout + " '" );
207+
208+ // extract the optional sequence number
209+ uint32_t nSequenceIn = CTxIn::SEQUENCE_FINAL;
210+ if (vStrInputParts.size () > 2 )
211+ nSequenceIn = std::stoul (vStrInputParts[2 ]);
199212
200213 // append to transaction input list
201- CTxIn txin (txid, vout);
202- tx.vin .push_back (txin);
214+ tx.vin .emplace_back (txid, vout, CScript (), nSequenceIn);
203215}
204216
205217static void MutateTxAddOutAddr (CMutableTransaction& tx, const std::string& strInput)
206218{
207- // separate VALUE:ADDRESS in string
208- size_t pos = strInput.find (' :' );
209- if ((pos == std::string::npos) ||
210- (pos == 0 ) ||
211- (pos == (strInput.size () - 1 )))
212- throw std::runtime_error (" TX output missing separator" );
219+ // Separate into VALUE:ADDRESS
220+ std::vector<std::string> vStrInputParts;
221+ boost::split (vStrInputParts, strInput, boost::is_any_of (" :" ));
213222
214- // extract and validate VALUE
215- std::string strValue = strInput. substr ( 0 , pos );
216- CAmount value;
217- if (! ParseMoney (strValue, value))
218- throw std::runtime_error ( " invalid TX output value " );
223+ if (vStrInputParts. size () != 2 )
224+ throw std::runtime_error ( " TX output missing or too many separators " );
225+
226+ // Extract and validate VALUE
227+ CAmount value = ExtractAndValidateValue (vStrInputParts[ 0 ] );
219228
220229 // extract and validate ADDRESS
221- std::string strAddr = strInput. substr (pos + 1 , std::string::npos) ;
230+ std::string strAddr = vStrInputParts[ 1 ] ;
222231 CTxDestination destination = DecodeDestination (strAddr);
223232 if (!IsValidDestination (destination)) {
224233 throw std::runtime_error (" invalid TX output address" );
225234 }
226235 CScript scriptPubKey = GetScriptForDestination (destination);
227236
228237 // construct TxOut, append to transaction output list
229- CTxOut txout (value, scriptPubKey);
230- tx.vout .push_back (txout);
238+ tx.vout .emplace_back (value, scriptPubKey);
239+ }
240+
241+ static void MutateTxAddOutPubKey (CMutableTransaction& tx, const std::string& strInput)
242+ {
243+ // Separate into VALUE:PUBKEY[:FLAGS]
244+ std::vector<std::string> vStrInputParts;
245+ boost::split (vStrInputParts, strInput, boost::is_any_of (" :" ));
246+
247+ if (vStrInputParts.size () < 2 || vStrInputParts.size () > 3 )
248+ throw std::runtime_error (" TX output missing or too many separators" );
249+
250+ // Extract and validate VALUE
251+ CAmount value = ExtractAndValidateValue (vStrInputParts[0 ]);
252+
253+ // Extract and validate PUBKEY
254+ CPubKey pubkey (ParseHex (vStrInputParts[1 ]));
255+ if (!pubkey.IsFullyValid ())
256+ throw std::runtime_error (" invalid TX output pubkey" );
257+ CScript scriptPubKey = GetScriptForRawPubKey (pubkey);
258+
259+ // Extract and validate FLAGS
260+ bool bScriptHash = false ;
261+ if (vStrInputParts.size () == 3 ) {
262+ std::string flags = vStrInputParts[2 ];
263+ bScriptHash = (flags.find (' S' ) != std::string::npos);
264+ }
265+ if (bScriptHash) {
266+ // Get the ID for the script, and then construct a P2SH destination for it.
267+ scriptPubKey = GetScriptForDestination (CScriptID (scriptPubKey));
268+ }
269+
270+ // construct TxOut, append to transaction output list
271+ tx.vout .emplace_back (value, scriptPubKey);
272+ }
273+
274+ static void MutateTxAddOutMultiSig (CMutableTransaction& tx, const std::string& strInput)
275+ {
276+ // Separate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]
277+ std::vector<std::string> vStrInputParts;
278+ boost::split (vStrInputParts, strInput, boost::is_any_of (" :" ));
279+
280+ // Check that there are enough parameters
281+ if (vStrInputParts.size () < 3 )
282+ throw std::runtime_error (" Not enough multisig parameters" );
283+
284+ // Extract and validate VALUE
285+ CAmount value = ExtractAndValidateValue (vStrInputParts[0 ]);
286+
287+ // Extract REQUIRED
288+ uint32_t required = stoul (vStrInputParts[1 ]);
289+
290+ // Extract NUMKEYS
291+ uint32_t numkeys = stoul (vStrInputParts[2 ]);
292+
293+ // Validate there are the correct number of pubkeys
294+ if (vStrInputParts.size () < numkeys + 3 )
295+ throw std::runtime_error (" incorrect number of multisig pubkeys" );
296+
297+ if (required < 1 || required > MAX_PUBKEYS_PER_MULTISIG || numkeys < 1 || numkeys > MAX_PUBKEYS_PER_MULTISIG || numkeys < required)
298+ throw std::runtime_error (" multisig parameter mismatch. Required " \
299+ + std::to_string (required) + " of " + std::to_string (numkeys) + " signatures." );
300+
301+ // extract and validate PUBKEYs
302+ std::vector<CPubKey> pubkeys;
303+ for (int pos = 1 ; pos <= int (numkeys); pos++) {
304+ CPubKey pubkey (ParseHex (vStrInputParts[pos + 2 ]));
305+ if (!pubkey.IsFullyValid ())
306+ throw std::runtime_error (" invalid TX output pubkey" );
307+ pubkeys.push_back (pubkey);
308+ }
309+
310+ // Extract FLAGS
311+ bool bScriptHash = false ;
312+ if (vStrInputParts.size () == numkeys + 4 ) {
313+ std::string flags = vStrInputParts.back ();
314+ bScriptHash = (flags.find (' S' ) != std::string::npos);
315+ }
316+ else if (vStrInputParts.size () > numkeys + 4 ) {
317+ // Validate that there were no more parameters passed
318+ throw std::runtime_error (" Too many parameters" );
319+ }
320+
321+ CScript scriptPubKey = GetScriptForMultisig (required, pubkeys);
322+
323+ if (bScriptHash) {
324+ if (scriptPubKey.size () > MAX_SCRIPT_ELEMENT_SIZE) {
325+ throw std::runtime_error (strprintf (
326+ " redeemScript exceeds size limit: %d > %d" , scriptPubKey.size (), MAX_SCRIPT_ELEMENT_SIZE));
327+ }
328+ // Get the ID for the script, and then construct a P2SH destination for it.
329+ scriptPubKey = GetScriptForDestination (CScriptID (scriptPubKey));
330+ }
331+
332+ // construct TxOut, append to transaction output list
333+ tx.vout .emplace_back (value, scriptPubKey);
231334}
232335
233336static void MutateTxAddOutScript (CMutableTransaction& tx, const std::string& strInput)
234337{
235- // separate VALUE:SCRIPT in string
236- size_t pos = strInput. find ( ' : ' ) ;
237- if ((pos == std::string::npos) ||
238- (pos == 0 ) )
338+ // separate VALUE:SCRIPT
339+ std::vector<std::string> vStrInputParts ;
340+ boost::split (vStrInputParts, strInput, boost::is_any_of ( " : " ));
341+ if (vStrInputParts. size () < 2 )
239342 throw std::runtime_error (" TX output missing separator" );
240343
241- // extract and validate VALUE
242- std::string strValue = strInput.substr (0 , pos);
243- CAmount value;
244- if (!ParseMoney (strValue, value))
245- throw std::runtime_error (" invalid TX output value" );
344+ // Extract and validate VALUE
345+ CAmount value = ExtractAndValidateValue (vStrInputParts[0 ]);
246346
247347 // extract and validate script
248- std::string strScript = strInput.substr (pos + 1 , std::string::npos);
249- CScript scriptPubKey = ParseScript (strScript); // throws on err
348+ std::string strScript = vStrInputParts[1 ];
349+ CScript scriptPubKey = ParseScript (strScript);
350+
351+ if (scriptPubKey.size () > MAX_SCRIPT_SIZE) {
352+ throw std::runtime_error (strprintf (
353+ " script exceeds size limit: %d > %d" , scriptPubKey.size (), MAX_SCRIPT_SIZE));
354+ }
250355
251356 // construct TxOut, append to transaction output list
252- CTxOut txout (value, scriptPubKey);
253- tx.vout .push_back (txout);
357+ tx.vout .emplace_back (value, scriptPubKey);
254358}
255359
256360static void MutateTxDelInput (CMutableTransaction& tx, const std::string& strInIdx)
@@ -495,36 +599,34 @@ class Secp256k1Init
495599static void MutateTx (CMutableTransaction& tx, const std::string& command, const std::string& commandVal)
496600{
497601 std::unique_ptr<Secp256k1Init> ecc;
498- if (command == " nversion" )
602+ if (command == " nversion" ) {
499603 MutateTxVersion (tx, commandVal);
500- else if (command == " locktime" )
604+ } else if (command == " locktime" ) {
501605 MutateTxLocktime (tx, commandVal);
502-
503- else if (command == " delin" )
606+ } else if (command == " delin" ) {
504607 MutateTxDelInput (tx, commandVal);
505- else if (command == " in" )
608+ } else if (command == " in" ) {
506609 MutateTxAddInput (tx, commandVal);
507-
508- else if (command == " delout" )
610+ } else if (command == " delout" ) {
509611 MutateTxDelOutput (tx, commandVal);
510- else if (command == " outaddr" )
612+ } else if (command == " outaddr" ) {
511613 MutateTxAddOutAddr (tx, commandVal);
512- else if (command == " outscript" )
614+ } else if (command == " outpubkey" ) {
615+ MutateTxAddOutPubKey (tx, commandVal);
616+ } else if (command == " outmultisig" ) {
617+ MutateTxAddOutMultiSig (tx, commandVal);
618+ } else if (command == " outscript" ) {
513619 MutateTxAddOutScript (tx, commandVal);
514-
515- else if (command == " sign" ){
620+ } else if (command == " sign" ) {
516621 if (!ecc) { ecc.reset (new Secp256k1Init ()); }
517622 MutateTxSign (tx, commandVal);
518- }
519-
520- else if (command == " load" )
623+ } else if (command == " load" ) {
521624 RegisterLoad (commandVal);
522-
523- else if (command == " set" )
625+ } else if (command == " set" ) {
524626 RegisterSet (commandVal);
525-
526- else
627+ } else {
527628 throw std::runtime_error (" unknown command" );
629+ }
528630}
529631
530632static void OutputTxJSON (const CTransaction& tx)
0 commit comments