]>
Commit | Line | Data |
---|---|---|
a2709fad | 1 | // Copyright (c) 2010 Satoshi Nakamoto |
8ac2a4e1 | 2 | // Copyright (c) 2009-2015 The Bitcoin Core developers |
72fb3d29 | 3 | // Distributed under the MIT software license, see the accompanying |
a2709fad GA |
4 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
5 | ||
a2709fad | 6 | #include "base58.h" |
be126699 | 7 | #include "consensus/upgrades.h" |
da29ecbc | 8 | #include "consensus/validation.h" |
ae775b5b | 9 | #include "core_io.h" |
a2709fad | 10 | #include "init.h" |
c037531d PK |
11 | #include "keystore.h" |
12 | #include "main.h" | |
59ed61b3 | 13 | #include "merkleblock.h" |
771ffb5e | 14 | #include "net.h" |
da29ecbc | 15 | #include "primitives/transaction.h" |
c037531d | 16 | #include "rpcserver.h" |
c4408a6c | 17 | #include "script/script.h" |
8ac2a4e1 | 18 | #include "script/script_error.h" |
e088d65a | 19 | #include "script/sign.h" |
611116d4 | 20 | #include "script/standard.h" |
51ed9ec9 | 21 | #include "uint256.h" |
48ba56cd | 22 | #ifdef ENABLE_WALLET |
50c72f23 | 23 | #include "wallet/wallet.h" |
48ba56cd | 24 | #endif |
a2709fad | 25 | |
037a30c3 | 26 | #include "komodo_defs.h" |
27 | ||
51ed9ec9 BD |
28 | #include <stdint.h> |
29 | ||
30 | #include <boost/assign/list_of.hpp> | |
51ed9ec9 | 31 | |
a10a6e2a | 32 | #include <univalue.h> |
d014114d | 33 | |
611116d4 | 34 | using namespace std; |
a2709fad | 35 | |
08bddeff | 36 | extern char ASSETCHAINS_SYMBOL[]; |
37 | ||
d014114d | 38 | void ScriptPubKeyToJSON(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex) |
a2709fad GA |
39 | { |
40 | txnouttype type; | |
41 | vector<CTxDestination> addresses; | |
42 | int nRequired; | |
43 | ||
44 | out.push_back(Pair("asm", scriptPubKey.ToString())); | |
be066fad PT |
45 | if (fIncludeHex) |
46 | out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end()))); | |
a2709fad | 47 | |
c00ab642 | 48 | if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired)) |
49 | { | |
22de68df | 50 | out.push_back(Pair("type", GetTxnOutputType(type))); |
a2709fad GA |
51 | return; |
52 | } | |
53 | ||
54 | out.push_back(Pair("reqSigs", nRequired)); | |
55 | out.push_back(Pair("type", GetTxnOutputType(type))); | |
56 | ||
38fc4b70 | 57 | UniValue a(UniValue::VARR); |
a2709fad GA |
58 | BOOST_FOREACH(const CTxDestination& addr, addresses) |
59 | a.push_back(CBitcoinAddress(addr).ToString()); | |
60 | out.push_back(Pair("addresses", a)); | |
61 | } | |
62 | ||
0d37ae3a JG |
63 | UniValue TxJoinSplitToJSON(const CTransaction& tx) { |
64 | UniValue vjoinsplit(UniValue::VARR); | |
8675d94b | 65 | for (unsigned int i = 0; i < tx.vjoinsplit.size(); i++) { |
b7e4abd6 | 66 | const JSDescription& jsdescription = tx.vjoinsplit[i]; |
0d37ae3a | 67 | UniValue joinsplit(UniValue::VOBJ); |
63ea9b75 | 68 | |
6d0ab3ee | 69 | joinsplit.push_back(Pair("vpub_old", ValueFromAmount(jsdescription.vpub_old))); |
8b78a819 | 70 | joinsplit.push_back(Pair("vpub_oldZat", jsdescription.vpub_old)); |
6d0ab3ee | 71 | joinsplit.push_back(Pair("vpub_new", ValueFromAmount(jsdescription.vpub_new))); |
8b78a819 | 72 | joinsplit.push_back(Pair("vpub_newZat", jsdescription.vpub_new)); |
63ea9b75 | 73 | |
b7e4abd6 | 74 | joinsplit.push_back(Pair("anchor", jsdescription.anchor.GetHex())); |
63ea9b75 SB |
75 | |
76 | { | |
0d37ae3a | 77 | UniValue nullifiers(UniValue::VARR); |
22de1602 SB |
78 | BOOST_FOREACH(const uint256 nf, jsdescription.nullifiers) { |
79 | nullifiers.push_back(nf.GetHex()); | |
63ea9b75 | 80 | } |
b7e4abd6 | 81 | joinsplit.push_back(Pair("nullifiers", nullifiers)); |
63ea9b75 SB |
82 | } |
83 | ||
84 | { | |
0d37ae3a | 85 | UniValue commitments(UniValue::VARR); |
b7e4abd6 | 86 | BOOST_FOREACH(const uint256 commitment, jsdescription.commitments) { |
63ea9b75 SB |
87 | commitments.push_back(commitment.GetHex()); |
88 | } | |
b7e4abd6 | 89 | joinsplit.push_back(Pair("commitments", commitments)); |
63ea9b75 SB |
90 | } |
91 | ||
6d0ab3ee JG |
92 | joinsplit.push_back(Pair("onetimePubKey", jsdescription.ephemeralKey.GetHex())); |
93 | joinsplit.push_back(Pair("randomSeed", jsdescription.randomSeed.GetHex())); | |
94 | ||
63ea9b75 | 95 | { |
0d37ae3a | 96 | UniValue macs(UniValue::VARR); |
b7e4abd6 | 97 | BOOST_FOREACH(const uint256 mac, jsdescription.macs) { |
63ea9b75 SB |
98 | macs.push_back(mac.GetHex()); |
99 | } | |
b7e4abd6 | 100 | joinsplit.push_back(Pair("macs", macs)); |
63ea9b75 SB |
101 | } |
102 | ||
6d0ab3ee JG |
103 | CDataStream ssProof(SER_NETWORK, PROTOCOL_VERSION); |
104 | ssProof << jsdescription.proof; | |
105 | joinsplit.push_back(Pair("proof", HexStr(ssProof.begin(), ssProof.end()))); | |
106 | ||
107 | { | |
0d37ae3a | 108 | UniValue ciphertexts(UniValue::VARR); |
6d0ab3ee JG |
109 | for (const ZCNoteEncryption::Ciphertext ct : jsdescription.ciphertexts) { |
110 | ciphertexts.push_back(HexStr(ct.begin(), ct.end())); | |
111 | } | |
112 | joinsplit.push_back(Pair("ciphertexts", ciphertexts)); | |
113 | } | |
63ea9b75 | 114 | |
b7e4abd6 | 115 | vjoinsplit.push_back(joinsplit); |
63ea9b75 | 116 | } |
f7cfb52d S |
117 | return vjoinsplit; |
118 | } | |
119 | ||
ae3f3fea | 120 | 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); |
cad0d1ca | 121 | |
8b78a819 | 122 | void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, int nHeight = 0, int nConfirmations = 0, int nBlockTime = 0) |
f7cfb52d | 123 | { |
bd1f06d8 | 124 | uint256 txid = tx.GetHash(); |
125 | entry.push_back(Pair("txid", txid.GetHex())); | |
072099d7 | 126 | entry.push_back(Pair("overwintered", tx.fOverwintered)); |
f7cfb52d | 127 | entry.push_back(Pair("version", tx.nVersion)); |
072099d7 S |
128 | if (tx.fOverwintered) { |
129 | entry.push_back(Pair("versiongroupid", HexInt(tx.nVersionGroupId))); | |
130 | } | |
f7cfb52d | 131 | entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); |
072099d7 S |
132 | if (tx.fOverwintered) { |
133 | entry.push_back(Pair("expiryheight", (int64_t)tx.nExpiryHeight)); | |
134 | } | |
38fc4b70 | 135 | UniValue vin(UniValue::VARR); |
f7cfb52d | 136 | BOOST_FOREACH(const CTxIn& txin, tx.vin) { |
38fc4b70 | 137 | UniValue in(UniValue::VOBJ); |
f7cfb52d S |
138 | if (tx.IsCoinBase()) |
139 | in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); | |
89cfc427 SS |
140 | else if (tx.IsCoinImport()) { |
141 | in.push_back(Pair("is_import", "1")); | |
142 | } | |
f7cfb52d S |
143 | else { |
144 | in.push_back(Pair("txid", txin.prevout.hash.GetHex())); | |
145 | in.push_back(Pair("vout", (int64_t)txin.prevout.n)); | |
1d03bae2 | 146 | { |
147 | uint256 hash; CTransaction tx; CTxDestination address; | |
148 | if (GetTransaction(txin.prevout.hash,tx,hash,false)) | |
149 | { | |
c1ec8014 | 150 | if (ExtractDestination(tx.vout[txin.prevout.n].scriptPubKey, address)) |
151 | in.push_back(Pair("address", CBitcoinAddress(address).ToString())); | |
1d03bae2 | 152 | } |
153 | } | |
38fc4b70 | 154 | UniValue o(UniValue::VOBJ); |
f7cfb52d S |
155 | o.push_back(Pair("asm", txin.scriptSig.ToString())); |
156 | o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); | |
157 | in.push_back(Pair("scriptSig", o)); | |
8b78a819 T |
158 | |
159 | // Add address and value info if spentindex enabled | |
160 | CSpentIndexValue spentInfo; | |
161 | CSpentIndexKey spentKey(txin.prevout.hash, txin.prevout.n); | |
162 | if (GetSpentIndex(spentKey, spentInfo)) { | |
163 | in.push_back(Pair("value", ValueFromAmount(spentInfo.satoshis))); | |
164 | in.push_back(Pair("valueSat", spentInfo.satoshis)); | |
165 | if (spentInfo.addressType == 1) { | |
166 | in.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString())); | |
fa7bf712 | 167 | } |
168 | else if (spentInfo.addressType == 2) { | |
8b78a819 T |
169 | in.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString())); |
170 | } | |
171 | } | |
f7cfb52d S |
172 | } |
173 | in.push_back(Pair("sequence", (int64_t)txin.nSequence)); | |
174 | vin.push_back(in); | |
175 | } | |
176 | entry.push_back(Pair("vin", vin)); | |
e42867d1 | 177 | BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock()); |
0fec0cc4 | 178 | CBlockIndex *tipindex,*pindex = it->second; |
cad0d1ca | 179 | uint64_t interest; |
e251f1ed | 180 | UniValue vout(UniValue::VARR); |
c465612c | 181 | for (unsigned int i = 0; i < tx.vout.size(); i++) |
182 | { | |
f7cfb52d | 183 | const CTxOut& txout = tx.vout[i]; |
38fc4b70 | 184 | UniValue out(UniValue::VOBJ); |
f7cfb52d | 185 | out.push_back(Pair("value", ValueFromAmount(txout.nValue))); |
c465612c | 186 | if ( ASSETCHAINS_SYMBOL[0] == 0 && pindex != 0 && tx.nLockTime >= 500000000 && (tipindex= chainActive.LastTip()) != 0 ) |
e42867d1 | 187 | { |
ae3f3fea | 188 | int64_t interest; int32_t txheight; uint32_t locktime; |
316a563c | 189 | interest = komodo_accrued_interest(&txheight,&locktime,tx.GetHash(),i,0,txout.nValue,(int32_t)tipindex->nHeight); |
e42867d1 | 190 | out.push_back(Pair("interest", ValueFromAmount(interest))); |
191 | } | |
c66c731a | 192 | out.push_back(Pair("valueZat", txout.nValue)); |
dbc80050 | 193 | out.push_back(Pair("valueSat", txout.nValue)); // [+] Decker |
f7cfb52d | 194 | out.push_back(Pair("n", (int64_t)i)); |
38fc4b70 | 195 | UniValue o(UniValue::VOBJ); |
f7cfb52d S |
196 | ScriptPubKeyToJSON(txout.scriptPubKey, o, true); |
197 | out.push_back(Pair("scriptPubKey", o)); | |
8b78a819 T |
198 | |
199 | // Add spent information if spentindex is enabled | |
200 | CSpentIndexValue spentInfo; | |
201 | CSpentIndexKey spentKey(txid, i); | |
202 | if (GetSpentIndex(spentKey, spentInfo)) { | |
203 | out.push_back(Pair("spentTxId", spentInfo.txid.GetHex())); | |
204 | out.push_back(Pair("spentIndex", (int)spentInfo.inputIndex)); | |
205 | out.push_back(Pair("spentHeight", spentInfo.blockHeight)); | |
206 | } | |
207 | ||
f7cfb52d S |
208 | vout.push_back(out); |
209 | } | |
210 | entry.push_back(Pair("vout", vout)); | |
63ea9b75 | 211 | |
0d37ae3a | 212 | UniValue vjoinsplit = TxJoinSplitToJSON(tx); |
8675d94b | 213 | entry.push_back(Pair("vjoinsplit", vjoinsplit)); |
63ea9b75 | 214 | |
8b78a819 T |
215 | if (!hashBlock.IsNull()) { |
216 | entry.push_back(Pair("blockhash", hashBlock.GetHex())); | |
217 | ||
218 | if (nConfirmations > 0) { | |
219 | entry.push_back(Pair("height", nHeight)); | |
220 | entry.push_back(Pair("confirmations", nConfirmations)); | |
221 | entry.push_back(Pair("time", nBlockTime)); | |
222 | entry.push_back(Pair("blocktime", nBlockTime)); | |
223 | } else { | |
224 | entry.push_back(Pair("height", -1)); | |
225 | entry.push_back(Pair("confirmations", 0)); | |
226 | } | |
227 | } | |
228 | ||
229 | } | |
230 | ||
231 | void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry) | |
232 | { | |
8b78a819 T |
233 | uint256 txid = tx.GetHash(); |
234 | entry.push_back(Pair("txid", txid.GetHex())); | |
235 | entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION))); | |
236 | entry.push_back(Pair("version", tx.nVersion)); | |
237 | entry.push_back(Pair("locktime", (int64_t)tx.nLockTime)); | |
238 | ||
239 | UniValue vin(UniValue::VARR); | |
240 | BOOST_FOREACH(const CTxIn& txin, tx.vin) { | |
241 | UniValue in(UniValue::VOBJ); | |
242 | if (tx.IsCoinBase()) | |
243 | in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); | |
244 | else { | |
245 | in.push_back(Pair("txid", txin.prevout.hash.GetHex())); | |
246 | in.push_back(Pair("vout", (int64_t)txin.prevout.n)); | |
247 | UniValue o(UniValue::VOBJ); | |
248 | o.push_back(Pair("asm", txin.scriptSig.ToString())); | |
249 | o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); | |
250 | in.push_back(Pair("scriptSig", o)); | |
251 | } | |
252 | in.push_back(Pair("sequence", (int64_t)txin.nSequence)); | |
253 | vin.push_back(in); | |
254 | } | |
255 | entry.push_back(Pair("vin", vin)); | |
256 | ||
257 | UniValue vout(UniValue::VARR); | |
258 | for (unsigned int i = 0; i < tx.vout.size(); i++) { | |
259 | const CTxOut& txout = tx.vout[i]; | |
260 | UniValue out(UniValue::VOBJ); | |
261 | out.push_back(Pair("value", ValueFromAmount(txout.nValue))); | |
262 | out.push_back(Pair("valueSat", txout.nValue)); | |
263 | out.push_back(Pair("n", (int64_t)i)); | |
264 | UniValue o(UniValue::VOBJ); | |
265 | ScriptPubKeyToJSON(txout.scriptPubKey, o, true); | |
266 | out.push_back(Pair("scriptPubKey", o)); | |
267 | vout.push_back(out); | |
268 | } | |
269 | entry.push_back(Pair("vout", vout)); | |
270 | ||
4f152496 | 271 | if (!hashBlock.IsNull()) { |
a2709fad | 272 | entry.push_back(Pair("blockhash", hashBlock.GetHex())); |
145d5be8 | 273 | BlockMap::iterator mi = mapBlockIndex.find(hashBlock); |
3ce7e669 | 274 | if (mi != mapBlockIndex.end() && (*mi).second) { |
a2709fad | 275 | CBlockIndex* pindex = (*mi).second; |
3ce7e669 | 276 | if (chainActive.Contains(pindex)) { |
8b78a819 | 277 | entry.push_back(Pair("height", pindex->nHeight)); |
4c6d41b8 | 278 | entry.push_back(Pair("confirmations", 1 + chainActive.Height() - pindex->nHeight)); |
209377a7 | 279 | entry.push_back(Pair("time", pindex->GetBlockTime())); |
280 | entry.push_back(Pair("blocktime", pindex->GetBlockTime())); | |
8b78a819 T |
281 | } else { |
282 | entry.push_back(Pair("height", -1)); | |
a2709fad | 283 | entry.push_back(Pair("confirmations", 0)); |
8b78a819 | 284 | } |
a2709fad GA |
285 | } |
286 | } | |
287 | } | |
288 | ||
d014114d | 289 | UniValue getrawtransaction(const UniValue& params, bool fHelp) |
a2709fad GA |
290 | { |
291 | if (fHelp || params.size() < 1 || params.size() > 2) | |
292 | throw runtime_error( | |
a6099ef3 | 293 | "getrawtransaction \"txid\" ( verbose )\n" |
3e61eb9c CL |
294 | "\nNOTE: By default this function only works sometimes. This is when the tx is in the mempool\n" |
295 | "or there is an unspent output in the utxo for this transaction. To make it always work,\n" | |
296 | "you need to maintain a transaction index, using the -txindex command line option.\n" | |
a6099ef3 | 297 | "\nReturn the raw transaction data.\n" |
298 | "\nIf verbose=0, returns a string that is serialized, hex-encoded data for 'txid'.\n" | |
299 | "If verbose is non-zero, returns an Object with information about 'txid'.\n" | |
300 | ||
301 | "\nArguments:\n" | |
302 | "1. \"txid\" (string, required) The transaction id\n" | |
303 | "2. verbose (numeric, optional, default=0) If 0, return a string, other return a json object\n" | |
304 | ||
305 | "\nResult (if verbose is not set or set to 0):\n" | |
306 | "\"data\" (string) The serialized, hex-encoded data for 'txid'\n" | |
307 | ||
308 | "\nResult (if verbose > 0):\n" | |
309 | "{\n" | |
310 | " \"hex\" : \"data\", (string) The serialized, hex-encoded data for 'txid'\n" | |
311 | " \"txid\" : \"id\", (string) The transaction id (same as provided)\n" | |
312 | " \"version\" : n, (numeric) The version\n" | |
313 | " \"locktime\" : ttt, (numeric) The lock time\n" | |
9bb37bf0 | 314 | " \"expiryheight\" : ttt, (numeric, optional) The block height after which the transaction expires\n" |
a6099ef3 | 315 | " \"vin\" : [ (array of json objects)\n" |
316 | " {\n" | |
317 | " \"txid\": \"id\", (string) The transaction id\n" | |
318 | " \"vout\": n, (numeric) \n" | |
319 | " \"scriptSig\": { (json object) The script\n" | |
320 | " \"asm\": \"asm\", (string) asm\n" | |
321 | " \"hex\": \"hex\" (string) hex\n" | |
322 | " },\n" | |
323 | " \"sequence\": n (numeric) The script sequence number\n" | |
324 | " }\n" | |
325 | " ,...\n" | |
326 | " ],\n" | |
327 | " \"vout\" : [ (array of json objects)\n" | |
328 | " {\n" | |
091b2116 | 329 | " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" |
a6099ef3 | 330 | " \"n\" : n, (numeric) index\n" |
331 | " \"scriptPubKey\" : { (json object)\n" | |
332 | " \"asm\" : \"asm\", (string) the asm\n" | |
333 | " \"hex\" : \"hex\", (string) the hex\n" | |
334 | " \"reqSigs\" : n, (numeric) The required sigs\n" | |
335 | " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" | |
336 | " \"addresses\" : [ (json array of string)\n" | |
a7322d77 | 337 | " \"komodoaddress\" (string) Komodo address\n" |
a6099ef3 | 338 | " ,...\n" |
339 | " ]\n" | |
340 | " }\n" | |
341 | " }\n" | |
342 | " ,...\n" | |
343 | " ],\n" | |
6d0ab3ee JG |
344 | " \"vjoinsplit\" : [ (array of json objects, only for version >= 2)\n" |
345 | " {\n" | |
f37f614e JDL |
346 | " \"vpub_old\" : x.xxx, (numeric) public input value in KMD\n" |
347 | " \"vpub_new\" : x.xxx, (numeric) public output value in KMD\n" | |
6d0ab3ee JG |
348 | " \"anchor\" : \"hex\", (string) the anchor\n" |
349 | " \"nullifiers\" : [ (json array of string)\n" | |
350 | " \"hex\" (string) input note nullifier\n" | |
351 | " ,...\n" | |
352 | " ],\n" | |
353 | " \"commitments\" : [ (json array of string)\n" | |
354 | " \"hex\" (string) output note commitment\n" | |
355 | " ,...\n" | |
356 | " ],\n" | |
357 | " \"onetimePubKey\" : \"hex\", (string) the onetime public key used to encrypt the ciphertexts\n" | |
358 | " \"randomSeed\" : \"hex\", (string) the random seed\n" | |
359 | " \"macs\" : [ (json array of string)\n" | |
360 | " \"hex\" (string) input note MAC\n" | |
361 | " ,...\n" | |
362 | " ],\n" | |
363 | " \"proof\" : \"hex\", (string) the zero-knowledge proof\n" | |
364 | " \"ciphertexts\" : [ (json array of string)\n" | |
365 | " \"hex\" (string) output note ciphertext\n" | |
366 | " ,...\n" | |
367 | " ]\n" | |
368 | " }\n" | |
369 | " ,...\n" | |
370 | " ],\n" | |
a6099ef3 | 371 | " \"blockhash\" : \"hash\", (string) the block hash\n" |
372 | " \"confirmations\" : n, (numeric) The confirmations\n" | |
373 | " \"time\" : ttt, (numeric) The transaction time in seconds since epoch (Jan 1 1970 GMT)\n" | |
374 | " \"blocktime\" : ttt (numeric) The block time in seconds since epoch (Jan 1 1970 GMT)\n" | |
375 | "}\n" | |
376 | ||
377 | "\nExamples:\n" | |
378 | + HelpExampleCli("getrawtransaction", "\"mytxid\"") | |
379 | + HelpExampleCli("getrawtransaction", "\"mytxid\" 1") | |
380 | + HelpExampleRpc("getrawtransaction", "\"mytxid\", 1") | |
381 | ); | |
a2709fad | 382 | |
4401b2d7 | 383 | |
2d43f88e | 384 | uint256 hash = ParseHashV(params[0], "parameter 1"); |
a2709fad GA |
385 | |
386 | bool fVerbose = false; | |
387 | if (params.size() > 1) | |
388 | fVerbose = (params[1].get_int() != 0); | |
389 | ||
390 | CTransaction tx; | |
4f152496 | 391 | uint256 hashBlock; |
8b78a819 T |
392 | int nHeight = 0; |
393 | int nConfirmations = 0; | |
394 | int nBlockTime = 0; | |
395 | ||
396 | { | |
397 | LOCK(cs_main); | |
398 | if (!GetTransaction(hash, tx, hashBlock, true)) | |
399 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction"); | |
400 | ||
401 | BlockMap::iterator mi = mapBlockIndex.find(hashBlock); | |
402 | if (mi != mapBlockIndex.end() && (*mi).second) { | |
403 | CBlockIndex* pindex = (*mi).second; | |
404 | if (chainActive.Contains(pindex)) { | |
405 | nHeight = pindex->nHeight; | |
406 | nConfirmations = 1 + chainActive.Height() - pindex->nHeight; | |
407 | nBlockTime = pindex->GetBlockTime(); | |
408 | } else { | |
409 | nHeight = -1; | |
410 | nConfirmations = 0; | |
411 | nBlockTime = pindex->GetBlockTime(); | |
412 | } | |
413 | } | |
414 | } | |
a2709fad | 415 | |
ae775b5b | 416 | string strHex = EncodeHexTx(tx); |
a2709fad GA |
417 | |
418 | if (!fVerbose) | |
419 | return strHex; | |
420 | ||
38fc4b70 | 421 | UniValue result(UniValue::VOBJ); |
a2709fad | 422 | result.push_back(Pair("hex", strHex)); |
8b78a819 | 423 | TxToJSONExpanded(tx, hashBlock, result, nHeight, nConfirmations, nBlockTime); |
a2709fad GA |
424 | return result; |
425 | } | |
426 | ||
cd78b481 | 427 | int32_t gettxout_scriptPubKey(uint8_t *scriptPubKey,int32_t maxsize,uint256 txid,int32_t n) |
428 | { | |
429 | int32_t i,m; uint8_t *ptr; | |
430 | LOCK(cs_main); | |
431 | /*CCoins coins; | |
432 | for (iter=0; iter<2; iter++) | |
433 | { | |
434 | if ( iter == 0 ) | |
435 | { | |
436 | LOCK(mempool.cs); | |
437 | CCoinsViewMemPool view(pcoinsTip,mempool); | |
438 | if ( view.GetCoins(txid,coins) == 0 ) | |
439 | { | |
440 | //fprintf(stderr,"cant get view\n"); | |
441 | continue; | |
442 | } | |
443 | mempool.pruneSpent(txid, coins); // TODO: this should be done by the CCoinsViewMemPool | |
444 | } | |
445 | else if ( pcoinsTip->GetCoins(txid,coins) == 0 ) | |
446 | { | |
447 | //fprintf(stderr,"cant get pcoinsTip->GetCoins\n"); | |
448 | continue; | |
449 | } | |
450 | if ( n < 0 || (unsigned int)n >= coins.vout.size() || coins.vout[n].IsNull() ) | |
451 | { | |
452 | fprintf(stderr,"iter.%d n.%d vs voutsize.%d\n",iter,n,(int32_t)coins.vout.size()); | |
453 | continue; | |
454 | } | |
455 | ptr = (uint8_t *)coins.vout[n].scriptPubKey.data(); | |
456 | m = coins.vout[n].scriptPubKey.size(); | |
457 | for (i=0; i<maxsize&&i<m; i++) | |
458 | scriptPubKey[i] = ptr[i]; | |
459 | return(i); | |
460 | }*/ | |
461 | CTransaction tx; | |
462 | uint256 hashBlock; | |
e2d299ee | 463 | if ( GetTransaction(txid,tx,hashBlock,false) == 0 ) |
cd78b481 | 464 | return(-1); |
465 | else if ( n <= tx.vout.size() ) // vout.size() seems off by 1 | |
466 | { | |
467 | ptr = (uint8_t *)tx.vout[n].scriptPubKey.data(); | |
468 | m = tx.vout[n].scriptPubKey.size(); | |
469 | for (i=0; i<maxsize&&i<m; i++) | |
470 | scriptPubKey[i] = ptr[i]; | |
69383c9f | 471 | //fprintf(stderr,"got scriptPubKey via rawtransaction\n"); |
cd78b481 | 472 | return(i); |
473 | } | |
474 | return(-1); | |
475 | } | |
476 | ||
d014114d | 477 | UniValue gettxoutproof(const UniValue& params, bool fHelp) |
59ed61b3 MC |
478 | { |
479 | if (fHelp || (params.size() != 1 && params.size() != 2)) | |
480 | throw runtime_error( | |
481 | "gettxoutproof [\"txid\",...] ( blockhash )\n" | |
482 | "\nReturns a hex-encoded proof that \"txid\" was included in a block.\n" | |
483 | "\nNOTE: By default this function only works sometimes. This is when there is an\n" | |
484 | "unspent output in the utxo for this transaction. To make it always work,\n" | |
485 | "you need to maintain a transaction index, using the -txindex command line option or\n" | |
486 | "specify the block in which the transaction is included in manually (by blockhash).\n" | |
487 | "\nReturn the raw transaction data.\n" | |
488 | "\nArguments:\n" | |
489 | "1. \"txids\" (string) A json array of txids to filter\n" | |
490 | " [\n" | |
491 | " \"txid\" (string) A transaction hash\n" | |
492 | " ,...\n" | |
493 | " ]\n" | |
494 | "2. \"block hash\" (string, optional) If specified, looks for txid in the block with this hash\n" | |
495 | "\nResult:\n" | |
496 | "\"data\" (string) A string that is a serialized, hex-encoded data for the proof.\n" | |
497 | ); | |
498 | ||
499 | set<uint256> setTxids; | |
500 | uint256 oneTxid; | |
851f58f9 | 501 | UniValue txids = params[0].get_array(); |
cc71666a | 502 | for (size_t idx = 0; idx < txids.size(); idx++) { |
d014114d | 503 | const UniValue& txid = txids[idx]; |
59ed61b3 MC |
504 | if (txid.get_str().length() != 64 || !IsHex(txid.get_str())) |
505 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid txid ")+txid.get_str()); | |
506 | uint256 hash(uint256S(txid.get_str())); | |
507 | if (setTxids.count(hash)) | |
508 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated txid: ")+txid.get_str()); | |
509 | setTxids.insert(hash); | |
510 | oneTxid = hash; | |
511 | } | |
512 | ||
513 | LOCK(cs_main); | |
514 | ||
515 | CBlockIndex* pblockindex = NULL; | |
516 | ||
517 | uint256 hashBlock; | |
518 | if (params.size() > 1) | |
519 | { | |
520 | hashBlock = uint256S(params[1].get_str()); | |
521 | if (!mapBlockIndex.count(hashBlock)) | |
522 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found"); | |
523 | pblockindex = mapBlockIndex[hashBlock]; | |
524 | } else { | |
525 | CCoins coins; | |
526 | if (pcoinsTip->GetCoins(oneTxid, coins) && coins.nHeight > 0 && coins.nHeight <= chainActive.Height()) | |
527 | pblockindex = chainActive[coins.nHeight]; | |
528 | } | |
529 | ||
530 | if (pblockindex == NULL) | |
531 | { | |
532 | CTransaction tx; | |
533 | if (!GetTransaction(oneTxid, tx, hashBlock, false) || hashBlock.IsNull()) | |
534 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Transaction not yet in block"); | |
535 | if (!mapBlockIndex.count(hashBlock)) | |
536 | throw JSONRPCError(RPC_INTERNAL_ERROR, "Transaction index corrupt"); | |
537 | pblockindex = mapBlockIndex[hashBlock]; | |
538 | } | |
539 | ||
540 | CBlock block; | |
b8add6a4 | 541 | if(!ReadBlockFromDisk(block, pblockindex,1)) |
59ed61b3 MC |
542 | throw JSONRPCError(RPC_INTERNAL_ERROR, "Can't read block from disk"); |
543 | ||
544 | unsigned int ntxFound = 0; | |
545 | BOOST_FOREACH(const CTransaction&tx, block.vtx) | |
805344dc | 546 | if (setTxids.count(tx.GetHash())) |
59ed61b3 MC |
547 | ntxFound++; |
548 | if (ntxFound != setTxids.size()) | |
549 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "(Not all) transactions not found in specified block"); | |
550 | ||
551 | CDataStream ssMB(SER_NETWORK, PROTOCOL_VERSION); | |
552 | CMerkleBlock mb(block, setTxids); | |
553 | ssMB << mb; | |
554 | std::string strHex = HexStr(ssMB.begin(), ssMB.end()); | |
555 | return strHex; | |
556 | } | |
557 | ||
d014114d | 558 | UniValue verifytxoutproof(const UniValue& params, bool fHelp) |
59ed61b3 MC |
559 | { |
560 | if (fHelp || params.size() != 1) | |
561 | throw runtime_error( | |
562 | "verifytxoutproof \"proof\"\n" | |
563 | "\nVerifies that a proof points to a transaction in a block, returning the transaction it commits to\n" | |
564 | "and throwing an RPC error if the block is not in our best chain\n" | |
565 | "\nArguments:\n" | |
566 | "1. \"proof\" (string, required) The hex-encoded proof generated by gettxoutproof\n" | |
567 | "\nResult:\n" | |
568 | "[\"txid\"] (array, strings) The txid(s) which the proof commits to, or empty array if the proof is invalid\n" | |
569 | ); | |
570 | ||
571 | CDataStream ssMB(ParseHexV(params[0], "proof"), SER_NETWORK, PROTOCOL_VERSION); | |
572 | CMerkleBlock merkleBlock; | |
573 | ssMB >> merkleBlock; | |
574 | ||
38fc4b70 | 575 | UniValue res(UniValue::VARR); |
59ed61b3 MC |
576 | |
577 | vector<uint256> vMatch; | |
578 | if (merkleBlock.txn.ExtractMatches(vMatch) != merkleBlock.header.hashMerkleRoot) | |
579 | return res; | |
580 | ||
581 | LOCK(cs_main); | |
582 | ||
583 | if (!mapBlockIndex.count(merkleBlock.header.GetHash()) || !chainActive.Contains(mapBlockIndex[merkleBlock.header.GetHash()])) | |
584 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Block not found in chain"); | |
585 | ||
586 | BOOST_FOREACH(const uint256& hash, vMatch) | |
587 | res.push_back(hash.GetHex()); | |
588 | return res; | |
589 | } | |
590 | ||
d014114d | 591 | UniValue createrawtransaction(const UniValue& params, bool fHelp) |
a2709fad GA |
592 | { |
593 | if (fHelp || params.size() != 2) | |
594 | throw runtime_error( | |
a6099ef3 | 595 | "createrawtransaction [{\"txid\":\"id\",\"vout\":n},...] {\"address\":amount,...}\n" |
596 | "\nCreate a transaction spending the given inputs and sending to the given addresses.\n" | |
a2709fad GA |
597 | "Returns hex-encoded raw transaction.\n" |
598 | "Note that the transaction's inputs are not signed, and\n" | |
a6099ef3 | 599 | "it is not stored in the wallet or transmitted to the network.\n" |
600 | ||
601 | "\nArguments:\n" | |
602 | "1. \"transactions\" (string, required) A json array of json objects\n" | |
603 | " [\n" | |
604 | " {\n" | |
605 | " \"txid\":\"id\", (string, required) The transaction id\n" | |
606 | " \"vout\":n (numeric, required) The output number\n" | |
607 | " }\n" | |
608 | " ,...\n" | |
609 | " ]\n" | |
610 | "2. \"addresses\" (string, required) a json object with addresses as keys and amounts as values\n" | |
611 | " {\n" | |
a7322d77 | 612 | " \"address\": x.xxx (numeric, required) The key is the Komodo address, the value is the " + CURRENCY_UNIT + " amount\n" |
a6099ef3 | 613 | " ,...\n" |
614 | " }\n" | |
615 | ||
616 | "\nResult:\n" | |
617 | "\"transaction\" (string) hex string of the transaction\n" | |
618 | ||
619 | "\nExamples\n" | |
620 | + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\" \"{\\\"address\\\":0.01}\"") | |
621 | + HelpExampleRpc("createrawtransaction", "\"[{\\\"txid\\\":\\\"myid\\\",\\\"vout\\\":0}]\", \"{\\\"address\\\":0.01}\"") | |
622 | ); | |
a2709fad | 623 | |
4401b2d7 | 624 | LOCK(cs_main); |
ed21d5bd | 625 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VARR)(UniValue::VOBJ)); |
a2709fad | 626 | |
851f58f9 JS |
627 | UniValue inputs = params[0].get_array(); |
628 | UniValue sendTo = params[1].get_obj(); | |
a2709fad | 629 | |
9bb37bf0 | 630 | int nextBlockHeight = chainActive.Height() + 1; |
072099d7 | 631 | CMutableTransaction rawTx = CreateNewContextualCMutableTransaction( |
9bb37bf0 | 632 | Params().GetConsensus(), nextBlockHeight); |
a7322d77 | 633 | |
9bb37bf0 JG |
634 | if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) { |
635 | rawTx.nExpiryHeight = nextBlockHeight + expiryDelta; | |
636 | if (rawTx.nExpiryHeight >= TX_EXPIRY_HEIGHT_THRESHOLD){ | |
637 | throw JSONRPCError(RPC_INVALID_PARAMETER, "nExpiryHeight must be less than TX_EXPIRY_HEIGHT_THRESHOLD."); | |
638 | } | |
639 | } | |
a2709fad | 640 | |
cc71666a | 641 | for (size_t idx = 0; idx < inputs.size(); idx++) { |
d014114d JS |
642 | const UniValue& input = inputs[idx]; |
643 | const UniValue& o = input.get_obj(); | |
a2709fad | 644 | |
2d43f88e | 645 | uint256 txid = ParseHashO(o, "txid"); |
a2709fad | 646 | |
d014114d | 647 | const UniValue& vout_v = find_value(o, "vout"); |
ed21d5bd | 648 | if (!vout_v.isNum()) |
738835d7 | 649 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key"); |
a2709fad GA |
650 | int nOutput = vout_v.get_int(); |
651 | if (nOutput < 0) | |
738835d7 | 652 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive"); |
a2709fad | 653 | |
2d43f88e | 654 | CTxIn in(COutPoint(txid, nOutput)); |
a2709fad GA |
655 | rawTx.vin.push_back(in); |
656 | } | |
657 | ||
658 | set<CBitcoinAddress> setAddress; | |
ed21d5bd JG |
659 | vector<string> addrList = sendTo.getKeys(); |
660 | BOOST_FOREACH(const string& name_, addrList) { | |
661 | CBitcoinAddress address(name_); | |
a2709fad | 662 | if (!address.IsValid()) |
a7322d77 | 663 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Komodo address: ")+name_); |
a2709fad GA |
664 | |
665 | if (setAddress.count(address)) | |
ed21d5bd | 666 | throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+name_); |
a2709fad GA |
667 | setAddress.insert(address); |
668 | ||
0be990ba | 669 | CScript scriptPubKey = GetScriptForDestination(address.Get()); |
ed21d5bd | 670 | CAmount nAmount = AmountFromValue(sendTo[name_]); |
a2709fad GA |
671 | |
672 | CTxOut out(nAmount, scriptPubKey); | |
673 | rawTx.vout.push_back(out); | |
674 | } | |
675 | ||
ae775b5b | 676 | return EncodeHexTx(rawTx); |
a2709fad GA |
677 | } |
678 | ||
d014114d | 679 | UniValue decoderawtransaction(const UniValue& params, bool fHelp) |
a2709fad GA |
680 | { |
681 | if (fHelp || params.size() != 1) | |
682 | throw runtime_error( | |
a6099ef3 | 683 | "decoderawtransaction \"hexstring\"\n" |
684 | "\nReturn a JSON object representing the serialized, hex-encoded transaction.\n" | |
685 | ||
686 | "\nArguments:\n" | |
e0c06d2c | 687 | "1. \"hex\" (string, required) The transaction hex string\n" |
a6099ef3 | 688 | |
689 | "\nResult:\n" | |
690 | "{\n" | |
e0c06d2c | 691 | " \"txid\" : \"id\", (string) The transaction id\n" |
9bb37bf0 | 692 | " \"overwintered\" : bool (boolean) The Overwintered flag\n" |
a6099ef3 | 693 | " \"version\" : n, (numeric) The version\n" |
072099d7 | 694 | " \"versiongroupid\": \"hex\" (string, optional) The version group id (Overwintered txs)\n" |
a6099ef3 | 695 | " \"locktime\" : ttt, (numeric) The lock time\n" |
072099d7 | 696 | " \"expiryheight\" : n, (numeric, optional) Last valid block height for mining transaction (Overwintered txs)\n" |
a6099ef3 | 697 | " \"vin\" : [ (array of json objects)\n" |
698 | " {\n" | |
699 | " \"txid\": \"id\", (string) The transaction id\n" | |
700 | " \"vout\": n, (numeric) The output number\n" | |
701 | " \"scriptSig\": { (json object) The script\n" | |
702 | " \"asm\": \"asm\", (string) asm\n" | |
703 | " \"hex\": \"hex\" (string) hex\n" | |
704 | " },\n" | |
705 | " \"sequence\": n (numeric) The script sequence number\n" | |
706 | " }\n" | |
707 | " ,...\n" | |
708 | " ],\n" | |
709 | " \"vout\" : [ (array of json objects)\n" | |
710 | " {\n" | |
091b2116 | 711 | " \"value\" : x.xxx, (numeric) The value in " + CURRENCY_UNIT + "\n" |
a6099ef3 | 712 | " \"n\" : n, (numeric) index\n" |
713 | " \"scriptPubKey\" : { (json object)\n" | |
714 | " \"asm\" : \"asm\", (string) the asm\n" | |
715 | " \"hex\" : \"hex\", (string) the hex\n" | |
716 | " \"reqSigs\" : n, (numeric) The required sigs\n" | |
717 | " \"type\" : \"pubkeyhash\", (string) The type, eg 'pubkeyhash'\n" | |
718 | " \"addresses\" : [ (json array of string)\n" | |
a7322d77 | 719 | " \"RTZMZHDFSTFQst8XmX2dR4DaH87cEUs3gC\" (string) komodo address\n" |
a6099ef3 | 720 | " ,...\n" |
721 | " ]\n" | |
722 | " }\n" | |
723 | " }\n" | |
724 | " ,...\n" | |
725 | " ],\n" | |
6d0ab3ee JG |
726 | " \"vjoinsplit\" : [ (array of json objects, only for version >= 2)\n" |
727 | " {\n" | |
f37f614e JDL |
728 | " \"vpub_old\" : x.xxx, (numeric) public input value in KMD\n" |
729 | " \"vpub_new\" : x.xxx, (numeric) public output value in KMD\n" | |
6d0ab3ee JG |
730 | " \"anchor\" : \"hex\", (string) the anchor\n" |
731 | " \"nullifiers\" : [ (json array of string)\n" | |
732 | " \"hex\" (string) input note nullifier\n" | |
733 | " ,...\n" | |
734 | " ],\n" | |
735 | " \"commitments\" : [ (json array of string)\n" | |
736 | " \"hex\" (string) output note commitment\n" | |
737 | " ,...\n" | |
738 | " ],\n" | |
739 | " \"onetimePubKey\" : \"hex\", (string) the onetime public key used to encrypt the ciphertexts\n" | |
740 | " \"randomSeed\" : \"hex\", (string) the random seed\n" | |
741 | " \"macs\" : [ (json array of string)\n" | |
742 | " \"hex\" (string) input note MAC\n" | |
743 | " ,...\n" | |
744 | " ],\n" | |
745 | " \"proof\" : \"hex\", (string) the zero-knowledge proof\n" | |
746 | " \"ciphertexts\" : [ (json array of string)\n" | |
747 | " \"hex\" (string) output note ciphertext\n" | |
748 | " ,...\n" | |
749 | " ]\n" | |
750 | " }\n" | |
751 | " ,...\n" | |
752 | " ],\n" | |
a6099ef3 | 753 | "}\n" |
754 | ||
755 | "\nExamples:\n" | |
756 | + HelpExampleCli("decoderawtransaction", "\"hexstring\"") | |
757 | + HelpExampleRpc("decoderawtransaction", "\"hexstring\"") | |
758 | ); | |
a2709fad | 759 | |
4401b2d7 | 760 | LOCK(cs_main); |
ed21d5bd | 761 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); |
ae775b5b | 762 | |
a2709fad | 763 | CTransaction tx; |
ae775b5b JG |
764 | |
765 | if (!DecodeHexTx(tx, params[0].get_str())) | |
738835d7 | 766 | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); |
a2709fad | 767 | |
9756b7bd | 768 | UniValue result(UniValue::VOBJ); |
4f152496 | 769 | TxToJSON(tx, uint256(), result); |
a2709fad GA |
770 | |
771 | return result; | |
772 | } | |
773 | ||
d014114d | 774 | UniValue decodescript(const UniValue& params, bool fHelp) |
be066fad PT |
775 | { |
776 | if (fHelp || params.size() != 1) | |
777 | throw runtime_error( | |
a6099ef3 | 778 | "decodescript \"hex\"\n" |
779 | "\nDecode a hex-encoded script.\n" | |
780 | "\nArguments:\n" | |
781 | "1. \"hex\" (string) the hex encoded script\n" | |
782 | "\nResult:\n" | |
783 | "{\n" | |
784 | " \"asm\":\"asm\", (string) Script public key\n" | |
785 | " \"hex\":\"hex\", (string) hex encoded public key\n" | |
786 | " \"type\":\"type\", (string) The output type\n" | |
787 | " \"reqSigs\": n, (numeric) The required signatures\n" | |
788 | " \"addresses\": [ (json array of string)\n" | |
a7322d77 | 789 | " \"address\" (string) Komodo address\n" |
a6099ef3 | 790 | " ,...\n" |
791 | " ],\n" | |
792 | " \"p2sh\",\"address\" (string) script address\n" | |
793 | "}\n" | |
794 | "\nExamples:\n" | |
795 | + HelpExampleCli("decodescript", "\"hexstring\"") | |
796 | + HelpExampleRpc("decodescript", "\"hexstring\"") | |
797 | ); | |
be066fad | 798 | |
4401b2d7 | 799 | LOCK(cs_main); |
9756b7bd | 800 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)); |
be066fad | 801 | |
38fc4b70 | 802 | UniValue r(UniValue::VOBJ); |
be066fad PT |
803 | CScript script; |
804 | if (params[0].get_str().size() > 0){ | |
805 | vector<unsigned char> scriptData(ParseHexV(params[0], "argument")); | |
806 | script = CScript(scriptData.begin(), scriptData.end()); | |
807 | } else { | |
808 | // Empty scripts are valid | |
809 | } | |
810 | ScriptPubKeyToJSON(script, r, false); | |
811 | ||
066e2a14 | 812 | r.push_back(Pair("p2sh", CBitcoinAddress(CScriptID(script)).ToString())); |
be066fad PT |
813 | return r; |
814 | } | |
815 | ||
8ac2a4e1 | 816 | /** Pushes a JSON object for script verification or signing errors to vErrorsRet. */ |
d014114d | 817 | static void TxInErrorToJSON(const CTxIn& txin, UniValue& vErrorsRet, const std::string& strMessage) |
8ac2a4e1 | 818 | { |
38fc4b70 | 819 | UniValue entry(UniValue::VOBJ); |
8ac2a4e1 | 820 | entry.push_back(Pair("txid", txin.prevout.hash.ToString())); |
821 | entry.push_back(Pair("vout", (uint64_t)txin.prevout.n)); | |
822 | entry.push_back(Pair("scriptSig", HexStr(txin.scriptSig.begin(), txin.scriptSig.end()))); | |
823 | entry.push_back(Pair("sequence", (uint64_t)txin.nSequence)); | |
824 | entry.push_back(Pair("error", strMessage)); | |
825 | vErrorsRet.push_back(entry); | |
826 | } | |
827 | ||
d014114d | 828 | UniValue signrawtransaction(const UniValue& params, bool fHelp) |
a2709fad | 829 | { |
3c3666d6 | 830 | if (fHelp || params.size() < 1 || params.size() > 4) |
a2709fad | 831 | throw runtime_error( |
a6099ef3 | 832 | "signrawtransaction \"hexstring\" ( [{\"txid\":\"id\",\"vout\":n,\"scriptPubKey\":\"hex\",\"redeemScript\":\"hex\"},...] [\"privatekey1\",...] sighashtype )\n" |
833 | "\nSign inputs for raw transaction (serialized, hex-encoded).\n" | |
834 | "The second optional argument (may be null) is an array of previous transaction outputs that\n" | |
729b1806 | 835 | "this transaction depends on but may not yet be in the block chain.\n" |
a6099ef3 | 836 | "The third optional argument (may be null) is an array of base58-encoded private\n" |
a2709fad | 837 | "keys that, if given, will be the only keys used to sign the transaction.\n" |
48ba56cd | 838 | #ifdef ENABLE_WALLET |
a6099ef3 | 839 | + HelpRequiringPassphrase() + "\n" |
48ba56cd | 840 | #endif |
a6099ef3 | 841 | |
842 | "\nArguments:\n" | |
843 | "1. \"hexstring\" (string, required) The transaction hex string\n" | |
844 | "2. \"prevtxs\" (string, optional) An json array of previous dependent transaction outputs\n" | |
041f71bb | 845 | " [ (json array of json objects, or 'null' if none provided)\n" |
a6099ef3 | 846 | " {\n" |
847 | " \"txid\":\"id\", (string, required) The transaction id\n" | |
848 | " \"vout\":n, (numeric, required) The output number\n" | |
849 | " \"scriptPubKey\": \"hex\", (string, required) script key\n" | |
157a5d0d PW |
850 | " \"redeemScript\": \"hex\", (string, required for P2SH) redeem script\n" |
851 | " \"amount\": value (numeric, required) The amount spent\n" | |
a6099ef3 | 852 | " }\n" |
853 | " ,...\n" | |
854 | " ]\n" | |
855 | "3. \"privatekeys\" (string, optional) A json array of base58-encoded private keys for signing\n" | |
041f71bb | 856 | " [ (json array of strings, or 'null' if none provided)\n" |
a6099ef3 | 857 | " \"privatekey\" (string) private key in base58-encoding\n" |
858 | " ,...\n" | |
859 | " ]\n" | |
ab643811 | 860 | "4. \"sighashtype\" (string, optional, default=ALL) The signature hash type. Must be one of\n" |
a6099ef3 | 861 | " \"ALL\"\n" |
862 | " \"NONE\"\n" | |
863 | " \"SINGLE\"\n" | |
864 | " \"ALL|ANYONECANPAY\"\n" | |
865 | " \"NONE|ANYONECANPAY\"\n" | |
866 | " \"SINGLE|ANYONECANPAY\"\n" | |
867 | ||
868 | "\nResult:\n" | |
869 | "{\n" | |
8ac2a4e1 | 870 | " \"hex\" : \"value\", (string) The hex-encoded raw transaction with signature(s)\n" |
871 | " \"complete\" : true|false, (boolean) If the transaction has a complete set of signatures\n" | |
872 | " \"errors\" : [ (json array of objects) Script verification errors (if there are any)\n" | |
873 | " {\n" | |
874 | " \"txid\" : \"hash\", (string) The hash of the referenced, previous transaction\n" | |
875 | " \"vout\" : n, (numeric) The index of the output to spent and used as input\n" | |
876 | " \"scriptSig\" : \"hex\", (string) The hex-encoded signature script\n" | |
877 | " \"sequence\" : n, (numeric) Script sequence number\n" | |
878 | " \"error\" : \"text\" (string) Verification or signing error related to the input\n" | |
879 | " }\n" | |
880 | " ,...\n" | |
881 | " ]\n" | |
a6099ef3 | 882 | "}\n" |
883 | ||
884 | "\nExamples:\n" | |
885 | + HelpExampleCli("signrawtransaction", "\"myhex\"") | |
886 | + HelpExampleRpc("signrawtransaction", "\"myhex\"") | |
887 | ); | |
a2709fad | 888 | |
4401b2d7 EL |
889 | #ifdef ENABLE_WALLET |
890 | LOCK2(cs_main, pwalletMain ? &pwalletMain->cs_wallet : NULL); | |
891 | #else | |
892 | LOCK(cs_main); | |
893 | #endif | |
ed21d5bd | 894 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VARR)(UniValue::VARR)(UniValue::VSTR), true); |
a2709fad | 895 | |
2d43f88e | 896 | vector<unsigned char> txData(ParseHexV(params[0], "argument 1")); |
a2709fad | 897 | CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); |
4949004d | 898 | vector<CMutableTransaction> txVariants; |
3ce7e669 | 899 | while (!ssData.empty()) { |
a2709fad | 900 | try { |
4949004d | 901 | CMutableTransaction tx; |
a2709fad GA |
902 | ssData >> tx; |
903 | txVariants.push_back(tx); | |
904 | } | |
27df4123 | 905 | catch (const std::exception&) { |
738835d7 | 906 | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); |
a2709fad GA |
907 | } |
908 | } | |
909 | ||
910 | if (txVariants.empty()) | |
738835d7 | 911 | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction"); |
a2709fad GA |
912 | |
913 | // mergedTx will end up with all the signatures; it | |
914 | // starts as a clone of the rawtx: | |
4949004d | 915 | CMutableTransaction mergedTx(txVariants[0]); |
a2709fad GA |
916 | |
917 | // Fetch previous transactions (inputs): | |
450cbb09 | 918 | CCoinsView viewDummy; |
7c70438d | 919 | CCoinsViewCache view(&viewDummy); |
a2709fad | 920 | { |
450cbb09 | 921 | LOCK(mempool.cs); |
ae8bfd12 | 922 | CCoinsViewCache &viewChain = *pcoinsTip; |
7c70438d | 923 | CCoinsViewMemPool viewMempool(&viewChain, mempool); |
450cbb09 PW |
924 | view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view |
925 | ||
926 | BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) { | |
a2709fad | 927 | const uint256& prevHash = txin.prevout.hash; |
450cbb09 | 928 | CCoins coins; |
629d75fa | 929 | view.AccessCoins(prevHash); // this is certainly allowed to fail |
a2709fad | 930 | } |
450cbb09 | 931 | |
ae8bfd12 | 932 | view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long |
a2709fad GA |
933 | } |
934 | ||
03346a61 GA |
935 | bool fGivenKeys = false; |
936 | CBasicKeyStore tempKeystore; | |
ed21d5bd | 937 | if (params.size() > 2 && !params[2].isNull()) { |
03346a61 | 938 | fGivenKeys = true; |
851f58f9 | 939 | UniValue keys = params[2].get_array(); |
cc71666a | 940 | for (size_t idx = 0; idx < keys.size(); idx++) { |
851f58f9 | 941 | UniValue k = keys[idx]; |
03346a61 GA |
942 | CBitcoinSecret vchSecret; |
943 | bool fGood = vchSecret.SetString(k.get_str()); | |
944 | if (!fGood) | |
945 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key"); | |
dfa23b94 | 946 | CKey key = vchSecret.GetKey(); |
aa768f18 PJ |
947 | if (!key.IsValid()) |
948 | throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Private key outside allowed range"); | |
03346a61 GA |
949 | tempKeystore.AddKey(key); |
950 | } | |
951 | } | |
48ba56cd | 952 | #ifdef ENABLE_WALLET |
4401b2d7 | 953 | else if (pwalletMain) |
03346a61 | 954 | EnsureWalletIsUnlocked(); |
48ba56cd | 955 | #endif |
03346a61 | 956 | |
a2709fad | 957 | // Add previous txouts given in the RPC call: |
ed21d5bd | 958 | if (params.size() > 1 && !params[1].isNull()) { |
851f58f9 | 959 | UniValue prevTxs = params[1].get_array(); |
cc71666a | 960 | for (size_t idx = 0; idx < prevTxs.size(); idx++) { |
d014114d | 961 | const UniValue& p = prevTxs[idx]; |
ed21d5bd | 962 | if (!p.isObject()) |
738835d7 | 963 | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}"); |
a2709fad | 964 | |
851f58f9 | 965 | UniValue prevOut = p.get_obj(); |
a2709fad | 966 | |
ed21d5bd | 967 | RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)); |
a2709fad | 968 | |
2d43f88e | 969 | uint256 txid = ParseHashO(prevOut, "txid"); |
a2709fad GA |
970 | |
971 | int nOut = find_value(prevOut, "vout").get_int(); | |
972 | if (nOut < 0) | |
738835d7 | 973 | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); |
a2709fad | 974 | |
2d43f88e | 975 | vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey")); |
a2709fad GA |
976 | CScript scriptPubKey(pkData.begin(), pkData.end()); |
977 | ||
f28aec01 PW |
978 | { |
979 | CCoinsModifier coins = view.ModifyCoins(txid); | |
980 | if (coins->IsAvailable(nOut) && coins->vout[nOut].scriptPubKey != scriptPubKey) { | |
a2709fad | 981 | string err("Previous output scriptPubKey mismatch:\n"); |
f28aec01 | 982 | err = err + coins->vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+ |
a2709fad | 983 | scriptPubKey.ToString(); |
738835d7 | 984 | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err); |
a2709fad | 985 | } |
f28aec01 PW |
986 | if ((unsigned int)nOut >= coins->vout.size()) |
987 | coins->vout.resize(nOut+1); | |
988 | coins->vout[nOut].scriptPubKey = scriptPubKey; | |
157a5d0d PW |
989 | coins->vout[nOut].nValue = 0; |
990 | if (prevOut.exists("amount")) { | |
991 | coins->vout[nOut].nValue = AmountFromValue(find_value(prevOut, "amount")); | |
992 | } | |
a2709fad | 993 | } |
a2709fad | 994 | |
03346a61 GA |
995 | // if redeemScript given and not using the local wallet (private keys |
996 | // given), add redeemScript to the tempKeystore so it can be signed: | |
3ce7e669 | 997 | if (fGivenKeys && scriptPubKey.IsPayToScriptHash()) { |
ed21d5bd | 998 | RPCTypeCheckObj(prevOut, boost::assign::map_list_of("txid", UniValue::VSTR)("vout", UniValue::VNUM)("scriptPubKey", UniValue::VSTR)("redeemScript",UniValue::VSTR)); |
851f58f9 | 999 | UniValue v = find_value(prevOut, "redeemScript"); |
ed21d5bd | 1000 | if (!v.isNull()) { |
21c6d3ae GM |
1001 | vector<unsigned char> rsData(ParseHexV(v, "redeemScript")); |
1002 | CScript redeemScript(rsData.begin(), rsData.end()); | |
1003 | tempKeystore.AddCScript(redeemScript); | |
1004 | } | |
03346a61 | 1005 | } |
a2709fad GA |
1006 | } |
1007 | } | |
cc6dfd1f | 1008 | |
48ba56cd | 1009 | #ifdef ENABLE_WALLET |
b0730874 | 1010 | const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain); |
48ba56cd WL |
1011 | #else |
1012 | const CKeyStore& keystore = tempKeystore; | |
1013 | #endif | |
a2709fad | 1014 | |
3c3666d6 | 1015 | int nHashType = SIGHASH_ALL; |
ed21d5bd | 1016 | if (params.size() > 3 && !params[3].isNull()) { |
3c3666d6 GA |
1017 | static map<string, int> mapSigHashValues = |
1018 | boost::assign::map_list_of | |
1019 | (string("ALL"), int(SIGHASH_ALL)) | |
1020 | (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY)) | |
1021 | (string("NONE"), int(SIGHASH_NONE)) | |
1022 | (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY)) | |
1023 | (string("SINGLE"), int(SIGHASH_SINGLE)) | |
1024 | (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY)) | |
1025 | ; | |
1026 | string strHashType = params[3].get_str(); | |
1027 | if (mapSigHashValues.count(strHashType)) | |
1028 | nHashType = mapSigHashValues[strHashType]; | |
1029 | else | |
738835d7 | 1030 | throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param"); |
3c3666d6 GA |
1031 | } |
1032 | ||
d5e7b611 GA |
1033 | bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE); |
1034 | ||
be126699 JG |
1035 | // Grab the current consensus branch ID |
1036 | auto consensusBranchId = CurrentEpochBranchId(chainActive.Height() + 1, Params().GetConsensus()); | |
1037 | ||
8ac2a4e1 | 1038 | // Script verification errors |
38fc4b70 | 1039 | UniValue vErrors(UniValue::VARR); |
8ac2a4e1 | 1040 | |
ffda7e01 JN |
1041 | // Use CTransaction for the constant parts of the |
1042 | // transaction to avoid rehashing. | |
1043 | const CTransaction txConst(mergedTx); | |
a2709fad | 1044 | // Sign what we can: |
3ce7e669 | 1045 | for (unsigned int i = 0; i < mergedTx.vin.size(); i++) { |
a2709fad | 1046 | CTxIn& txin = mergedTx.vin[i]; |
629d75fa PW |
1047 | const CCoins* coins = view.AccessCoins(txin.prevout.hash); |
1048 | if (coins == NULL || !coins->IsAvailable(txin.prevout.n)) { | |
8ac2a4e1 | 1049 | TxInErrorToJSON(txin, vErrors, "Input not found or already spent"); |
a2709fad GA |
1050 | continue; |
1051 | } | |
629d75fa | 1052 | const CScript& prevPubKey = coins->vout[txin.prevout.n].scriptPubKey; |
2d42e1a9 | 1053 | const CAmount& amount = coins->vout[txin.prevout.n].nValue; |
a2709fad | 1054 | |
157a5d0d | 1055 | SignatureData sigdata; |
d5e7b611 GA |
1056 | // Only sign SIGHASH_SINGLE if there's a corresponding output: |
1057 | if (!fHashSingle || (i < mergedTx.vout.size())) | |
be126699 | 1058 | ProduceSignature(MutableTransactionSignatureCreator(&keystore, &mergedTx, i, amount, nHashType), prevPubKey, sigdata, consensusBranchId); |
a2709fad GA |
1059 | |
1060 | // ... and merge in other signatures: | |
3ce7e669 | 1061 | BOOST_FOREACH(const CMutableTransaction& txv, txVariants) { |
be126699 | 1062 | sigdata = CombineSignatures(prevPubKey, TransactionSignatureChecker(&txConst, i, amount), sigdata, DataFromTransaction(txv, i), consensusBranchId); |
a2709fad | 1063 | } |
157a5d0d PW |
1064 | |
1065 | UpdateTransaction(mergedTx, i, sigdata); | |
1066 | ||
8ac2a4e1 | 1067 | ScriptError serror = SCRIPT_ERR_OK; |
be126699 | 1068 | if (!VerifyScript(txin.scriptSig, prevPubKey, STANDARD_SCRIPT_VERIFY_FLAGS, TransactionSignatureChecker(&txConst, i, amount), consensusBranchId, &serror)) { |
8ac2a4e1 | 1069 | TxInErrorToJSON(txin, vErrors, ScriptErrorString(serror)); |
1070 | } | |
a2709fad | 1071 | } |
8ac2a4e1 | 1072 | bool fComplete = vErrors.empty(); |
a2709fad | 1073 | |
38fc4b70 | 1074 | UniValue result(UniValue::VOBJ); |
ae775b5b | 1075 | result.push_back(Pair("hex", EncodeHexTx(mergedTx))); |
a2709fad | 1076 | result.push_back(Pair("complete", fComplete)); |
8ac2a4e1 | 1077 | if (!vErrors.empty()) { |
1078 | result.push_back(Pair("errors", vErrors)); | |
1079 | } | |
a2709fad GA |
1080 | |
1081 | return result; | |
1082 | } | |
1083 | ||
d014114d | 1084 | UniValue sendrawtransaction(const UniValue& params, bool fHelp) |
a2709fad | 1085 | { |
9d14e689 | 1086 | if (fHelp || params.size() < 1 || params.size() > 2) |
a2709fad | 1087 | throw runtime_error( |
a6099ef3 | 1088 | "sendrawtransaction \"hexstring\" ( allowhighfees )\n" |
1089 | "\nSubmits raw transaction (serialized, hex-encoded) to local node and network.\n" | |
1090 | "\nAlso see createrawtransaction and signrawtransaction calls.\n" | |
1091 | "\nArguments:\n" | |
1092 | "1. \"hexstring\" (string, required) The hex string of the raw transaction)\n" | |
1093 | "2. allowhighfees (boolean, optional, default=false) Allow high fees\n" | |
1094 | "\nResult:\n" | |
1095 | "\"hex\" (string) The transaction hash in hex\n" | |
1096 | "\nExamples:\n" | |
1097 | "\nCreate a transaction\n" | |
1098 | + HelpExampleCli("createrawtransaction", "\"[{\\\"txid\\\" : \\\"mytxid\\\",\\\"vout\\\":0}]\" \"{\\\"myaddress\\\":0.01}\"") + | |
1099 | "Sign the transaction, and get back the hex\n" | |
1100 | + HelpExampleCli("signrawtransaction", "\"myhex\"") + | |
1101 | "\nSend the transaction (signed hex)\n" | |
1102 | + HelpExampleCli("sendrawtransaction", "\"signedhex\"") + | |
1103 | "\nAs a json rpc call\n" | |
1104 | + HelpExampleRpc("sendrawtransaction", "\"signedhex\"") | |
1105 | ); | |
1106 | ||
4401b2d7 | 1107 | LOCK(cs_main); |
ed21d5bd | 1108 | RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL)); |
a2709fad | 1109 | |
a2709fad | 1110 | // parse hex string from parameter |
a2709fad | 1111 | CTransaction tx; |
ae775b5b JG |
1112 | if (!DecodeHexTx(tx, params[0].get_str())) |
1113 | throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed"); | |
805344dc | 1114 | uint256 hashTx = tx.GetHash(); |
a2709fad | 1115 | |
9d14e689 GM |
1116 | bool fOverrideFees = false; |
1117 | if (params.size() > 1) | |
1118 | fOverrideFees = params[1].get_bool(); | |
1119 | ||
ae8bfd12 | 1120 | CCoinsViewCache &view = *pcoinsTip; |
629d75fa | 1121 | const CCoins* existingCoins = view.AccessCoins(hashTx); |
1d46fe33 | 1122 | bool fHaveMempool = mempool.exists(hashTx); |
629d75fa | 1123 | bool fHaveChain = existingCoins && existingCoins->nHeight < 1000000000; |
1d46fe33 WL |
1124 | if (!fHaveMempool && !fHaveChain) { |
1125 | // push to local node and sync with wallets | |
1126 | CValidationState state; | |
de8e801d PW |
1127 | bool fMissingInputs; |
1128 | if (!AcceptToMemoryPool(mempool, state, tx, false, &fMissingInputs, !fOverrideFees)) { | |
1129 | if (state.IsInvalid()) { | |
1d46fe33 | 1130 | throw JSONRPCError(RPC_TRANSACTION_REJECTED, strprintf("%i: %s", state.GetRejectCode(), state.GetRejectReason())); |
de8e801d PW |
1131 | } else { |
1132 | if (fMissingInputs) { | |
1133 | throw JSONRPCError(RPC_TRANSACTION_ERROR, "Missing inputs"); | |
1134 | } | |
1d46fe33 | 1135 | throw JSONRPCError(RPC_TRANSACTION_ERROR, state.GetRejectReason()); |
de8e801d | 1136 | } |
450cbb09 | 1137 | } |
1d46fe33 WL |
1138 | } else if (fHaveChain) { |
1139 | throw JSONRPCError(RPC_TRANSACTION_ALREADY_IN_CHAIN, "transaction already in block chain"); | |
771ffb5e | 1140 | } |
d38da59b | 1141 | RelayTransaction(tx); |
a2709fad | 1142 | |
771ffb5e | 1143 | return hashTx.GetHex(); |
a2709fad | 1144 | } |