Line data Source code
1 : // Copyright (c) 2009-2014 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 : #include "base58.h"
6 : #include "clientversion.h"
7 : #include "coins.h"
8 : #include "consensus/consensus.h"
9 : #include "core_io.h"
10 : #include "keystore.h"
11 : #include "policy/policy.h"
12 : #include "primitives/transaction.h"
13 : #include "script/script.h"
14 : #include "script/sign.h"
15 : #include <univalue.h>
16 : #include "util.h"
17 : #include "utilmoneystr.h"
18 : #include "utilstrencodings.h"
19 :
20 : #include <stdio.h>
21 :
22 : #include <boost/algorithm/string.hpp>
23 : #include <boost/assign/list_of.hpp>
24 :
25 : using namespace std;
26 :
27 : static bool fCreateBlank;
28 14 : static map<string,UniValue> registers;
29 :
30 14 : static bool AppInitRawTx(int argc, char* argv[])
31 : {
32 : //
33 : // Parameters
34 : //
35 14 : ParseParameters(argc, argv);
36 :
37 : // Check for -testnet or -regtest parameter (Params() calls are only valid after this clause)
38 14 : if (!SelectParamsFromCommandLine()) {
39 0 : fprintf(stderr, "Error: Invalid combination of -regtest and -testnet.\n");
40 0 : return false;
41 : }
42 :
43 42 : fCreateBlank = GetBoolArg("-create", false);
44 :
45 98 : if (argc<2 || mapArgs.count("-?") || mapArgs.count("-help"))
46 : {
47 : // First part of help message is specific to this utility
48 0 : std::string strUsage = _("Bitcoin Core bitcoin-tx utility version") + " " + FormatFullVersion() + "\n\n" +
49 0 : _("Usage:") + "\n" +
50 0 : " bitcoin-tx [options] <hex-tx> [commands] " + _("Update hex-encoded bitcoin transaction") + "\n" +
51 0 : " bitcoin-tx [options] -create [commands] " + _("Create hex-encoded bitcoin transaction") + "\n" +
52 0 : "\n";
53 :
54 0 : fprintf(stdout, "%s", strUsage.c_str());
55 :
56 0 : strUsage = HelpMessageGroup(_("Options:"));
57 0 : strUsage += HelpMessageOpt("-?", _("This help message"));
58 0 : strUsage += HelpMessageOpt("-create", _("Create new, empty TX."));
59 0 : strUsage += HelpMessageOpt("-json", _("Select JSON output"));
60 0 : strUsage += HelpMessageOpt("-txid", _("Output only the hex-encoded transaction id of the resultant transaction."));
61 0 : strUsage += HelpMessageOpt("-regtest", _("Enter regression test mode, which uses a special chain in which blocks can be solved instantly."));
62 0 : strUsage += HelpMessageOpt("-testnet", _("Use the test network"));
63 :
64 0 : fprintf(stdout, "%s", strUsage.c_str());
65 :
66 0 : strUsage = HelpMessageGroup(_("Commands:"));
67 0 : strUsage += HelpMessageOpt("delin=N", _("Delete input N from TX"));
68 0 : strUsage += HelpMessageOpt("delout=N", _("Delete output N from TX"));
69 0 : strUsage += HelpMessageOpt("in=TXID:VOUT", _("Add input to TX"));
70 0 : strUsage += HelpMessageOpt("locktime=N", _("Set TX lock time to N"));
71 0 : strUsage += HelpMessageOpt("nversion=N", _("Set TX version to N"));
72 0 : strUsage += HelpMessageOpt("outaddr=VALUE:ADDRESS", _("Add address-based output to TX"));
73 0 : strUsage += HelpMessageOpt("outdata=[VALUE:]DATA", _("Add data-based output to TX"));
74 0 : strUsage += HelpMessageOpt("outscript=VALUE:SCRIPT", _("Add raw script output to TX"));
75 0 : strUsage += HelpMessageOpt("sign=SIGHASH-FLAGS", _("Add zero or more signatures to transaction") + ". " +
76 0 : _("This command requires JSON registers:") +
77 0 : _("prevtxs=JSON object") + ", " +
78 0 : _("privatekeys=JSON object") + ". " +
79 : _("See signrawtransaction docs for format of sighash flags, JSON objects."));
80 0 : fprintf(stdout, "%s", strUsage.c_str());
81 :
82 0 : strUsage = HelpMessageGroup(_("Register Commands:"));
83 0 : strUsage += HelpMessageOpt("load=NAME:FILENAME", _("Load JSON file FILENAME into register NAME"));
84 0 : strUsage += HelpMessageOpt("set=NAME:JSON-STRING", _("Set register NAME to given JSON-STRING"));
85 0 : fprintf(stdout, "%s", strUsage.c_str());
86 :
87 0 : return false;
88 : }
89 : return true;
90 : }
91 :
92 2 : static void RegisterSetJson(const string& key, const string& rawJson)
93 : {
94 : UniValue val;
95 2 : if (!val.read(rawJson)) {
96 0 : string strErr = "Cannot parse JSON for key " + key;
97 0 : throw runtime_error(strErr);
98 : }
99 :
100 2 : registers[key] = val;
101 2 : }
102 :
103 2 : static void RegisterSet(const string& strInput)
104 : {
105 : // separate NAME:VALUE in string
106 2 : size_t pos = strInput.find(':');
107 4 : if ((pos == string::npos) ||
108 4 : (pos == 0) ||
109 2 : (pos == (strInput.size() - 1)))
110 0 : throw runtime_error("Register input requires NAME:VALUE");
111 :
112 2 : string key = strInput.substr(0, pos);
113 2 : string valStr = strInput.substr(pos + 1, string::npos);
114 :
115 2 : RegisterSetJson(key, valStr);
116 2 : }
117 :
118 0 : static void RegisterLoad(const string& strInput)
119 : {
120 : // separate NAME:FILENAME in string
121 0 : size_t pos = strInput.find(':');
122 0 : if ((pos == string::npos) ||
123 0 : (pos == 0) ||
124 0 : (pos == (strInput.size() - 1)))
125 0 : throw runtime_error("Register load requires NAME:FILENAME");
126 :
127 0 : string key = strInput.substr(0, pos);
128 0 : string filename = strInput.substr(pos + 1, string::npos);
129 :
130 0 : FILE *f = fopen(filename.c_str(), "r");
131 0 : if (!f) {
132 0 : string strErr = "Cannot open file " + filename;
133 0 : throw runtime_error(strErr);
134 : }
135 :
136 : // load file chunks into one big buffer
137 : string valStr;
138 0 : while ((!feof(f)) && (!ferror(f))) {
139 : char buf[4096];
140 0 : int bread = fread(buf, 1, sizeof(buf), f);
141 0 : if (bread <= 0)
142 : break;
143 :
144 0 : valStr.insert(valStr.size(), buf, bread);
145 : }
146 :
147 0 : int error = ferror(f);
148 0 : fclose(f);
149 :
150 0 : if (error) {
151 0 : string strErr = "Error reading file " + filename;
152 0 : throw runtime_error(strErr);
153 : }
154 :
155 : // evaluate as JSON buffer register
156 0 : RegisterSetJson(key, valStr);
157 0 : }
158 :
159 0 : static void MutateTxVersion(CMutableTransaction& tx, const string& cmdVal)
160 : {
161 0 : int64_t newVersion = atoi64(cmdVal);
162 0 : if (newVersion < 1 || newVersion > CTransaction::CURRENT_VERSION)
163 0 : throw runtime_error("Invalid TX version requested");
164 :
165 0 : tx.nVersion = (int) newVersion;
166 0 : }
167 :
168 1 : static void MutateTxLocktime(CMutableTransaction& tx, const string& cmdVal)
169 : {
170 1 : int64_t newLocktime = atoi64(cmdVal);
171 1 : if (newLocktime < 0LL || newLocktime > 0xffffffffLL)
172 0 : throw runtime_error("Invalid TX locktime requested");
173 :
174 1 : tx.nLockTime = (unsigned int) newLocktime;
175 1 : }
176 :
177 8 : static void MutateTxAddInput(CMutableTransaction& tx, const string& strInput)
178 : {
179 : // separate TXID:VOUT in string
180 8 : size_t pos = strInput.find(':');
181 16 : if ((pos == string::npos) ||
182 16 : (pos == 0) ||
183 8 : (pos == (strInput.size() - 1)))
184 0 : throw runtime_error("TX input missing separator");
185 :
186 : // extract and validate TXID
187 8 : string strTxid = strInput.substr(0, pos);
188 8 : if ((strTxid.size() != 64) || !IsHex(strTxid))
189 0 : throw runtime_error("invalid TX input txid");
190 : uint256 txid(uint256S(strTxid));
191 :
192 : static const unsigned int minTxOutSz = 9;
193 : static const unsigned int maxVout = MAX_BLOCK_SIZE / minTxOutSz;
194 :
195 : // extract and validate vout
196 8 : string strVout = strInput.substr(pos + 1, string::npos);
197 8 : int vout = atoi(strVout);
198 8 : if ((vout < 0) || (vout > (int)maxVout))
199 0 : throw runtime_error("invalid TX input vout");
200 :
201 : // append to transaction input list
202 24 : CTxIn txin(txid, vout);
203 8 : tx.vin.push_back(txin);
204 8 : }
205 :
206 5 : static void MutateTxAddOutAddr(CMutableTransaction& tx, const string& strInput)
207 : {
208 : // separate VALUE:ADDRESS in string
209 5 : size_t pos = strInput.find(':');
210 10 : if ((pos == string::npos) ||
211 10 : (pos == 0) ||
212 5 : (pos == (strInput.size() - 1)))
213 0 : throw runtime_error("TX output missing separator");
214 :
215 : // extract and validate VALUE
216 5 : string strValue = strInput.substr(0, pos);
217 : CAmount value;
218 5 : if (!ParseMoney(strValue, value))
219 0 : throw runtime_error("invalid TX output value");
220 :
221 : // extract and validate ADDRESS
222 5 : string strAddr = strInput.substr(pos + 1, string::npos);
223 5 : CBitcoinAddress addr(strAddr);
224 5 : if (!addr.IsValid())
225 0 : throw runtime_error("invalid TX output address");
226 :
227 : // build standard output script via GetScriptForDestination()
228 10 : CScript scriptPubKey = GetScriptForDestination(addr.Get());
229 :
230 : // construct TxOut, append to transaction output list
231 10 : CTxOut txout(value, scriptPubKey);
232 5 : tx.vout.push_back(txout);
233 5 : }
234 :
235 4 : static void MutateTxAddOutData(CMutableTransaction& tx, const string& strInput)
236 : {
237 4 : CAmount value = 0;
238 :
239 : // separate [VALUE:]DATA in string
240 4 : size_t pos = strInput.find(':');
241 :
242 4 : if (pos==0)
243 0 : throw runtime_error("TX output value not specified");
244 :
245 4 : if (pos != string::npos) {
246 : // extract and validate VALUE
247 2 : string strValue = strInput.substr(0, pos);
248 2 : if (!ParseMoney(strValue, value))
249 0 : throw runtime_error("invalid TX output value");
250 : }
251 :
252 : // extract and validate DATA
253 4 : string strData = strInput.substr(pos + 1, string::npos);
254 :
255 4 : if (!IsHex(strData))
256 6 : throw runtime_error("invalid TX output data");
257 :
258 2 : std::vector<unsigned char> data = ParseHex(strData);
259 :
260 8 : CTxOut txout(value, CScript() << OP_RETURN << data);
261 2 : tx.vout.push_back(txout);
262 2 : }
263 :
264 1 : static void MutateTxAddOutScript(CMutableTransaction& tx, const string& strInput)
265 : {
266 : // separate VALUE:SCRIPT in string
267 1 : size_t pos = strInput.find(':');
268 1 : if ((pos == string::npos) ||
269 : (pos == 0))
270 0 : throw runtime_error("TX output missing separator");
271 :
272 : // extract and validate VALUE
273 1 : string strValue = strInput.substr(0, pos);
274 : CAmount value;
275 1 : if (!ParseMoney(strValue, value))
276 0 : throw runtime_error("invalid TX output value");
277 :
278 : // extract and validate script
279 1 : string strScript = strInput.substr(pos + 1, string::npos);
280 1 : CScript scriptPubKey = ParseScript(strScript); // throws on err
281 :
282 : // construct TxOut, append to transaction output list
283 2 : CTxOut txout(value, scriptPubKey);
284 1 : tx.vout.push_back(txout);
285 1 : }
286 :
287 2 : static void MutateTxDelInput(CMutableTransaction& tx, const string& strInIdx)
288 : {
289 : // parse requested deletion index
290 2 : int inIdx = atoi(strInIdx);
291 4 : if (inIdx < 0 || inIdx >= (int)tx.vin.size()) {
292 2 : string strErr = "Invalid TX input index '" + strInIdx + "'";
293 3 : throw runtime_error(strErr.c_str());
294 : }
295 :
296 : // delete input from transaction
297 3 : tx.vin.erase(tx.vin.begin() + inIdx);
298 1 : }
299 :
300 2 : static void MutateTxDelOutput(CMutableTransaction& tx, const string& strOutIdx)
301 : {
302 : // parse requested deletion index
303 2 : int outIdx = atoi(strOutIdx);
304 4 : if (outIdx < 0 || outIdx >= (int)tx.vout.size()) {
305 2 : string strErr = "Invalid TX output index '" + strOutIdx + "'";
306 4 : throw runtime_error(strErr.c_str());
307 : }
308 :
309 : // delete output from transaction
310 3 : tx.vout.erase(tx.vout.begin() + outIdx);
311 1 : }
312 :
313 : static const unsigned int N_SIGHASH_OPTS = 6;
314 : static const struct {
315 : const char *flagStr;
316 : int flags;
317 : } sighashOptions[N_SIGHASH_OPTS] = {
318 : {"ALL", SIGHASH_ALL},
319 : {"NONE", SIGHASH_NONE},
320 : {"SINGLE", SIGHASH_SINGLE},
321 : {"ALL|ANYONECANPAY", SIGHASH_ALL|SIGHASH_ANYONECANPAY},
322 : {"NONE|ANYONECANPAY", SIGHASH_NONE|SIGHASH_ANYONECANPAY},
323 : {"SINGLE|ANYONECANPAY", SIGHASH_SINGLE|SIGHASH_ANYONECANPAY},
324 : };
325 :
326 1 : static bool findSighashFlags(int& flags, const string& flagStr)
327 : {
328 1 : flags = 0;
329 :
330 1 : for (unsigned int i = 0; i < N_SIGHASH_OPTS; i++) {
331 2 : if (flagStr == sighashOptions[i].flagStr) {
332 1 : flags = sighashOptions[i].flags;
333 1 : return true;
334 : }
335 : }
336 :
337 : return false;
338 : }
339 :
340 0 : uint256 ParseHashUO(map<string,UniValue>& o, string strKey)
341 : {
342 0 : if (!o.count(strKey))
343 : return uint256();
344 0 : return ParseHashUV(o[strKey], strKey);
345 : }
346 :
347 0 : vector<unsigned char> ParseHexUO(map<string,UniValue>& o, string strKey)
348 : {
349 0 : if (!o.count(strKey)) {
350 : vector<unsigned char> emptyVec;
351 0 : return emptyVec;
352 : }
353 0 : return ParseHexUV(o[strKey], strKey);
354 : }
355 :
356 1 : static void MutateTxSign(CMutableTransaction& tx, const string& flagStr)
357 : {
358 1 : int nHashType = SIGHASH_ALL;
359 :
360 1 : if (flagStr.size() > 0)
361 1 : if (!findSighashFlags(nHashType, flagStr))
362 0 : throw runtime_error("unknown sighash flag/sign option");
363 :
364 : vector<CTransaction> txVariants;
365 2 : txVariants.push_back(tx);
366 :
367 : // mergedTx will end up with all the signatures; it
368 : // starts as a clone of the raw tx:
369 1 : CMutableTransaction mergedTx(txVariants[0]);
370 1 : bool fComplete = true;
371 : CCoinsView viewDummy;
372 2 : CCoinsViewCache view(&viewDummy);
373 :
374 4 : if (!registers.count("privatekeys"))
375 0 : throw runtime_error("privatekeys register variable must be set.");
376 1 : bool fGivenKeys = false;
377 2 : CBasicKeyStore tempKeystore;
378 4 : UniValue keysObj = registers["privatekeys"];
379 1 : fGivenKeys = true;
380 :
381 4 : for (unsigned int kidx = 0; kidx < keysObj.size(); kidx++) {
382 1 : if (!keysObj[kidx].isStr())
383 0 : throw runtime_error("privatekey not a string");
384 : CBitcoinSecret vchSecret;
385 2 : bool fGood = vchSecret.SetString(keysObj[kidx].getValStr());
386 1 : if (!fGood)
387 0 : throw runtime_error("privatekey not valid");
388 :
389 1 : CKey key = vchSecret.GetKey();
390 1 : tempKeystore.AddKey(key);
391 : }
392 :
393 : // Add previous txouts given in the RPC call:
394 4 : if (!registers.count("prevtxs"))
395 0 : throw runtime_error("prevtxs register variable must be set.");
396 4 : UniValue prevtxsObj = registers["prevtxs"];
397 : {
398 4 : for (unsigned int previdx = 0; previdx < prevtxsObj.size(); previdx++) {
399 1 : UniValue prevOut = prevtxsObj[previdx];
400 1 : if (!prevOut.isObject())
401 0 : throw runtime_error("expected prevtxs internal object");
402 :
403 4 : map<string,UniValue::VType> types = boost::assign::map_list_of("txid", UniValue::VSTR)("vout",UniValue::VNUM)("scriptPubKey",UniValue::VSTR);
404 1 : if (!prevOut.checkObject(types))
405 0 : throw runtime_error("prevtxs internal object typecheck fail");
406 :
407 5 : uint256 txid = ParseHashUV(prevOut["txid"], "txid");
408 :
409 4 : int nOut = atoi(prevOut["vout"].getValStr());
410 1 : if (nOut < 0)
411 0 : throw runtime_error("vout must be positive");
412 :
413 5 : vector<unsigned char> pkData(ParseHexUV(prevOut["scriptPubKey"], "scriptPubKey"));
414 2 : CScript scriptPubKey(pkData.begin(), pkData.end());
415 :
416 : {
417 1 : CCoinsModifier coins = view.ModifyCoins(txid);
418 3 : if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) {
419 0 : string err("Previous output scriptPubKey mismatch:\n");
420 0 : err = err + ScriptToAsmStr(coins->vout[nOut].scriptPubKey) + "\nvs:\n"+
421 : ScriptToAsmStr(scriptPubKey);
422 0 : throw runtime_error(err);
423 : }
424 2 : if ((unsigned int)nOut >= coins->vout.size())
425 3 : coins->vout.resize(nOut+1);
426 3 : coins->vout[nOut].scriptPubKey = scriptPubKey;
427 3 : coins->vout[nOut].nValue = 0; // we don't know the actual output value
428 : }
429 :
430 : // if redeemScript given and private keys given,
431 : // add redeemScript to the tempKeystore so it can be signed:
432 3 : if (fGivenKeys && scriptPubKey.IsPayToScriptHash() &&
433 1 : prevOut.exists("redeemScript")) {
434 0 : UniValue v = prevOut["redeemScript"];
435 0 : vector<unsigned char> rsData(ParseHexUV(v, "redeemScript"));
436 0 : CScript redeemScript(rsData.begin(), rsData.end());
437 0 : tempKeystore.AddCScript(redeemScript);
438 : }
439 1 : }
440 : }
441 :
442 1 : const CKeyStore& keystore = tempKeystore;
443 :
444 1 : bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
445 :
446 : // Sign what we can:
447 4 : for (unsigned int i = 0; i < mergedTx.vin.size(); i++) {
448 2 : CTxIn& txin = mergedTx.vin[i];
449 1 : const CCoins* coins = view.AccessCoins(txin.prevout.hash);
450 2 : if (!coins || !coins->IsAvailable(txin.prevout.n)) {
451 : fComplete = false;
452 : continue;
453 : }
454 2 : const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey;
455 :
456 1 : txin.scriptSig.clear();
457 : // Only sign SIGHASH_SINGLE if there's a corresponding output:
458 1 : if (!fHashSingle || (i < mergedTx.vout.size()))
459 1 : SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
460 :
461 : // ... and merge in other signatures:
462 11 : BOOST_FOREACH(const CTransaction& txv, txVariants) {
463 4 : txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
464 : }
465 1 : if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, MutableTransactionSignatureChecker(&mergedTx, i)))
466 : fComplete = false;
467 : }
468 :
469 : if (fComplete) {
470 : // do nothing... for now
471 : // perhaps store this for later optional JSON output
472 : }
473 :
474 2 : tx = mergedTx;
475 1 : }
476 :
477 : class Secp256k1Init
478 : {
479 : public:
480 1 : Secp256k1Init() { ECC_Start(); }
481 1 : ~Secp256k1Init() { ECC_Stop(); }
482 : };
483 :
484 26 : static void MutateTx(CMutableTransaction& tx, const string& command,
485 : const string& commandVal)
486 : {
487 : boost::scoped_ptr<Secp256k1Init> ecc;
488 :
489 26 : if (command == "nversion")
490 0 : MutateTxVersion(tx, commandVal);
491 26 : else if (command == "locktime")
492 1 : MutateTxLocktime(tx, commandVal);
493 :
494 25 : else if (command == "delin")
495 2 : MutateTxDelInput(tx, commandVal);
496 23 : else if (command == "in")
497 8 : MutateTxAddInput(tx, commandVal);
498 :
499 15 : else if (command == "delout")
500 2 : MutateTxDelOutput(tx, commandVal);
501 13 : else if (command == "outaddr")
502 5 : MutateTxAddOutAddr(tx, commandVal);
503 8 : else if (command == "outdata")
504 4 : MutateTxAddOutData(tx, commandVal);
505 4 : else if (command == "outscript")
506 1 : MutateTxAddOutScript(tx, commandVal);
507 :
508 3 : else if (command == "sign") {
509 2 : if (!ecc) { ecc.reset(new Secp256k1Init()); }
510 1 : MutateTxSign(tx, commandVal);
511 : }
512 :
513 2 : else if (command == "load")
514 0 : RegisterLoad(commandVal);
515 :
516 2 : else if (command == "set")
517 2 : RegisterSet(commandVal);
518 :
519 : else
520 0 : throw runtime_error("unknown command");
521 22 : }
522 :
523 0 : static void OutputTxJSON(const CTransaction& tx)
524 : {
525 0 : UniValue entry(UniValue::VOBJ);
526 0 : TxToUniv(tx, uint256(), entry);
527 :
528 0 : string jsonOutput = entry.write(4);
529 0 : fprintf(stdout, "%s\n", jsonOutput.c_str());
530 0 : }
531 :
532 0 : static void OutputTxHash(const CTransaction& tx)
533 : {
534 0 : string strHexHash = tx.GetHash().GetHex(); // the hex-encoded transaction hash (aka the transaction id)
535 :
536 0 : fprintf(stdout, "%s\n", strHexHash.c_str());
537 0 : }
538 :
539 10 : static void OutputTxHex(const CTransaction& tx)
540 : {
541 10 : string strHex = EncodeHexTx(tx);
542 :
543 10 : fprintf(stdout, "%s\n", strHex.c_str());
544 10 : }
545 :
546 10 : static void OutputTx(const CTransaction& tx)
547 : {
548 30 : if (GetBoolArg("-json", false))
549 0 : OutputTxJSON(tx);
550 30 : else if (GetBoolArg("-txid", false))
551 0 : OutputTxHash(tx);
552 : else
553 10 : OutputTxHex(tx);
554 10 : }
555 :
556 6 : static string readStdin()
557 : {
558 : char buf[4096];
559 : string ret;
560 :
561 11 : while (!feof(stdin)) {
562 22 : size_t bread = fread(buf, 1, sizeof(buf), stdin);
563 11 : ret.append(buf, bread);
564 11 : if (bread < sizeof(buf))
565 : break;
566 : }
567 :
568 6 : if (ferror(stdin))
569 0 : throw runtime_error("error reading stdin");
570 :
571 6 : boost::algorithm::trim_right(ret);
572 :
573 6 : return ret;
574 : }
575 :
576 14 : static int CommandLineRawTx(int argc, char* argv[])
577 : {
578 : string strPrint;
579 14 : int nRet = 0;
580 : try {
581 : // Skip switches; Permit common stdin convention "-"
582 50 : while (argc > 1 && IsSwitchChar(argv[1][0]) &&
583 14 : (argv[1][1] != 0)) {
584 8 : argc--;
585 8 : argv++;
586 : }
587 :
588 14 : CTransaction txDecodeTmp;
589 : int startArg;
590 :
591 14 : if (!fCreateBlank) {
592 : // require at least one param
593 6 : if (argc < 2)
594 0 : throw runtime_error("too few parameters");
595 :
596 : // param: hex-encoded bitcoin transaction
597 16 : string strHexTx(argv[1]);
598 6 : if (strHexTx == "-") // "-" implies standard input
599 12 : strHexTx = readStdin();
600 :
601 6 : if (!DecodeHexTx(txDecodeTmp, strHexTx))
602 0 : throw runtime_error("invalid transaction encoding");
603 :
604 6 : startArg = 2;
605 : } else
606 : startArg = 1;
607 :
608 14 : CMutableTransaction tx(txDecodeTmp);
609 :
610 22 : for (int i = startArg; i < argc; i++) {
611 56 : string arg = argv[i];
612 : string key, value;
613 26 : size_t eqpos = arg.find('=');
614 26 : if (eqpos == string::npos)
615 : key = arg;
616 : else {
617 52 : key = arg.substr(0, eqpos);
618 52 : value = arg.substr(eqpos + 1);
619 : }
620 :
621 26 : MutateTx(tx, key, value);
622 : }
623 :
624 20 : OutputTx(tx);
625 : }
626 :
627 0 : catch (const boost::thread_interrupted&) {
628 0 : throw;
629 : }
630 8 : catch (const std::exception& e) {
631 16 : strPrint = string("error: ") + e.what();
632 4 : nRet = EXIT_FAILURE;
633 : }
634 0 : catch (...) {
635 0 : PrintExceptionContinue(NULL, "CommandLineRawTx()");
636 0 : throw;
637 : }
638 :
639 14 : if (strPrint != "") {
640 4 : fprintf((nRet == 0 ? stdout : stderr), "%s\n", strPrint.c_str());
641 : }
642 28 : return nRet;
643 : }
644 :
645 14 : int main(int argc, char* argv[])
646 : {
647 14 : SetupEnvironment();
648 :
649 : try {
650 14 : if(!AppInitRawTx(argc, argv))
651 : return EXIT_FAILURE;
652 : }
653 0 : catch (const std::exception& e) {
654 0 : PrintExceptionContinue(&e, "AppInitRawTx()");
655 : return EXIT_FAILURE;
656 0 : } catch (...) {
657 0 : PrintExceptionContinue(NULL, "AppInitRawTx()");
658 : return EXIT_FAILURE;
659 : }
660 :
661 14 : int ret = EXIT_FAILURE;
662 : try {
663 14 : ret = CommandLineRawTx(argc, argv);
664 : }
665 0 : catch (const std::exception& e) {
666 0 : PrintExceptionContinue(&e, "CommandLineRawTx()");
667 0 : } catch (...) {
668 0 : PrintExceptionContinue(NULL, "CommandLineRawTx()");
669 : }
670 14 : return ret;
671 42 : }
|