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