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