Extended signature format checkings.
[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 "txdb.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))
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 createrawtransaction(const Array& params, bool fHelp)
216 {
217     if (fHelp || params.size() != 2)
218         throw runtime_error(
219             "createrawtransaction [{\"txid\":txid,\"vout\":n},...] {address:amount,...}\n"
220             "Create a transaction spending given inputs\n"
221             "(array of objects containing transaction id and output number),\n"
222             "sending to given address(es).\n"
223             "Returns hex-encoded raw transaction.\n"
224             "Note that the transaction's inputs are not signed, and\n"
225             "it is not stored in the wallet or transmitted to the network.");
226
227     RPCTypeCheck(params, list_of(array_type)(obj_type));
228
229     Array inputs = params[0].get_array();
230     Object sendTo = params[1].get_obj();
231
232     CTransaction rawTx;
233
234     BOOST_FOREACH(Value& input, inputs)
235     {
236         const Object& o = input.get_obj();
237
238         const Value& txid_v = find_value(o, "txid");
239         if (txid_v.type() != str_type)
240             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key");
241         string txid = txid_v.get_str();
242         if (!IsHex(txid))
243             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");
244
245         const Value& vout_v = find_value(o, "vout");
246         if (vout_v.type() != int_type)
247             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
248         int nOutput = vout_v.get_int();
249         if (nOutput < 0)
250             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
251
252         CTxIn in(COutPoint(uint256(txid), nOutput));
253         rawTx.vin.push_back(in);
254     }
255
256     set<CBitcoinAddress> setAddress;
257     BOOST_FOREACH(const Pair& s, sendTo)
258     {
259         CBitcoinAddress address(s.name_);
260         if (!address.IsValid())
261             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid NovaCoin address: ")+s.name_);
262
263         if (setAddress.count(address))
264             throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
265         setAddress.insert(address);
266
267         CScript scriptPubKey;
268         scriptPubKey.SetDestination(address.Get());
269         int64 nAmount = AmountFromValue(s.value_);
270
271         CTxOut out(nAmount, scriptPubKey);
272         rawTx.vout.push_back(out);
273     }
274
275     CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
276     ss << rawTx;
277     return HexStr(ss.begin(), ss.end());
278 }
279
280 Value decoderawtransaction(const Array& params, bool fHelp)
281 {
282     if (fHelp || params.size() != 1)
283         throw runtime_error(
284             "decoderawtransaction <hex string>\n"
285             "Return a JSON object representing the serialized, hex-encoded transaction.");
286
287     RPCTypeCheck(params, list_of(str_type));
288
289     vector<unsigned char> txData(ParseHex(params[0].get_str()));
290     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
291     CTransaction tx;
292     try {
293         ssData >> tx;
294     }
295     catch (std::exception &e) {
296         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
297     }
298
299     Object result;
300     TxToJSON(tx, 0, result);
301
302     return result;
303 }
304
305 Value decodescript(const Array& params, bool fHelp)
306 {
307     if (fHelp || params.size() != 1)
308         throw runtime_error(
309             "decodescript <hex string>\n"
310             "Decode a hex-encoded script.");
311
312     RPCTypeCheck(params, list_of(str_type));
313
314     Object r;
315     CScript script;
316     if (params[0].get_str().size() > 0){
317         vector<unsigned char> scriptData(ParseHexV(params[0], "argument"));
318         script = CScript(scriptData.begin(), scriptData.end());
319     } else {
320         // Empty scripts are valid
321     }
322     ScriptPubKeyToJSON(script, r, false);
323
324     r.push_back(Pair("p2sh", CBitcoinAddress(script.GetID()).ToString()));
325     return r;
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     map<COutPoint, CScript> mapPrevOut;
372     for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
373     {
374         CTransaction tempTx;
375         MapPrevTx mapPrevTx;
376         CTxDB txdb("r");
377         map<uint256, CTxIndex> unused;
378         bool fInvalid;
379
380         // FetchInputs aborts on failure, so we go one at a time.
381         tempTx.vin.push_back(mergedTx.vin[i]);
382         tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
383
384         // Copy results into mapPrevOut:
385         BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
386         {
387             const uint256& prevHash = txin.prevout.hash;
388             if (mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size()>txin.prevout.n)
389                 mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
390         }
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             COutPoint outpoint(txid, nOut);
423             if (mapPrevOut.count(outpoint))
424             {
425                 // Complain if scriptPubKey doesn't match
426                 if (mapPrevOut[outpoint] != scriptPubKey)
427                 {
428                     string err("Previous output scriptPubKey mismatch:\n");
429                     err = err + mapPrevOut[outpoint].ToString() + "\nvs:\n"+
430                         scriptPubKey.ToString();
431                     throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
432                 }
433             }
434             else
435                 mapPrevOut[outpoint] = scriptPubKey;
436         }
437     }
438
439     bool fGivenKeys = false;
440     CBasicKeyStore tempKeystore;
441     if (params.size() > 2 && params[2].type() != null_type)
442     {
443         fGivenKeys = true;
444         Array keys = params[2].get_array();
445         BOOST_FOREACH(Value k, keys)
446         {
447             CBitcoinSecret vchSecret;
448             bool fGood = vchSecret.SetString(k.get_str());
449             if (!fGood)
450                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,"Invalid private key");
451             CKey key;
452             bool fCompressed;
453             CSecret secret = vchSecret.GetSecret(fCompressed);
454             key.SetSecret(secret, fCompressed);
455             tempKeystore.AddKey(key);
456         }
457     }
458     else
459         EnsureWalletIsUnlocked();
460
461     const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);
462
463     int nHashType = SIGHASH_ALL;
464     if (params.size() > 3 && params[3].type() != null_type)
465     {
466         static map<string, int> mapSigHashValues =
467             boost::assign::map_list_of
468             (string("ALL"), int(SIGHASH_ALL))
469             (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))
470             (string("NONE"), int(SIGHASH_NONE))
471             (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
472             (string("SINGLE"), int(SIGHASH_SINGLE))
473             (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
474             ;
475         string strHashType = params[3].get_str();
476         if (mapSigHashValues.count(strHashType))
477             nHashType = mapSigHashValues[strHashType];
478         else
479             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
480     }
481
482     bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
483
484     // Sign what we can:
485     for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
486     {
487         CTxIn& txin = mergedTx.vin[i];
488         if (mapPrevOut.count(txin.prevout) == 0)
489         {
490             fComplete = false;
491             continue;
492         }
493         const CScript& prevPubKey = mapPrevOut[txin.prevout];
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, STRICT_FLAGS, 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     // See if the transaction is already in a block
542     // or in the memory pool:
543     CTransaction existingTx;
544     uint256 hashBlock = 0;
545     if (GetTransaction(hashTx, existingTx, hashBlock))
546     {
547         if (hashBlock != 0)
548             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("transaction already in block ")+hashBlock.GetHex());
549         // Not in block, but already in the memory pool; will drop
550         // through to re-relay it.
551     }
552     else
553     {
554         // push to local node
555         CTxDB txdb("r");
556         if (!tx.AcceptToMemoryPool(txdb))
557             throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
558
559         SyncWithWallets(tx, NULL, true);
560     }
561     RelayTransaction(tx, hashTx);
562
563     return hashTx.GetHex();
564 }