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