203583e716772a0900ea82b9b0a44298d4d7f653
[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         CCoinsDB coinsdb("r");
376         CCoinsViewDB viewDB(coinsdb);
377         CCoinsViewMemPool viewMempool(viewDB, mempool);
378         view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
379
380         BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) {
381             const uint256& prevHash = txin.prevout.hash;
382             CCoins coins;
383             view.GetCoins(prevHash, coins); // this is certainly allowed to fail
384         }
385
386         view.SetBackend(viewDummy); // switch back to avoid locking db/mempool too long
387     }
388
389     // Add previous txouts given in the RPC call:
390     if (params.size() > 1 && params[1].type() != null_type)
391     {
392         Array prevTxs = params[1].get_array();
393         BOOST_FOREACH(Value& p, prevTxs)
394         {
395             if (p.type() != obj_type)
396                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
397
398             Object prevOut = p.get_obj();
399
400             RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type));
401
402             string txidHex = find_value(prevOut, "txid").get_str();
403             if (!IsHex(txidHex))
404                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "txid must be hexadecimal");
405             uint256 txid;
406             txid.SetHex(txidHex);
407
408             int nOut = find_value(prevOut, "vout").get_int();
409             if (nOut < 0)
410                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
411
412             string pkHex = find_value(prevOut, "scriptPubKey").get_str();
413             if (!IsHex(pkHex))
414                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "scriptPubKey must be hexadecimal");
415             vector<unsigned char> pkData(ParseHex(pkHex));
416             CScript scriptPubKey(pkData.begin(), pkData.end());
417
418             CCoins coins;
419             if (view.GetCoins(txid, coins)) {
420                 if (coins.IsAvailable(nOut) && coins.vout[nOut].scriptPubKey != scriptPubKey) {
421                     string err("Previous output scriptPubKey mismatch:\n");
422                     err = err + coins.vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
423                         scriptPubKey.ToString();
424                     throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
425                 }
426                 // what todo if txid is known, but the actual output isn't?
427             }
428             coins.vout[nOut].scriptPubKey = scriptPubKey;
429             coins.vout[nOut].nValue = 0; // we don't know the actual output value
430             view.SetCoins(txid, coins);
431         }
432     }
433
434     bool fGivenKeys = false;
435     CBasicKeyStore tempKeystore;
436     if (params.size() > 2 && params[2].type() != null_type)
437     {
438         fGivenKeys = true;
439         Array keys = params[2].get_array();
440         BOOST_FOREACH(Value k, keys)
441         {
442             CBitcoinSecret vchSecret;
443             bool fGood = vchSecret.SetString(k.get_str());
444             if (!fGood)
445                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,"Invalid private key");
446             CKey key;
447             bool fCompressed;
448             CSecret secret = vchSecret.GetSecret(fCompressed);
449             key.SetSecret(secret, fCompressed);
450             tempKeystore.AddKey(key);
451         }
452     }
453     else
454         EnsureWalletIsUnlocked();
455
456     const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);
457
458     int nHashType = SIGHASH_ALL;
459     if (params.size() > 3 && params[3].type() != null_type)
460     {
461         static map<string, int> mapSigHashValues =
462             boost::assign::map_list_of
463             (string("ALL"), int(SIGHASH_ALL))
464             (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))
465             (string("NONE"), int(SIGHASH_NONE))
466             (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
467             (string("SINGLE"), int(SIGHASH_SINGLE))
468             (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
469             ;
470         string strHashType = params[3].get_str();
471         if (mapSigHashValues.count(strHashType))
472             nHashType = mapSigHashValues[strHashType];
473         else
474             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
475     }
476
477     bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
478
479     // Sign what we can:
480     for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
481     {
482         CTxIn& txin = mergedTx.vin[i];
483         CCoins coins;
484         if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n))
485         {
486             fComplete = false;
487             continue;
488         }
489         const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey;
490
491         txin.scriptSig.clear();
492         // Only sign SIGHASH_SINGLE if there's a corresponding output:
493         if (!fHashSingle || (i < mergedTx.vout.size()))
494             SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
495
496         // ... and merge in other signatures:
497         BOOST_FOREACH(const CTransaction& txv, txVariants)
498         {
499             txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
500         }
501         if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, true, 0))
502             fComplete = false;
503     }
504
505     Object result;
506     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
507     ssTx << mergedTx;
508     result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
509     result.push_back(Pair("complete", fComplete));
510
511     return result;
512 }
513
514 Value sendrawtransaction(const Array& params, bool fHelp)
515 {
516     if (fHelp || params.size() < 1 || params.size() > 1)
517         throw runtime_error(
518             "sendrawtransaction <hex string>\n"
519             "Submits raw transaction (serialized, hex-encoded) to local node and network.");
520
521     RPCTypeCheck(params, list_of(str_type));
522
523     // parse hex string from parameter
524     vector<unsigned char> txData(ParseHex(params[0].get_str()));
525     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
526     CTransaction tx;
527
528     // deserialize binary data stream
529     try {
530         ssData >> tx;
531     }
532     catch (std::exception &e) {
533         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
534     }
535     uint256 hashTx = tx.GetHash();
536
537     bool fHave = false;
538     CCoins existingCoins;
539     {
540         CCoinsDB coinsdb("r");
541         {
542             CCoinsViewDB coinsviewDB(coinsdb);
543             CCoinsViewMemPool coinsview(coinsviewDB, mempool);
544             fHave = coinsview.GetCoins(hashTx, existingCoins);
545         }
546         if (!fHave) {
547             // push to local node
548             if (!tx.AcceptToMemoryPool(coinsdb))
549                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
550         }
551     }
552     if (fHave) {
553         if (existingCoins.nHeight < 1000000000)
554             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "transaction already in block chain");
555         // Not in block, but already in the memory pool; will drop
556         // through to re-relay it.
557     } else {
558         SyncWithWallets(tx, NULL, true);
559     }
560     RelayTransaction(tx, hashTx);
561
562     return hashTx.GetHex();
563 }