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