Address in listunspent
[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         CTxDestination address;
198         if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
199         {
200             entry.push_back(Pair("address", CBitcoinAddress(address).ToString()));
201             if (pwalletMain->mapAddressBook.count(address))
202                 entry.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
203         }
204         entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end())));
205         entry.push_back(Pair("amount",ValueFromAmount(nValue)));
206         entry.push_back(Pair("confirmations",out.nDepth));
207         results.push_back(entry);
208     }
209
210     return results;
211 }
212
213 Value createrawtransaction(const Array& params, bool fHelp)
214 {
215     if (fHelp || params.size() != 2)
216         throw runtime_error(
217             "createrawtransaction [{\"txid\":txid,\"vout\":n},...] {address:amount,...}\n"
218             "Create a transaction spending given inputs\n"
219             "(array of objects containing transaction id and output number),\n"
220             "sending to given address(es).\n"
221             "Returns hex-encoded raw transaction.\n"
222             "Note that the transaction's inputs are not signed, and\n"
223             "it is not stored in the wallet or transmitted to the network.");
224
225     RPCTypeCheck(params, list_of(array_type)(obj_type));
226
227     Array inputs = params[0].get_array();
228     Object sendTo = params[1].get_obj();
229
230     CTransaction rawTx;
231
232     BOOST_FOREACH(Value& input, inputs)
233     {
234         const Object& o = input.get_obj();
235
236         const Value& txid_v = find_value(o, "txid");
237         if (txid_v.type() != str_type)
238             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key");
239         string txid = txid_v.get_str();
240         if (!IsHex(txid))
241             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");
242
243         const Value& vout_v = find_value(o, "vout");
244         if (vout_v.type() != int_type)
245             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
246         int nOutput = vout_v.get_int();
247         if (nOutput < 0)
248             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
249
250         CTxIn in(COutPoint(uint256(txid), nOutput));
251         rawTx.vin.push_back(in);
252     }
253
254     set<CBitcoinAddress> setAddress;
255     BOOST_FOREACH(const Pair& s, sendTo)
256     {
257         CBitcoinAddress address(s.name_);
258         if (!address.IsValid())
259             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
260
261         if (setAddress.count(address))
262             throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
263         setAddress.insert(address);
264
265         CScript scriptPubKey;
266         scriptPubKey.SetDestination(address.Get());
267         int64 nAmount = AmountFromValue(s.value_);
268
269         CTxOut out(nAmount, scriptPubKey);
270         rawTx.vout.push_back(out);
271     }
272
273     CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
274     ss << rawTx;
275     return HexStr(ss.begin(), ss.end());
276 }
277
278 Value decoderawtransaction(const Array& params, bool fHelp)
279 {
280     if (fHelp || params.size() != 1)
281         throw runtime_error(
282             "decoderawtransaction <hex string>\n"
283             "Return a JSON object representing the serialized, hex-encoded transaction.");
284
285     RPCTypeCheck(params, list_of(str_type));
286
287     vector<unsigned char> txData(ParseHex(params[0].get_str()));
288     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
289     CTransaction tx;
290     try {
291         ssData >> tx;
292     }
293     catch (std::exception &e) {
294         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
295     }
296
297     Object result;
298     TxToJSON(tx, 0, result);
299
300     return result;
301 }
302
303 Value signrawtransaction(const Array& params, bool fHelp)
304 {
305     if (fHelp || params.size() < 1 || params.size() > 4)
306         throw runtime_error(
307             "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n"
308             "Sign inputs for raw transaction (serialized, hex-encoded).\n"
309             "Second optional argument (may be null) is an array of previous transaction outputs that\n"
310             "this transaction depends on but may not yet be in the blockchain.\n"
311             "Third optional argument (may be null) is an array of base58-encoded private\n"
312             "keys that, if given, will be the only keys used to sign the transaction.\n"
313             "Fourth optional argument is a string that is one of six values; ALL, NONE, SINGLE or\n"
314             "ALL|ANYONECANPAY, NONE|ANYONECANPAY, SINGLE|ANYONECANPAY.\n"
315             "Returns json object with keys:\n"
316             "  hex : raw transaction with signature(s) (hex-encoded string)\n"
317             "  complete : 1 if transaction has a complete set of signature (0 if not)"
318             + HelpRequiringPassphrase());
319
320     RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true);
321
322     vector<unsigned char> txData(ParseHex(params[0].get_str()));
323     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
324     vector<CTransaction> txVariants;
325     while (!ssData.empty())
326     {
327         try {
328             CTransaction tx;
329             ssData >> tx;
330             txVariants.push_back(tx);
331         }
332         catch (std::exception &e) {
333             throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
334         }
335     }
336
337     if (txVariants.empty())
338         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
339
340     // mergedTx will end up with all the signatures; it
341     // starts as a clone of the rawtx:
342     CTransaction mergedTx(txVariants[0]);
343     bool fComplete = true;
344
345     // Fetch previous transactions (inputs):
346     map<COutPoint, CScript> mapPrevOut;
347     for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
348     {
349         CTransaction tempTx;
350         MapPrevTx mapPrevTx;
351         CTxDB txdb("r");
352         map<uint256, CTxIndex> unused;
353         bool fInvalid;
354
355         // FetchInputs aborts on failure, so we go one at a time.
356         tempTx.vin.push_back(mergedTx.vin[i]);
357         tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
358
359         // Copy results into mapPrevOut:
360         BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
361         {
362             const uint256& prevHash = txin.prevout.hash;
363             if (mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size()>txin.prevout.n)
364                 mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
365         }
366     }
367
368     // Add previous txouts given in the RPC call:
369     if (params.size() > 1 && params[1].type() != null_type)
370     {
371         Array prevTxs = params[1].get_array();
372         BOOST_FOREACH(Value& p, prevTxs)
373         {
374             if (p.type() != obj_type)
375                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
376
377             Object prevOut = p.get_obj();
378
379             RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type));
380
381             string txidHex = find_value(prevOut, "txid").get_str();
382             if (!IsHex(txidHex))
383                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "txid must be hexadecimal");
384             uint256 txid;
385             txid.SetHex(txidHex);
386
387             int nOut = find_value(prevOut, "vout").get_int();
388             if (nOut < 0)
389                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
390
391             string pkHex = find_value(prevOut, "scriptPubKey").get_str();
392             if (!IsHex(pkHex))
393                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "scriptPubKey must be hexadecimal");
394             vector<unsigned char> pkData(ParseHex(pkHex));
395             CScript scriptPubKey(pkData.begin(), pkData.end());
396
397             COutPoint outpoint(txid, nOut);
398             if (mapPrevOut.count(outpoint))
399             {
400                 // Complain if scriptPubKey doesn't match
401                 if (mapPrevOut[outpoint] != scriptPubKey)
402                 {
403                     string err("Previous output scriptPubKey mismatch:\n");
404                     err = err + mapPrevOut[outpoint].ToString() + "\nvs:\n"+
405                         scriptPubKey.ToString();
406                     throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
407                 }
408             }
409             else
410                 mapPrevOut[outpoint] = scriptPubKey;
411         }
412     }
413
414     bool fGivenKeys = false;
415     CBasicKeyStore tempKeystore;
416     if (params.size() > 2 && params[2].type() != null_type)
417     {
418         fGivenKeys = true;
419         Array keys = params[2].get_array();
420         BOOST_FOREACH(Value k, keys)
421         {
422             CBitcoinSecret vchSecret;
423             bool fGood = vchSecret.SetString(k.get_str());
424             if (!fGood)
425                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,"Invalid private key");
426             CKey key;
427             bool fCompressed;
428             CSecret secret = vchSecret.GetSecret(fCompressed);
429             key.SetSecret(secret, fCompressed);
430             tempKeystore.AddKey(key);
431         }
432     }
433     else
434         EnsureWalletIsUnlocked();
435
436     const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);
437
438     int nHashType = SIGHASH_ALL;
439     if (params.size() > 3 && params[3].type() != null_type)
440     {
441         static map<string, int> mapSigHashValues =
442             boost::assign::map_list_of
443             (string("ALL"), int(SIGHASH_ALL))
444             (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))
445             (string("NONE"), int(SIGHASH_NONE))
446             (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
447             (string("SINGLE"), int(SIGHASH_SINGLE))
448             (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
449             ;
450         string strHashType = params[3].get_str();
451         if (mapSigHashValues.count(strHashType))
452             nHashType = mapSigHashValues[strHashType];
453         else
454             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
455     }
456
457     bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
458
459     // Sign what we can:
460     for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
461     {
462         CTxIn& txin = mergedTx.vin[i];
463         if (mapPrevOut.count(txin.prevout) == 0)
464         {
465             fComplete = false;
466             continue;
467         }
468         const CScript& prevPubKey = mapPrevOut[txin.prevout];
469
470         txin.scriptSig.clear();
471         // Only sign SIGHASH_SINGLE if there's a corresponding output:
472         if (!fHashSingle || (i < mergedTx.vout.size()))
473             SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
474
475         // ... and merge in other signatures:
476         BOOST_FOREACH(const CTransaction& txv, txVariants)
477         {
478             txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
479         }
480         if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0))
481             fComplete = false;
482     }
483
484     Object result;
485     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
486     ssTx << mergedTx;
487     result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
488     result.push_back(Pair("complete", fComplete));
489
490     return result;
491 }
492
493 Value sendrawtransaction(const Array& params, bool fHelp)
494 {
495     if (fHelp || params.size() < 1 || params.size() > 1)
496         throw runtime_error(
497             "sendrawtransaction <hex string>\n"
498             "Submits raw transaction (serialized, hex-encoded) to local node and network.");
499
500     RPCTypeCheck(params, list_of(str_type));
501
502     // parse hex string from parameter
503     vector<unsigned char> txData(ParseHex(params[0].get_str()));
504     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
505     CTransaction tx;
506
507     // deserialize binary data stream
508     try {
509         ssData >> tx;
510     }
511     catch (std::exception &e) {
512         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
513     }
514     uint256 hashTx = tx.GetHash();
515
516     // See if the transaction is already in a block
517     // or in the memory pool:
518     CTransaction existingTx;
519     uint256 hashBlock = 0;
520     if (GetTransaction(hashTx, existingTx, hashBlock))
521     {
522         if (hashBlock != 0)
523             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("transaction already in block ")+hashBlock.GetHex());
524         // Not in block, but already in the memory pool; will drop
525         // through to re-relay it.
526     }
527     else
528     {
529         // push to local node
530         CTxDB txdb("r");
531         if (!tx.AcceptToMemoryPool(txdb))
532             throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
533
534         SyncWithWallets(tx, NULL, true);
535     }
536     RelayMessage(CInv(MSG_TX, hashTx), tx);
537
538     return hashTx.GetHex();
539 }