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