]> Git Repo - VerusCoin.git/blob - src/rpcrawtransaction.cpp
Merge pull request #1940 from grimd34th/patch-1
[VerusCoin.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 //
22 // Utilities: convert hex-encoded Values
23 // (throws error if not hex).
24 //
25 uint256 ParseHashV(const Value& v, string strName)
26 {
27     string strHex;
28     if (v.type() == str_type)
29         strHex = v.get_str();
30     if (!IsHex(strHex)) // Note: IsHex("") is false
31         throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
32     uint256 result;
33     result.SetHex(strHex);
34     return result;
35 }
36 uint256 ParseHashO(const Object& o, string strKey)
37 {
38     return ParseHashV(find_value(o, strKey), strKey);
39 }
40 vector<unsigned char> ParseHexV(const Value& v, string strName)
41 {
42     string strHex;
43     if (v.type() == str_type)
44         strHex = v.get_str();
45     if (!IsHex(strHex))
46         throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
47     return ParseHex(strHex);
48 }
49 vector<unsigned char> ParseHexO(const Object& o, string strKey)
50 {
51     return ParseHexV(find_value(o, strKey), strKey);
52 }
53
54 void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out)
55 {
56     txnouttype type;
57     vector<CTxDestination> addresses;
58     int nRequired;
59
60     out.push_back(Pair("asm", scriptPubKey.ToString()));
61     out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
62
63     if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired))
64     {
65         out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD)));
66         return;
67     }
68
69     out.push_back(Pair("reqSigs", nRequired));
70     out.push_back(Pair("type", GetTxnOutputType(type)));
71
72     Array a;
73     BOOST_FOREACH(const CTxDestination& addr, addresses)
74         a.push_back(CBitcoinAddress(addr).ToString());
75     out.push_back(Pair("addresses", a));
76 }
77
78 void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
79 {
80     entry.push_back(Pair("txid", tx.GetHash().GetHex()));
81     entry.push_back(Pair("version", tx.nVersion));
82     entry.push_back(Pair("locktime", (boost::int64_t)tx.nLockTime));
83     Array vin;
84     BOOST_FOREACH(const CTxIn& txin, tx.vin)
85     {
86         Object in;
87         if (tx.IsCoinBase())
88             in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
89         else
90         {
91             in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
92             in.push_back(Pair("vout", (boost::int64_t)txin.prevout.n));
93             Object o;
94             o.push_back(Pair("asm", txin.scriptSig.ToString()));
95             o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
96             in.push_back(Pair("scriptSig", o));
97         }
98         in.push_back(Pair("sequence", (boost::int64_t)txin.nSequence));
99         vin.push_back(in);
100     }
101     entry.push_back(Pair("vin", vin));
102     Array vout;
103     for (unsigned int i = 0; i < tx.vout.size(); i++)
104     {
105         const CTxOut& txout = tx.vout[i];
106         Object out;
107         out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
108         out.push_back(Pair("n", (boost::int64_t)i));
109         Object o;
110         ScriptPubKeyToJSON(txout.scriptPubKey, o);
111         out.push_back(Pair("scriptPubKey", o));
112         vout.push_back(out);
113     }
114     entry.push_back(Pair("vout", vout));
115
116     if (hashBlock != 0)
117     {
118         entry.push_back(Pair("blockhash", hashBlock.GetHex()));
119         map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
120         if (mi != mapBlockIndex.end() && (*mi).second)
121         {
122             CBlockIndex* pindex = (*mi).second;
123             if (pindex->IsInMainChain())
124             {
125                 entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
126                 entry.push_back(Pair("time", (boost::int64_t)pindex->nTime));
127                 entry.push_back(Pair("blocktime", (boost::int64_t)pindex->nTime));
128             }
129             else
130                 entry.push_back(Pair("confirmations", 0));
131         }
132     }
133 }
134
135 Value getrawtransaction(const Array& params, bool fHelp)
136 {
137     if (fHelp || params.size() < 1 || params.size() > 2)
138         throw runtime_error(
139             "getrawtransaction <txid> [verbose=0]\n"
140             "If verbose=0, returns a string that is\n"
141             "serialized, hex-encoded data for <txid>.\n"
142             "If verbose is non-zero, returns an Object\n"
143             "with information about <txid>.");
144
145     uint256 hash = ParseHashV(params[0], "parameter 1");
146
147     bool fVerbose = false;
148     if (params.size() > 1)
149         fVerbose = (params[1].get_int() != 0);
150
151     CTransaction tx;
152     uint256 hashBlock = 0;
153     if (!GetTransaction(hash, tx, hashBlock, true))
154         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
155
156     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
157     ssTx << tx;
158     string strHex = HexStr(ssTx.begin(), ssTx.end());
159
160     if (!fVerbose)
161         return strHex;
162
163     Object result;
164     result.push_back(Pair("hex", strHex));
165     TxToJSON(tx, hashBlock, result);
166     return result;
167 }
168
169 Value listunspent(const Array& params, bool fHelp)
170 {
171     if (fHelp || params.size() > 3)
172         throw runtime_error(
173             "listunspent [minconf=1] [maxconf=9999999]  [\"address\",...]\n"
174             "Returns array of unspent transaction outputs\n"
175             "with between minconf and maxconf (inclusive) confirmations.\n"
176             "Optionally filtered to only include txouts paid to specified addresses.\n"
177             "Results are an array of Objects, each of which has:\n"
178             "{txid, vout, scriptPubKey, amount, confirmations}");
179
180     RPCTypeCheck(params, list_of(int_type)(int_type)(array_type));
181
182     int nMinDepth = 1;
183     if (params.size() > 0)
184         nMinDepth = params[0].get_int();
185
186     int nMaxDepth = 9999999;
187     if (params.size() > 1)
188         nMaxDepth = params[1].get_int();
189
190     set<CBitcoinAddress> setAddress;
191     if (params.size() > 2)
192     {
193         Array inputs = params[2].get_array();
194         BOOST_FOREACH(Value& input, inputs)
195         {
196             CBitcoinAddress address(input.get_str());
197             if (!address.IsValid())
198                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+input.get_str());
199             if (setAddress.count(address))
200                 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str());
201            setAddress.insert(address);
202         }
203     }
204
205     Array results;
206     vector<COutput> vecOutputs;
207     pwalletMain->AvailableCoins(vecOutputs, false);
208     BOOST_FOREACH(const COutput& out, vecOutputs)
209     {
210         if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
211             continue;
212
213         if (setAddress.size())
214         {
215             CTxDestination address;
216             if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
217                 continue;
218
219             if (!setAddress.count(address))
220                 continue;
221         }
222
223         int64 nValue = out.tx->vout[out.i].nValue;
224         const CScript& pk = out.tx->vout[out.i].scriptPubKey;
225         Object entry;
226         entry.push_back(Pair("txid", out.tx->GetHash().GetHex()));
227         entry.push_back(Pair("vout", out.i));
228         entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end())));
229         if (pk.IsPayToScriptHash())
230         {
231             CTxDestination address;
232             if (ExtractDestination(pk, address))
233             {
234                 const CScriptID& hash = boost::get<const CScriptID&>(address);
235                 CScript redeemScript;
236                 if (pwalletMain->GetCScript(hash, redeemScript))
237                     entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())));
238             }
239         }
240         entry.push_back(Pair("amount",ValueFromAmount(nValue)));
241         entry.push_back(Pair("confirmations",out.nDepth));
242         results.push_back(entry);
243     }
244
245     return results;
246 }
247
248 Value createrawtransaction(const Array& params, bool fHelp)
249 {
250     if (fHelp || params.size() != 2)
251         throw runtime_error(
252             "createrawtransaction [{\"txid\":txid,\"vout\":n},...] {address:amount,...}\n"
253             "Create a transaction spending given inputs\n"
254             "(array of objects containing transaction id and output number),\n"
255             "sending to given address(es).\n"
256             "Returns hex-encoded raw transaction.\n"
257             "Note that the transaction's inputs are not signed, and\n"
258             "it is not stored in the wallet or transmitted to the network.");
259
260     RPCTypeCheck(params, list_of(array_type)(obj_type));
261
262     Array inputs = params[0].get_array();
263     Object sendTo = params[1].get_obj();
264
265     CTransaction rawTx;
266
267     BOOST_FOREACH(const Value& input, inputs)
268     {
269         const Object& o = input.get_obj();
270
271         uint256 txid = ParseHashO(o, "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(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     vector<unsigned char> txData(ParseHexV(params[0], "argument"));
316     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
317     CTransaction tx;
318     try {
319         ssData >> tx;
320     }
321     catch (std::exception &e) {
322         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
323     }
324
325     Object result;
326     TxToJSON(tx, 0, result);
327
328     return result;
329 }
330
331 Value signrawtransaction(const Array& params, bool fHelp)
332 {
333     if (fHelp || params.size() < 1 || params.size() > 4)
334         throw runtime_error(
335             "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex,\"redeemScript\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n"
336             "Sign inputs for raw transaction (serialized, hex-encoded).\n"
337             "Second optional argument (may be null) is an array of previous transaction outputs that\n"
338             "this transaction depends on but may not yet be in the block chain.\n"
339             "Third optional argument (may be null) is an array of base58-encoded private\n"
340             "keys that, if given, will be the only keys used to sign the transaction.\n"
341             "Fourth optional argument is a string that is one of six values; ALL, NONE, SINGLE or\n"
342             "ALL|ANYONECANPAY, NONE|ANYONECANPAY, SINGLE|ANYONECANPAY.\n"
343             "Returns json object with keys:\n"
344             "  hex : raw transaction with signature(s) (hex-encoded string)\n"
345             "  complete : 1 if transaction has a complete set of signature (0 if not)"
346             + HelpRequiringPassphrase());
347
348     RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true);
349
350     vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
351     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
352     vector<CTransaction> txVariants;
353     while (!ssData.empty())
354     {
355         try {
356             CTransaction tx;
357             ssData >> tx;
358             txVariants.push_back(tx);
359         }
360         catch (std::exception &e) {
361             throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
362         }
363     }
364
365     if (txVariants.empty())
366         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
367
368     // mergedTx will end up with all the signatures; it
369     // starts as a clone of the rawtx:
370     CTransaction mergedTx(txVariants[0]);
371     bool fComplete = true;
372
373     // Fetch previous transactions (inputs):
374     CCoinsView viewDummy;
375     CCoinsViewCache view(viewDummy);
376     {
377         LOCK(mempool.cs);
378         CCoinsViewCache &viewChain = *pcoinsTip;
379         CCoinsViewMemPool viewMempool(viewChain, mempool);
380         view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
381
382         BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) {
383             const uint256& prevHash = txin.prevout.hash;
384             CCoins coins;
385             view.GetCoins(prevHash, coins); // this is certainly allowed to fail
386         }
387
388         view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
389     }
390
391     bool fGivenKeys = false;
392     CBasicKeyStore tempKeystore;
393     if (params.size() > 2 && params[2].type() != null_type)
394     {
395         fGivenKeys = true;
396         Array keys = params[2].get_array();
397         BOOST_FOREACH(Value k, keys)
398         {
399             CBitcoinSecret vchSecret;
400             bool fGood = vchSecret.SetString(k.get_str());
401             if (!fGood)
402                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
403             CKey key;
404             bool fCompressed;
405             CSecret secret = vchSecret.GetSecret(fCompressed);
406             key.SetSecret(secret, fCompressed);
407             tempKeystore.AddKey(key);
408         }
409     }
410     else
411         EnsureWalletIsUnlocked();
412
413     // Add previous txouts given in the RPC call:
414     if (params.size() > 1 && params[1].type() != null_type)
415     {
416         Array prevTxs = params[1].get_array();
417         BOOST_FOREACH(Value& p, prevTxs)
418         {
419             if (p.type() != obj_type)
420                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
421
422             Object prevOut = p.get_obj();
423
424             RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type));
425
426             uint256 txid = ParseHashO(prevOut, "txid");
427
428             int nOut = find_value(prevOut, "vout").get_int();
429             if (nOut < 0)
430                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
431
432             vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
433             CScript scriptPubKey(pkData.begin(), pkData.end());
434
435             CCoins coins;
436             if (view.GetCoins(txid, coins)) {
437                 if (coins.IsAvailable(nOut) && coins.vout[nOut].scriptPubKey != scriptPubKey) {
438                     string err("Previous output scriptPubKey mismatch:\n");
439                     err = err + coins.vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
440                         scriptPubKey.ToString();
441                     throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
442                 }
443                 // what todo if txid is known, but the actual output isn't?
444             }
445             if ((unsigned int)nOut >= coins.vout.size())
446                 coins.vout.resize(nOut+1);
447             coins.vout[nOut].scriptPubKey = scriptPubKey;
448             coins.vout[nOut].nValue = 0; // we don't know the actual output value
449             view.SetCoins(txid, coins);
450
451             // if redeemScript given and not using the local wallet (private keys
452             // given), add redeemScript to the tempKeystore so it can be signed:
453             Value v = find_value(prevOut, "redeemScript");
454             if (fGivenKeys && scriptPubKey.IsPayToScriptHash() && !(v == Value::null))
455             {
456                 vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
457                 CScript redeemScript(rsData.begin(), rsData.end());
458                 tempKeystore.AddCScript(redeemScript);
459             }
460         }
461     }
462
463     const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);
464
465     int nHashType = SIGHASH_ALL;
466     if (params.size() > 3 && params[3].type() != null_type)
467     {
468         static map<string, int> mapSigHashValues =
469             boost::assign::map_list_of
470             (string("ALL"), int(SIGHASH_ALL))
471             (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))
472             (string("NONE"), int(SIGHASH_NONE))
473             (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
474             (string("SINGLE"), int(SIGHASH_SINGLE))
475             (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
476             ;
477         string strHashType = params[3].get_str();
478         if (mapSigHashValues.count(strHashType))
479             nHashType = mapSigHashValues[strHashType];
480         else
481             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
482     }
483
484     bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
485
486     // Sign what we can:
487     for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
488     {
489         CTxIn& txin = mergedTx.vin[i];
490         CCoins coins;
491         if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n))
492         {
493             fComplete = false;
494             continue;
495         }
496         const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey;
497
498         txin.scriptSig.clear();
499         // Only sign SIGHASH_SINGLE if there's a corresponding output:
500         if (!fHashSingle || (i < mergedTx.vout.size()))
501             SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
502
503         // ... and merge in other signatures:
504         BOOST_FOREACH(const CTransaction& txv, txVariants)
505         {
506             txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
507         }
508         if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0))
509             fComplete = false;
510     }
511
512     Object result;
513     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
514     ssTx << mergedTx;
515     result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
516     result.push_back(Pair("complete", fComplete));
517
518     return result;
519 }
520
521 Value sendrawtransaction(const Array& params, bool fHelp)
522 {
523     if (fHelp || params.size() < 1 || params.size() > 1)
524         throw runtime_error(
525             "sendrawtransaction <hex string>\n"
526             "Submits raw transaction (serialized, hex-encoded) to local node and network.");
527
528     // parse hex string from parameter
529     vector<unsigned char> txData(ParseHexV(params[0], "parameter"));
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     RelayMessage(CInv(MSG_TX, hashTx), tx);
562
563     return hashTx.GetHex();
564 }
This page took 0.056426 seconds and 4 git commands to generate.