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