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