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