]> Git Repo - VerusCoin.git/blob - src/rpcrawtransaction.cpp
Merge pull request #1906 from laanwj/2012_10_help_stdout
[VerusCoin.git] / src / rpcrawtransaction.cpp
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2012 The Bitcoin developers
3 // Distributed under the MIT/X11 software license, see the accompanying
4 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6 #include <boost/assign/list_of.hpp>
7
8 #include "base58.h"
9 #include "bitcoinrpc.h"
10 #include "db.h"
11 #include "init.h"
12 #include "main.h"
13 #include "net.h"
14 #include "wallet.h"
15
16 using namespace std;
17 using namespace boost;
18 using namespace boost::assign;
19 using namespace json_spirit;
20
21 void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out)
22 {
23     txnouttype type;
24     vector<CTxDestination> addresses;
25     int nRequired;
26
27     out.push_back(Pair("asm", scriptPubKey.ToString()));
28     out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
29
30     if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired))
31     {
32         out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD)));
33         return;
34     }
35
36     out.push_back(Pair("reqSigs", nRequired));
37     out.push_back(Pair("type", GetTxnOutputType(type)));
38
39     Array a;
40     BOOST_FOREACH(const CTxDestination& addr, addresses)
41         a.push_back(CBitcoinAddress(addr).ToString());
42     out.push_back(Pair("addresses", a));
43 }
44
45 void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
46 {
47     entry.push_back(Pair("txid", tx.GetHash().GetHex()));
48     entry.push_back(Pair("version", tx.nVersion));
49     entry.push_back(Pair("locktime", (boost::int64_t)tx.nLockTime));
50     Array vin;
51     BOOST_FOREACH(const CTxIn& txin, tx.vin)
52     {
53         Object in;
54         if (tx.IsCoinBase())
55             in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
56         else
57         {
58             in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
59             in.push_back(Pair("vout", (boost::int64_t)txin.prevout.n));
60             Object o;
61             o.push_back(Pair("asm", txin.scriptSig.ToString()));
62             o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
63             in.push_back(Pair("scriptSig", o));
64         }
65         in.push_back(Pair("sequence", (boost::int64_t)txin.nSequence));
66         vin.push_back(in);
67     }
68     entry.push_back(Pair("vin", vin));
69     Array vout;
70     for (unsigned int i = 0; i < tx.vout.size(); i++)
71     {
72         const CTxOut& txout = tx.vout[i];
73         Object out;
74         out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
75         out.push_back(Pair("n", (boost::int64_t)i));
76         Object o;
77         ScriptPubKeyToJSON(txout.scriptPubKey, o);
78         out.push_back(Pair("scriptPubKey", o));
79         vout.push_back(out);
80     }
81     entry.push_back(Pair("vout", vout));
82
83     if (hashBlock != 0)
84     {
85         entry.push_back(Pair("blockhash", hashBlock.GetHex()));
86         map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
87         if (mi != mapBlockIndex.end() && (*mi).second)
88         {
89             CBlockIndex* pindex = (*mi).second;
90             if (pindex->IsInMainChain())
91             {
92                 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
93                 entry.push_back(Pair("time", (boost::int64_t)pindex->nTime));
94                 entry.push_back(Pair("blocktime", (boost::int64_t)pindex->nTime));
95             }
96             else
97                 entry.push_back(Pair("confirmations", 0));
98         }
99     }
100 }
101
102 Value getrawtransaction(const Array& params, bool fHelp)
103 {
104     if (fHelp || params.size() < 1 || params.size() > 2)
105         throw runtime_error(
106             "getrawtransaction <txid> [verbose=0]\n"
107             "If verbose=0, returns a string that is\n"
108             "serialized, hex-encoded data for <txid>.\n"
109             "If verbose is non-zero, returns an Object\n"
110             "with information about <txid>.");
111
112     uint256 hash;
113     hash.SetHex(params[0].get_str());
114
115     bool fVerbose = false;
116     if (params.size() > 1)
117         fVerbose = (params[1].get_int() != 0);
118
119     CTransaction tx;
120     uint256 hashBlock = 0;
121     if (!GetTransaction(hash, tx, hashBlock))
122         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
123
124     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
125     ssTx << tx;
126     string strHex = HexStr(ssTx.begin(), ssTx.end());
127
128     if (!fVerbose)
129         return strHex;
130
131     Object result;
132     result.push_back(Pair("hex", strHex));
133     TxToJSON(tx, hashBlock, result);
134     return result;
135 }
136
137 Value listunspent(const Array& params, bool fHelp)
138 {
139     if (fHelp || params.size() > 3)
140         throw runtime_error(
141             "listunspent [minconf=1] [maxconf=9999999]  [\"address\",...]\n"
142             "Returns array of unspent transaction outputs\n"
143             "with between minconf and maxconf (inclusive) confirmations.\n"
144             "Optionally filtered to only include txouts paid to specified addresses.\n"
145             "Results are an array of Objects, each of which has:\n"
146             "{txid, vout, scriptPubKey, amount, confirmations}");
147
148     RPCTypeCheck(params, list_of(int_type)(int_type)(array_type));
149
150     int nMinDepth = 1;
151     if (params.size() > 0)
152         nMinDepth = params[0].get_int();
153
154     int nMaxDepth = 9999999;
155     if (params.size() > 1)
156         nMaxDepth = params[1].get_int();
157
158     set<CBitcoinAddress> setAddress;
159     if (params.size() > 2)
160     {
161         Array inputs = params[2].get_array();
162         BOOST_FOREACH(Value& input, inputs)
163         {
164             CBitcoinAddress address(input.get_str());
165             if (!address.IsValid())
166                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+input.get_str());
167             if (setAddress.count(address))
168                 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str());
169            setAddress.insert(address);
170         }
171     }
172
173     Array results;
174     vector<COutput> vecOutputs;
175     pwalletMain->AvailableCoins(vecOutputs, false);
176     BOOST_FOREACH(const COutput& out, vecOutputs)
177     {
178         if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
179             continue;
180
181         if(setAddress.size())
182         {
183             CTxDestination address;
184             if(!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
185                 continue;
186
187             if (!setAddress.count(address))
188                 continue;
189         }
190
191         int64 nValue = out.tx->vout[out.i].nValue;
192         const CScript& pk = out.tx->vout[out.i].scriptPubKey;
193         Object entry;
194         entry.push_back(Pair("txid", out.tx->GetHash().GetHex()));
195         entry.push_back(Pair("vout", out.i));
196         entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end())));
197         entry.push_back(Pair("amount",ValueFromAmount(nValue)));
198         entry.push_back(Pair("confirmations",out.nDepth));
199         results.push_back(entry);
200     }
201
202     return results;
203 }
204
205 Value createrawtransaction(const Array& params, bool fHelp)
206 {
207     if (fHelp || params.size() != 2)
208         throw runtime_error(
209             "createrawtransaction [{\"txid\":txid,\"vout\":n},...] {address:amount,...}\n"
210             "Create a transaction spending given inputs\n"
211             "(array of objects containing transaction id and output number),\n"
212             "sending to given address(es).\n"
213             "Returns hex-encoded raw transaction.\n"
214             "Note that the transaction's inputs are not signed, and\n"
215             "it is not stored in the wallet or transmitted to the network.");
216
217     RPCTypeCheck(params, list_of(array_type)(obj_type));
218
219     Array inputs = params[0].get_array();
220     Object sendTo = params[1].get_obj();
221
222     CTransaction rawTx;
223
224     BOOST_FOREACH(Value& input, inputs)
225     {
226         const Object& o = input.get_obj();
227
228         const Value& txid_v = find_value(o, "txid");
229         if (txid_v.type() != str_type)
230             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key");
231         string txid = txid_v.get_str();
232         if (!IsHex(txid))
233             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");
234
235         const Value& vout_v = find_value(o, "vout");
236         if (vout_v.type() != int_type)
237             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
238         int nOutput = vout_v.get_int();
239         if (nOutput < 0)
240             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
241
242         CTxIn in(COutPoint(uint256(txid), nOutput));
243         rawTx.vin.push_back(in);
244     }
245
246     set<CBitcoinAddress> setAddress;
247     BOOST_FOREACH(const Pair& s, sendTo)
248     {
249         CBitcoinAddress address(s.name_);
250         if (!address.IsValid())
251             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_);
252
253         if (setAddress.count(address))
254             throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
255         setAddress.insert(address);
256
257         CScript scriptPubKey;
258         scriptPubKey.SetDestination(address.Get());
259         int64 nAmount = AmountFromValue(s.value_);
260
261         CTxOut out(nAmount, scriptPubKey);
262         rawTx.vout.push_back(out);
263     }
264
265     CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
266     ss << rawTx;
267     return HexStr(ss.begin(), ss.end());
268 }
269
270 Value decoderawtransaction(const Array& params, bool fHelp)
271 {
272     if (fHelp || params.size() != 1)
273         throw runtime_error(
274             "decoderawtransaction <hex string>\n"
275             "Return a JSON object representing the serialized, hex-encoded transaction.");
276
277     RPCTypeCheck(params, list_of(str_type));
278
279     vector<unsigned char> txData(ParseHex(params[0].get_str()));
280     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
281     CTransaction tx;
282     try {
283         ssData >> tx;
284     }
285     catch (std::exception &e) {
286         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
287     }
288
289     Object result;
290     TxToJSON(tx, 0, result);
291
292     return result;
293 }
294
295 Value signrawtransaction(const Array& params, bool fHelp)
296 {
297     if (fHelp || params.size() < 1 || params.size() > 4)
298         throw runtime_error(
299             "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n"
300             "Sign inputs for raw transaction (serialized, hex-encoded).\n"
301             "Second optional argument (may be null) is an array of previous transaction outputs that\n"
302             "this transaction depends on but may not yet be in the blockchain.\n"
303             "Third optional argument (may be null) is an array of base58-encoded private\n"
304             "keys that, if given, will be the only keys used to sign the transaction.\n"
305             "Fourth optional argument is a string that is one of six values; ALL, NONE, SINGLE or\n"
306             "ALL|ANYONECANPAY, NONE|ANYONECANPAY, SINGLE|ANYONECANPAY.\n"
307             "Returns json object with keys:\n"
308             "  hex : raw transaction with signature(s) (hex-encoded string)\n"
309             "  complete : 1 if transaction has a complete set of signature (0 if not)"
310             + HelpRequiringPassphrase());
311
312     RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true);
313
314     vector<unsigned char> txData(ParseHex(params[0].get_str()));
315     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
316     vector<CTransaction> txVariants;
317     while (!ssData.empty())
318     {
319         try {
320             CTransaction tx;
321             ssData >> tx;
322             txVariants.push_back(tx);
323         }
324         catch (std::exception &e) {
325             throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
326         }
327     }
328
329     if (txVariants.empty())
330         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
331
332     // mergedTx will end up with all the signatures; it
333     // starts as a clone of the rawtx:
334     CTransaction mergedTx(txVariants[0]);
335     bool fComplete = true;
336
337     // Fetch previous transactions (inputs):
338     map<COutPoint, CScript> mapPrevOut;
339     for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
340     {
341         CTransaction tempTx;
342         MapPrevTx mapPrevTx;
343         CTxDB txdb("r");
344         map<uint256, CTxIndex> unused;
345         bool fInvalid;
346
347         // FetchInputs aborts on failure, so we go one at a time.
348         tempTx.vin.push_back(mergedTx.vin[i]);
349         tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
350
351         // Copy results into mapPrevOut:
352         BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
353         {
354             const uint256& prevHash = txin.prevout.hash;
355             if (mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size()>txin.prevout.n)
356                 mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
357         }
358     }
359
360     // Add previous txouts given in the RPC call:
361     if (params.size() > 1 && params[1].type() != null_type)
362     {
363         Array prevTxs = params[1].get_array();
364         BOOST_FOREACH(Value& p, prevTxs)
365         {
366             if (p.type() != obj_type)
367                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
368
369             Object prevOut = p.get_obj();
370
371             RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type));
372
373             string txidHex = find_value(prevOut, "txid").get_str();
374             if (!IsHex(txidHex))
375                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "txid must be hexadecimal");
376             uint256 txid;
377             txid.SetHex(txidHex);
378
379             int nOut = find_value(prevOut, "vout").get_int();
380             if (nOut < 0)
381                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
382
383             string pkHex = find_value(prevOut, "scriptPubKey").get_str();
384             if (!IsHex(pkHex))
385                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "scriptPubKey must be hexadecimal");
386             vector<unsigned char> pkData(ParseHex(pkHex));
387             CScript scriptPubKey(pkData.begin(), pkData.end());
388
389             COutPoint outpoint(txid, nOut);
390             if (mapPrevOut.count(outpoint))
391             {
392                 // Complain if scriptPubKey doesn't match
393                 if (mapPrevOut[outpoint] != scriptPubKey)
394                 {
395                     string err("Previous output scriptPubKey mismatch:\n");
396                     err = err + mapPrevOut[outpoint].ToString() + "\nvs:\n"+
397                         scriptPubKey.ToString();
398                     throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
399                 }
400             }
401             else
402                 mapPrevOut[outpoint] = scriptPubKey;
403         }
404     }
405
406     bool fGivenKeys = false;
407     CBasicKeyStore tempKeystore;
408     if (params.size() > 2 && params[2].type() != null_type)
409     {
410         fGivenKeys = true;
411         Array keys = params[2].get_array();
412         BOOST_FOREACH(Value k, keys)
413         {
414             CBitcoinSecret vchSecret;
415             bool fGood = vchSecret.SetString(k.get_str());
416             if (!fGood)
417                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,"Invalid private key");
418             CKey key;
419             bool fCompressed;
420             CSecret secret = vchSecret.GetSecret(fCompressed);
421             key.SetSecret(secret, fCompressed);
422             tempKeystore.AddKey(key);
423         }
424     }
425     else
426         EnsureWalletIsUnlocked();
427
428     const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);
429
430     int nHashType = SIGHASH_ALL;
431     if (params.size() > 3 && params[3].type() != null_type)
432     {
433         static map<string, int> mapSigHashValues =
434             boost::assign::map_list_of
435             (string("ALL"), int(SIGHASH_ALL))
436             (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))
437             (string("NONE"), int(SIGHASH_NONE))
438             (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
439             (string("SINGLE"), int(SIGHASH_SINGLE))
440             (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
441             ;
442         string strHashType = params[3].get_str();
443         if (mapSigHashValues.count(strHashType))
444             nHashType = mapSigHashValues[strHashType];
445         else
446             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
447     }
448
449     bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
450
451     // Sign what we can:
452     for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
453     {
454         CTxIn& txin = mergedTx.vin[i];
455         if (mapPrevOut.count(txin.prevout) == 0)
456         {
457             fComplete = false;
458             continue;
459         }
460         const CScript& prevPubKey = mapPrevOut[txin.prevout];
461
462         txin.scriptSig.clear();
463         // Only sign SIGHASH_SINGLE if there's a corresponding output:
464         if (!fHashSingle || (i < mergedTx.vout.size()))
465             SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
466
467         // ... and merge in other signatures:
468         BOOST_FOREACH(const CTransaction& txv, txVariants)
469         {
470             txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
471         }
472         if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0))
473             fComplete = false;
474     }
475
476     Object result;
477     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
478     ssTx << mergedTx;
479     result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
480     result.push_back(Pair("complete", fComplete));
481
482     return result;
483 }
484
485 Value sendrawtransaction(const Array& params, bool fHelp)
486 {
487     if (fHelp || params.size() < 1 || params.size() > 1)
488         throw runtime_error(
489             "sendrawtransaction <hex string>\n"
490             "Submits raw transaction (serialized, hex-encoded) to local node and network.");
491
492     RPCTypeCheck(params, list_of(str_type));
493
494     // parse hex string from parameter
495     vector<unsigned char> txData(ParseHex(params[0].get_str()));
496     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
497     CTransaction tx;
498
499     // deserialize binary data stream
500     try {
501         ssData >> tx;
502     }
503     catch (std::exception &e) {
504         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
505     }
506     uint256 hashTx = tx.GetHash();
507
508     // See if the transaction is already in a block
509     // or in the memory pool:
510     CTransaction existingTx;
511     uint256 hashBlock = 0;
512     if (GetTransaction(hashTx, existingTx, hashBlock))
513     {
514         if (hashBlock != 0)
515             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("transaction already in block ")+hashBlock.GetHex());
516         // Not in block, but already in the memory pool; will drop
517         // through to re-relay it.
518     }
519     else
520     {
521         // push to local node
522         CTxDB txdb("r");
523         if (!tx.AcceptToMemoryPool(txdb))
524             throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
525
526         SyncWithWallets(tx, NULL, true);
527     }
528     RelayMessage(CInv(MSG_TX, hashTx), tx);
529
530     return hashTx.GetHex();
531 }
This page took 0.05804 seconds and 4 git commands to generate.