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