Line data Source code
1 : // Copyright (c) 2009-2014 The Bitcoin developers
2 : // Copyright (c) 2015-2021 The PIVX Core developers
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 : #if defined(HAVE_CONFIG_H)
7 : #include "config/pivx-config.h"
8 : #endif
9 :
10 : #include "clientversion.h"
11 : #include "coins.h"
12 : #include "core_io.h"
13 : #include "keystore.h"
14 : #include "key_io.h"
15 : #include "policy/policy.h"
16 : #include "primitives/transaction.h"
17 : #include "script/script.h"
18 : #include "script/sign.h"
19 : #include <univalue.h>
20 : #include "util/system.h"
21 : #include "utilmoneystr.h"
22 : #include "utilstrencodings.h"
23 :
24 : #include <stdio.h>
25 :
26 : #include <boost/algorithm/string.hpp>
27 :
28 : static bool fCreateBlank;
29 : static std::map<std::string, UniValue> registers;
30 : static const int CONTINUE_EXECUTION=-1;
31 :
32 : //
33 : // This function returns either one of EXIT_ codes when it's expected to stop the process or
34 : // CONTINUE_EXECUTION when it's expected to continue further.
35 : //
36 22 : static int AppInitRawTx(int argc, char* argv[])
37 : {
38 : //
39 : // Parameters
40 : //
41 22 : gArgs.ParseParameters(argc, argv);
42 :
43 : // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
44 22 : try {
45 22 : SelectParams(gArgs.GetChainName());
46 0 : } catch(const std::exception& e) {
47 0 : fprintf(stderr, "Error: %s\n", e.what());
48 0 : return EXIT_FAILURE;
49 : }
50 :
51 22 : fCreateBlank = gArgs.GetBoolArg("-create", false);
52 :
53 88 : if (argc < 2 || gArgs.IsArgSet("-?") || gArgs.IsArgSet("-h") || gArgs.IsArgSet("-help")) {
54 : // First part of help message is specific to this utility
55 0 : std::string strUsage = PACKAGE_NAME " pivx-tx utility version " + FormatFullVersion() + "\n\n" +
56 0 : "Usage: pivx-tx [options] <hex-tx> [commands] Update hex-encoded pivx transaction\n" +
57 : "or: pivx-tx [options] -create [commands] Create hex-encoded pivx transaction\n" +
58 0 : "\n";
59 :
60 0 : fprintf(stdout, "%s", strUsage.c_str());
61 :
62 0 : strUsage = HelpMessageGroup("Options:");
63 0 : strUsage += HelpMessageOpt("-?", "This help message");
64 0 : strUsage += HelpMessageOpt("-create", "Create new, empty TX.");
65 0 : strUsage += HelpMessageOpt("-json", "Select JSON output");
66 0 : strUsage += HelpMessageOpt("-txid", "Output only the hex-encoded transaction id of the resultant transaction.");
67 0 : strUsage += HelpMessageOpt("-regtest", "Enter regression test mode, which uses a special chain in which blocks can be solved instantly.");
68 0 : strUsage += HelpMessageOpt("-testnet", "Use the test network");
69 :
70 0 : fprintf(stdout, "%s", strUsage.c_str());
71 :
72 :
73 0 : strUsage = HelpMessageGroup("Commands:");
74 0 : strUsage += HelpMessageOpt("delin=N", "Delete input N from TX");
75 0 : strUsage += HelpMessageOpt("delout=N", "Delete output N from TX");
76 0 : strUsage += HelpMessageOpt("in=TXID:VOUT", "Add input to TX");
77 0 : strUsage += HelpMessageOpt("locktime=N", "Set TX lock time to N");
78 0 : strUsage += HelpMessageOpt("nversion=N", "Set TX version to N");
79 0 : strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", "Add address-based output to TX");
80 0 : strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT", "Add raw script output to TX");
81 0 : strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", "Add zero or more signatures to transaction. "
82 : "This command requires JSON registers:"
83 : "prevtxs=JSON object, "
84 : "privatekeys=JSON object. "
85 0 : "See signrawtransaction docs for format of sighash flags, JSON objects.");
86 0 : fprintf(stdout, "%s", strUsage.c_str());
87 :
88 0 : strUsage = HelpMessageGroup("Register Commands:");
89 0 : strUsage += HelpMessageOpt("load=NAME:FILENAME", "Load JSON file FILENAME into register NAME");
90 0 : strUsage += HelpMessageOpt("set=NAME:JSON-STRING", "Set register NAME to given JSON-STRING");
91 0 : fprintf(stdout, "%s", strUsage.c_str());
92 :
93 0 : if (argc < 2) {
94 0 : fprintf(stderr, "Error: too few parameters\n");
95 : return EXIT_FAILURE;
96 : }
97 : return EXIT_SUCCESS;
98 : }
99 : return CONTINUE_EXECUTION;
100 : }
101 :
102 2 : static void RegisterSetJson(const std::string& key, const std::string& rawJson)
103 : {
104 4 : UniValue val;
105 2 : if (!val.read(rawJson)) {
106 0 : std::string strErr = "Cannot parse JSON for key " + key;
107 0 : throw std::runtime_error(strErr);
108 : }
109 :
110 2 : registers[key] = val;
111 2 : }
112 :
113 2 : static void RegisterSet(const std::string& strInput)
114 : {
115 : // separate NAME:VALUE in string
116 2 : size_t pos = strInput.find(':');
117 2 : if ((pos == std::string::npos) ||
118 2 : (pos == 0) ||
119 2 : (pos == (strInput.size() - 1)))
120 0 : throw std::runtime_error("Register input requires NAME:VALUE");
121 :
122 2 : std::string key = strInput.substr(0, pos);
123 4 : std::string valStr = strInput.substr(pos + 1, std::string::npos);
124 :
125 2 : RegisterSetJson(key, valStr);
126 2 : }
127 :
128 0 : static void RegisterLoad(const std::string& strInput)
129 : {
130 : // separate NAME:FILENAME in string
131 0 : size_t pos = strInput.find(':');
132 0 : if ((pos == std::string::npos) ||
133 0 : (pos == 0) ||
134 0 : (pos == (strInput.size() - 1)))
135 0 : throw std::runtime_error("Register load requires NAME:FILENAME");
136 :
137 0 : std::string key = strInput.substr(0, pos);
138 0 : std::string filename = strInput.substr(pos + 1, std::string::npos);
139 :
140 0 : FILE* f = fopen(filename.c_str(), "r");
141 0 : if (!f) {
142 0 : std::string strErr = "Cannot open file " + filename;
143 0 : throw std::runtime_error(strErr);
144 : }
145 :
146 : // load file chunks into one big buffer
147 0 : std::string valStr;
148 0 : while ((!feof(f)) && (!ferror(f))) {
149 0 : char buf[4096];
150 0 : int bread = fread(buf, 1, sizeof(buf), f);
151 0 : if (bread <= 0)
152 : break;
153 :
154 0 : valStr.insert(valStr.size(), buf, bread);
155 : }
156 :
157 0 : if (ferror(f)) {
158 0 : std::string strErr = "Error reading file " + filename;
159 0 : throw std::runtime_error(strErr);
160 : }
161 :
162 0 : fclose(f);
163 :
164 : // evaluate as JSON buffer register
165 0 : RegisterSetJson(key, valStr);
166 0 : }
167 :
168 9 : static CAmount ExtractAndValidateValue(const std::string& strValue)
169 : {
170 9 : CAmount value;
171 9 : if (!ParseMoney(strValue, value))
172 0 : throw std::runtime_error("invalid TX output value");
173 9 : return value;
174 : }
175 :
176 3 : static void MutateTxVersion(CMutableTransaction& tx, const std::string& cmdVal)
177 : {
178 3 : int64_t newVersion;
179 3 : if (!ParseInt64(cmdVal, &newVersion) || newVersion < 1 || newVersion >= CTransaction::TxVersion::TOOHIGH)
180 0 : throw std::runtime_error("Invalid TX version requested: '" + cmdVal + "'");
181 :
182 3 : tx.nVersion = (int)newVersion;
183 3 : }
184 :
185 2 : static void MutateTxLocktime(CMutableTransaction& tx, const std::string& cmdVal)
186 : {
187 2 : int64_t newLocktime;
188 2 : if (!ParseInt64(cmdVal, &newLocktime) || newLocktime < 0LL || newLocktime > 0xffffffffLL)
189 0 : throw std::runtime_error("Invalid TX locktime requested: '" + cmdVal + "'");
190 :
191 2 : tx.nLockTime = (unsigned int)newLocktime;
192 2 : }
193 :
194 7 : static void MutateTxAddInput(CMutableTransaction& tx, const std::string& strInput)
195 : {
196 7 : std::vector<std::string> vStrInputParts;
197 7 : boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
198 :
199 : // separate TXID:VOUT in string
200 7 : if (vStrInputParts.size()<2)
201 0 : throw std::runtime_error("TX input missing separator");
202 :
203 : // extract and validate TXID
204 7 : uint256 txid;
205 7 : if (!ParseHashStr(vStrInputParts[0], txid)) {
206 0 : throw std::runtime_error("invalid TX input txid");
207 : }
208 :
209 7 : static const unsigned int minTxOutSz = 9;
210 7 : unsigned int nMaxSize = MAX_BLOCK_SIZE_LEGACY;
211 7 : static const unsigned int maxVout = nMaxSize / minTxOutSz;
212 :
213 : // extract and validate vout
214 7 : const std::string& strVout = vStrInputParts[1];
215 7 : int64_t vout;
216 7 : if (!ParseInt64(strVout, &vout) || vout < 0 || vout > static_cast<int64_t>(maxVout))
217 0 : throw std::runtime_error("invalid TX input vout '" + strVout + "'");
218 :
219 : // extract the optional sequence number
220 7 : uint32_t nSequenceIn = CTxIn::SEQUENCE_FINAL;
221 7 : if (vStrInputParts.size() > 2)
222 0 : nSequenceIn = std::stoul(vStrInputParts[2]);
223 :
224 : // append to transaction input list
225 14 : tx.vin.emplace_back(txid, vout, CScript(), nSequenceIn);
226 7 : }
227 :
228 7 : static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strInput)
229 : {
230 : // Separate into VALUE:ADDRESS
231 7 : std::vector<std::string> vStrInputParts;
232 9 : boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
233 :
234 7 : if (vStrInputParts.size() != 2)
235 2 : throw std::runtime_error("TX output missing or too many separators");
236 :
237 : // Extract and validate VALUE
238 5 : CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
239 :
240 : // extract and validate ADDRESS
241 10 : std::string strAddr = vStrInputParts[1];
242 10 : CTxDestination destination = DecodeDestination(strAddr);
243 5 : if (!IsValidDestination(destination)) {
244 0 : throw std::runtime_error("invalid TX output address");
245 : }
246 10 : CScript scriptPubKey = GetScriptForDestination(destination);
247 :
248 : // construct TxOut, append to transaction output list
249 5 : tx.vout.emplace_back(value, scriptPubKey);
250 5 : }
251 :
252 0 : static void MutateTxAddOutPubKey(CMutableTransaction& tx, const std::string& strInput)
253 : {
254 : // Separate into VALUE:PUBKEY[:FLAGS]
255 0 : std::vector<std::string> vStrInputParts;
256 0 : boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
257 :
258 0 : if (vStrInputParts.size() < 2 || vStrInputParts.size() > 3)
259 0 : throw std::runtime_error("TX output missing or too many separators");
260 :
261 : // Extract and validate VALUE
262 0 : CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
263 :
264 : // Extract and validate PUBKEY
265 0 : CPubKey pubkey(ParseHex(vStrInputParts[1]));
266 0 : if (!pubkey.IsFullyValid())
267 0 : throw std::runtime_error("invalid TX output pubkey");
268 0 : CScript scriptPubKey = GetScriptForRawPubKey(pubkey);
269 :
270 : // Extract and validate FLAGS
271 0 : bool bScriptHash = false;
272 0 : if (vStrInputParts.size() == 3) {
273 0 : std::string flags = vStrInputParts[2];
274 0 : bScriptHash = (flags.find('S') != std::string::npos);
275 : }
276 0 : if (bScriptHash) {
277 : // Get the ID for the script, and then construct a P2SH destination for it.
278 0 : scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
279 : }
280 :
281 : // construct TxOut, append to transaction output list
282 0 : tx.vout.emplace_back(value, scriptPubKey);
283 0 : }
284 :
285 0 : static void MutateTxAddOutMultiSig(CMutableTransaction& tx, const std::string& strInput)
286 : {
287 : // Separate into VALUE:REQUIRED:NUMKEYS:PUBKEY1:PUBKEY2:....[:FLAGS]
288 0 : std::vector<std::string> vStrInputParts;
289 0 : boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
290 :
291 : // Check that there are enough parameters
292 0 : if (vStrInputParts.size() < 3)
293 0 : throw std::runtime_error("Not enough multisig parameters");
294 :
295 : // Extract and validate VALUE
296 0 : CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
297 :
298 : // Extract REQUIRED
299 0 : uint32_t required = stoul(vStrInputParts[1]);
300 :
301 : // Extract NUMKEYS
302 0 : uint32_t numkeys = stoul(vStrInputParts[2]);
303 :
304 : // Validate there are the correct number of pubkeys
305 0 : if (vStrInputParts.size() < numkeys + 3)
306 0 : throw std::runtime_error("incorrect number of multisig pubkeys");
307 :
308 0 : if (required < 1 || required > MAX_PUBKEYS_PER_MULTISIG || numkeys < 1 || numkeys > MAX_PUBKEYS_PER_MULTISIG || numkeys < required)
309 0 : throw std::runtime_error("multisig parameter mismatch. Required " \
310 0 : + std::to_string(required) + " of " + std::to_string(numkeys) + "signatures.");
311 :
312 : // extract and validate PUBKEYs
313 0 : std::vector<CPubKey> pubkeys;
314 0 : for(int pos = 1; pos <= int(numkeys); pos++) {
315 0 : CPubKey pubkey(ParseHex(vStrInputParts[pos + 2]));
316 0 : if (!pubkey.IsFullyValid())
317 0 : throw std::runtime_error("invalid TX output pubkey");
318 0 : pubkeys.push_back(pubkey);
319 : }
320 :
321 : // Extract FLAGS
322 0 : bool bScriptHash = false;
323 0 : if (vStrInputParts.size() == numkeys + 4) {
324 0 : std::string flags = vStrInputParts.back();
325 0 : bScriptHash = (flags.find('S') != std::string::npos);
326 : }
327 0 : else if (vStrInputParts.size() > numkeys + 4) {
328 : // Validate that there were no more parameters passed
329 0 : throw std::runtime_error("Too many parameters");
330 : }
331 :
332 0 : CScript scriptPubKey = GetScriptForMultisig(required, pubkeys);
333 :
334 0 : if (bScriptHash) {
335 0 : if (scriptPubKey.size() > MAX_SCRIPT_ELEMENT_SIZE) {
336 0 : throw std::runtime_error(strprintf(
337 0 : "redeemScript exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_ELEMENT_SIZE));
338 : }
339 : // Get the ID for the script, and then construct a P2SH destination for it.
340 0 : scriptPubKey = GetScriptForDestination(CScriptID(scriptPubKey));
341 : }
342 :
343 : // construct TxOut, append to transaction output list
344 0 : tx.vout.emplace_back(value, scriptPubKey);
345 0 : }
346 :
347 4 : static void MutateTxAddOutScript(CMutableTransaction& tx, const std::string& strInput)
348 : {
349 : // separate VALUE:SCRIPT
350 4 : std::vector<std::string> vStrInputParts;
351 4 : boost::split(vStrInputParts, strInput, boost::is_any_of(":"));
352 4 : if (vStrInputParts.size() < 2)
353 0 : throw std::runtime_error("TX output missing separator");
354 :
355 : // Extract and validate VALUE
356 4 : CAmount value = ExtractAndValidateValue(vStrInputParts[0]);
357 :
358 : // extract and validate script
359 8 : std::string strScript = vStrInputParts[1];
360 12 : CScript scriptPubKey = ParseScript(strScript);
361 :
362 4 : if (scriptPubKey.size() > MAX_SCRIPT_SIZE) {
363 0 : throw std::runtime_error(strprintf(
364 0 : "script exceeds size limit: %d > %d", scriptPubKey.size(), MAX_SCRIPT_SIZE));
365 : }
366 :
367 : // construct TxOut, append to transaction output list
368 4 : tx.vout.emplace_back(value, scriptPubKey);
369 4 : }
370 :
371 3 : static void MutateTxDelInput(CMutableTransaction& tx, const std::string& strInIdx)
372 : {
373 : // parse requested deletion index
374 3 : int inIdx = atoi(strInIdx);
375 3 : if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
376 1 : std::string strErr = "Invalid TX input index '" + strInIdx + "'";
377 1 : throw std::runtime_error(strErr.c_str());
378 : }
379 :
380 : // delete input from transaction
381 2 : tx.vin.erase(tx.vin.begin() + inIdx);
382 2 : }
383 :
384 3 : static void MutateTxDelOutput(CMutableTransaction& tx, const std::string& strOutIdx)
385 : {
386 : // parse requested deletion index
387 3 : int outIdx = atoi(strOutIdx);
388 3 : if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
389 1 : std::string strErr = "Invalid TX output index '" + strOutIdx + "'";
390 1 : throw std::runtime_error(strErr.c_str());
391 : }
392 :
393 : // delete output from transaction
394 2 : tx.vout.erase(tx.vout.begin() + outIdx);
395 2 : }
396 :
397 : static const unsigned int N_SIGHASH_OPTS = 6;
398 : static const struct {
399 : const char* flagStr;
400 : int flags;
401 : } sighashOptions[N_SIGHASH_OPTS] = {
402 : {"ALL", SIGHASH_ALL},
403 : {"NONE", SIGHASH_NONE},
404 : {"SINGLE", SIGHASH_SINGLE},
405 : {"ALL|ANYONECANPAY", SIGHASH_ALL | SIGHASH_ANYONECANPAY},
406 : {"NONE|ANYONECANPAY", SIGHASH_NONE | SIGHASH_ANYONECANPAY},
407 : {"SINGLE|ANYONECANPAY", SIGHASH_SINGLE | SIGHASH_ANYONECANPAY},
408 : };
409 :
410 1 : static bool findSighashFlags(int& flags, const std::string& flagStr)
411 : {
412 1 : flags = 0;
413 :
414 1 : for (unsigned int i = 0; i < N_SIGHASH_OPTS; i++) {
415 1 : if (flagStr == sighashOptions[i].flagStr) {
416 1 : flags = sighashOptions[i].flags;
417 1 : return true;
418 : }
419 : }
420 :
421 : return false;
422 : }
423 :
424 0 : static inline int64_t roundint64(double d)
425 : {
426 0 : return (int64_t)(d > 0 ? d + 0.5 : d - 0.5);
427 : }
428 :
429 0 : static CAmount AmountFromValue(const UniValue& value)
430 : {
431 0 : if (!value.isNum() && !value.isStr())
432 0 : throw std::runtime_error("Amount is not a number or string");
433 0 : double dAmount = value.get_real();
434 0 : if (dAmount <= 0.0 || dAmount > 21000000.0)
435 0 : throw std::runtime_error("Invalid amount");
436 0 : CAmount nAmount = roundint64(dAmount * COIN);
437 0 : if (!Params().GetConsensus().MoneyRange(nAmount))
438 0 : throw std::runtime_error("Amount out of range");
439 0 : return nAmount;
440 : }
441 :
442 1 : static void MutateTxSign(CMutableTransaction& tx, const std::string& flagStr)
443 : {
444 1 : int nHashType = SIGHASH_ALL;
445 :
446 1 : if (flagStr.size() > 0)
447 1 : if (!findSighashFlags(nHashType, flagStr))
448 0 : throw std::runtime_error("unknown sighash flag/sign option");
449 :
450 2 : std::vector<CTransaction> txVariants;
451 2 : txVariants.push_back(tx);
452 :
453 : // mergedTx will end up with all the signatures; it
454 : // starts as a clone of the raw tx:
455 1 : CMutableTransaction mergedTx(txVariants[0]);
456 1 : bool fComplete = true;
457 1 : CCoinsView viewDummy;
458 2 : CCoinsViewCache view(&viewDummy);
459 :
460 2 : if (!registers.count("privatekeys"))
461 0 : throw std::runtime_error("privatekeys register variable must be set.");
462 1 : bool fGivenKeys = false;
463 2 : CBasicKeyStore tempKeystore;
464 2 : UniValue keysObj = registers["privatekeys"];
465 1 : fGivenKeys = true;
466 :
467 2 : for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) {
468 1 : if (!keysObj[kidx].isStr())
469 0 : throw std::runtime_error("privatekey not a string");
470 2 : CKey key = KeyIO::DecodeSecret(keysObj[kidx].getValStr());
471 1 : if (!key.IsValid()) {
472 0 : throw std::runtime_error("privatekey not valid");
473 : }
474 1 : tempKeystore.AddKey(key);
475 : }
476 :
477 : // Add previous txouts given in the RPC call:
478 2 : if (!registers.count("prevtxs"))
479 0 : throw std::runtime_error("prevtxs register variable must be set.");
480 2 : UniValue prevtxsObj = registers["prevtxs"];
481 1 : {
482 2 : for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) {
483 1 : UniValue prevOut = prevtxsObj[previdx];
484 1 : if (!prevOut.isObject())
485 0 : throw std::runtime_error("expected prevtxs internal object");
486 :
487 1 : std::map<std::string, UniValue::VType> types = {
488 1 : {"txid", UniValue::VSTR},
489 1 : {"vout", UniValue::VNUM},
490 1 : {"scriptPubKey", UniValue::VSTR}
491 5 : };
492 1 : if (!prevOut.checkObject(types))
493 0 : throw std::runtime_error("prevtxs internal object typecheck fail");
494 :
495 2 : uint256 txid = ParseHashUV(prevOut["txid"], "txid");
496 :
497 1 : int nOut = atoi(prevOut["vout"].getValStr());
498 1 : if (nOut < 0)
499 0 : throw std::runtime_error("vout must be positive");
500 :
501 1 : COutPoint out(txid, nOut);
502 3 : std::vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
503 2 : CScript scriptPubKey(pkData.begin(), pkData.end());
504 :
505 1 : {
506 1 : const Coin& coin = view.AccessCoin(out);
507 1 : if (!coin.IsSpent() && coin.out.scriptPubKey != scriptPubKey) {
508 0 : std::string err("Previous output scriptPubKey mismatch:\n");
509 0 : err = err + ScriptToAsmStr(coin.out.scriptPubKey) + "\nvs:\n"+
510 0 : ScriptToAsmStr(scriptPubKey);
511 0 : throw std::runtime_error(err);
512 : }
513 :
514 1 : Coin newcoin;
515 1 : newcoin.out.scriptPubKey = scriptPubKey;
516 1 : newcoin.out.nValue = 0;
517 1 : newcoin.nHeight = 1;
518 2 : if (prevOut.exists("amount")) {
519 0 : newcoin.out.nValue = AmountFromValue(prevOut["amount"]);
520 : }
521 : }
522 :
523 : // if redeemScript given and private keys given,
524 : // add redeemScript to the tempKeystore so it can be signed:
525 2 : if (fGivenKeys && scriptPubKey.IsPayToScriptHash() &&
526 1 : prevOut.exists("redeemScript")) {
527 0 : UniValue v = prevOut["redeemScript"];
528 0 : std::vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
529 0 : CScript redeemScript(rsData.begin(), rsData.end());
530 0 : tempKeystore.AddCScript(redeemScript);
531 : }
532 : }
533 : }
534 :
535 1 : const CKeyStore& keystore = tempKeystore;
536 :
537 1 : bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
538 :
539 : // Sign what we can:
540 2 : for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
541 1 : CTxIn& txin = mergedTx.vin[i];
542 1 : const Coin& coin = view.AccessCoin(txin.prevout);
543 1 : if (coin.IsSpent()) {
544 1 : fComplete = false;
545 1 : continue;
546 : }
547 :
548 0 : const CScript& prevPubKey = coin.out.scriptPubKey;
549 0 : const CAmount& amount = coin.out.nValue;
550 :
551 0 : SignatureData sigdata;
552 0 : SigVersion sigversion = mergedTx.GetRequiredSigVersion();
553 : // Only sign SIGHASH_SINGLE if there's a corresponding output:
554 0 : if (!fHashSingle || (i < mergedTx.vout.size()))
555 0 : ProduceSignature(
556 0 : MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType),
557 : prevPubKey,
558 : sigdata,
559 : sigversion,
560 : false // no cold stake
561 : );
562 :
563 : // ... and merge in other signatures:
564 0 : for (const CTransaction& txv : txVariants) {
565 0 : sigdata = CombineSignatures(prevPubKey, MutableTransactionSignatureChecker(&mergedTx, i, amount), sigdata, DataFromTransaction(txv, i));
566 : }
567 0 : UpdateTransaction(mergedTx, i, sigdata);
568 0 : if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS,
569 0 : MutableTransactionSignatureChecker(&mergedTx, i, amount), sigversion))
570 0 : fComplete = false;
571 : }
572 :
573 1 : if (fComplete) {
574 : // do nothing... for now
575 : // perhaps store this for later optional JSON output
576 : }
577 :
578 1 : tx = mergedTx;
579 1 : }
580 :
581 : class Secp256k1Init
582 : {
583 : ECCVerifyHandle globalVerifyHandle;
584 :
585 : public:
586 1 : Secp256k1Init() {
587 1 : ECC_Start();
588 1 : }
589 1 : ~Secp256k1Init() {
590 1 : ECC_Stop();
591 : }
592 : };
593 :
594 32 : static void MutateTx(CMutableTransaction& tx, const std::string& command, const std::string& commandVal)
595 : {
596 64 : std::unique_ptr<Secp256k1Init> ecc;
597 32 : if (command == "nversion") {
598 3 : MutateTxVersion(tx, commandVal);
599 29 : } else if (command == "locktime") {
600 2 : MutateTxLocktime(tx, commandVal);
601 27 : } else if (command == "delin") {
602 3 : MutateTxDelInput(tx, commandVal);
603 24 : } else if (command == "in") {
604 7 : MutateTxAddInput(tx, commandVal);
605 17 : } else if (command == "delout") {
606 3 : MutateTxDelOutput(tx, commandVal);
607 14 : } else if (command == "outaddr") {
608 7 : MutateTxAddOutAddr(tx, commandVal);
609 7 : } else if (command == "outpubkey") {
610 0 : ecc.reset(new Secp256k1Init());
611 0 : MutateTxAddOutPubKey(tx, commandVal);
612 7 : } else if (command == "outmultisig") {
613 0 : ecc.reset(new Secp256k1Init());
614 0 : MutateTxAddOutMultiSig(tx, commandVal);
615 7 : } else if (command == "outscript") {
616 4 : MutateTxAddOutScript(tx, commandVal);
617 3 : } else if (command == "sign") {
618 1 : ecc.reset(new Secp256k1Init());
619 1 : MutateTxSign(tx, commandVal);
620 2 : } else if (command == "load") {
621 0 : RegisterLoad(commandVal);
622 2 : } else if (command == "set") {
623 2 : RegisterSet(commandVal);
624 : } else {
625 0 : throw std::runtime_error("unknown command");
626 : }
627 28 : }
628 :
629 8 : static void OutputTxJSON(const CTransaction& tx)
630 : {
631 8 : UniValue entry(UniValue::VOBJ);
632 8 : TxToUniv(tx, UINT256_ZERO, entry);
633 :
634 16 : std::string jsonOutput = entry.write(4);
635 8 : fprintf(stdout, "%s\n", jsonOutput.c_str());
636 8 : }
637 :
638 0 : static void OutputTxHash(const CTransaction& tx)
639 : {
640 0 : std::string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
641 :
642 0 : fprintf(stdout, "%s\n", strHexHash.c_str());
643 0 : }
644 :
645 10 : static void OutputTxHex(const CTransaction& tx)
646 : {
647 10 : std::string strHex = EncodeHexTx(tx);
648 :
649 10 : fprintf(stdout, "%s\n", strHex.c_str());
650 10 : }
651 :
652 18 : static void OutputTx(const CTransaction& tx)
653 : {
654 18 : if (gArgs.GetBoolArg("-json", false))
655 8 : OutputTxJSON(tx);
656 10 : else if (gArgs.GetBoolArg("-txid", false))
657 0 : OutputTxHash(tx);
658 : else
659 10 : OutputTxHex(tx);
660 18 : }
661 :
662 9 : static std::string readStdin()
663 : {
664 9 : char buf[4096];
665 9 : std::string ret;
666 :
667 17 : while (!feof(stdin)) {
668 17 : size_t bread = fread(buf, 1, sizeof(buf), stdin);
669 17 : ret.append(buf, bread);
670 17 : if (bread < sizeof(buf))
671 : break;
672 : }
673 :
674 9 : if (ferror(stdin))
675 0 : throw std::runtime_error("error reading stdin");
676 :
677 9 : boost::algorithm::trim_right(ret);
678 :
679 9 : return ret;
680 : }
681 :
682 22 : static int CommandLineRawTx(int argc, char* argv[])
683 : {
684 22 : std::string strPrint;
685 22 : int nRet = 0;
686 41 : try {
687 : // Skip switches; Permit common stdin convention "-"
688 41 : while (argc > 1 && IsSwitchChar(argv[1][0]) &&
689 28 : (argv[1][1] != 0)) {
690 19 : argc--;
691 19 : argv++;
692 : }
693 :
694 22 : CMutableTransaction tx;
695 22 : int startArg;
696 :
697 22 : if (!fCreateBlank) {
698 : // require at least one param
699 11 : if (argc < 2)
700 0 : throw std::runtime_error("too few parameters");
701 :
702 : // param: hex-encoded pivx transaction
703 22 : std::string strHexTx(argv[1]);
704 11 : if (strHexTx == "-") // "-" implies standard input
705 9 : strHexTx = readStdin();
706 :
707 11 : if (!DecodeHexTx(tx, strHexTx))
708 0 : throw std::runtime_error("invalid transaction encoding");
709 :
710 11 : startArg = 2;
711 : } else
712 : startArg = 1;
713 :
714 50 : for (int i = startArg; i < argc; i++) {
715 64 : std::string arg = argv[i];
716 68 : std::string key, value;
717 32 : size_t eqpos = arg.find('=');
718 32 : if (eqpos == std::string::npos)
719 0 : key = arg;
720 : else {
721 32 : key = arg.substr(0, eqpos);
722 32 : value = arg.substr(eqpos + 1);
723 : }
724 :
725 32 : MutateTx(tx, key, value);
726 : }
727 :
728 22 : OutputTx(tx);
729 : }
730 :
731 0 : catch (const boost::thread_interrupted&) {
732 0 : throw;
733 4 : } catch (const std::exception& e) {
734 4 : strPrint = std::string("error: ") + e.what();
735 4 : nRet = EXIT_FAILURE;
736 0 : } catch (...) {
737 0 : PrintExceptionContinue(nullptr, "CommandLineRawTx()");
738 0 : throw;
739 : }
740 :
741 22 : if (strPrint != "") {
742 4 : fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
743 : }
744 26 : return nRet;
745 : }
746 :
747 22 : int main(int argc, char* argv[])
748 : {
749 22 : SetupEnvironment();
750 :
751 22 : try {
752 22 : int ret = AppInitRawTx(argc, argv);
753 22 : if (ret != CONTINUE_EXECUTION)
754 : return ret;
755 0 : } catch (const std::exception& e) {
756 0 : PrintExceptionContinue(&e, "AppInitRawTx()");
757 0 : return EXIT_FAILURE;
758 0 : } catch (...) {
759 0 : PrintExceptionContinue(nullptr, "AppInitRawTx()");
760 0 : return EXIT_FAILURE;
761 : }
762 :
763 22 : int ret = EXIT_FAILURE;
764 22 : try {
765 22 : ret = CommandLineRawTx(argc, argv);
766 0 : } catch (const std::exception& e) {
767 0 : PrintExceptionContinue(&e, "CommandLineRawTx()");
768 0 : } catch (...) {
769 0 : PrintExceptionContinue(nullptr, "CommandLineRawTx()");
770 : }
771 : return ret;
772 : }
|