]> Git Repo - VerusCoin.git/blob - src/rpcrawtransaction.cpp
Use boost signals for callbacks from main to wallet
[VerusCoin.git] / src / rpcrawtransaction.cpp
1 // Copyright (c) 2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2013 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 "net.h"
13 #include "wallet.h"
14
15 using namespace std;
16 using namespace boost;
17 using namespace boost::assign;
18 using namespace json_spirit;
19
20 void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex)
21 {
22     txnouttype type;
23     vector<CTxDestination> addresses;
24     int nRequired;
25
26     out.push_back(Pair("asm", scriptPubKey.ToString()));
27     if (fIncludeHex)
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, false);
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 (chainActive.Contains(pindex))
91             {
92                 entry.push_back(Pair("confirmations", 1 + chainActive.Height() - 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 = ParseHashV(params[0], "parameter 1");
113
114     bool fVerbose = false;
115     if (params.size() > 1)
116         fVerbose = (params[1].get_int() != 0);
117
118     CTransaction tx;
119     uint256 hashBlock = 0;
120     if (!GetTransaction(hash, tx, hashBlock, true))
121         throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
122
123     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
124     ssTx << tx;
125     string strHex = HexStr(ssTx.begin(), ssTx.end());
126
127     if (!fVerbose)
128         return strHex;
129
130     Object result;
131     result.push_back(Pair("hex", strHex));
132     TxToJSON(tx, hashBlock, result);
133     return result;
134 }
135
136 Value listunspent(const Array& params, bool fHelp)
137 {
138     if (fHelp || params.size() > 3)
139         throw runtime_error(
140             "listunspent [minconf=1] [maxconf=9999999]  [\"address\",...]\n"
141             "Returns array of unspent transaction outputs\n"
142             "with between minconf and maxconf (inclusive) confirmations.\n"
143             "Optionally filtered to only include txouts paid to specified addresses.\n"
144             "Results are an array of Objects, each of which has:\n"
145             "{txid, vout, scriptPubKey, amount, confirmations}");
146
147     RPCTypeCheck(params, list_of(int_type)(int_type)(array_type));
148
149     int nMinDepth = 1;
150     if (params.size() > 0)
151         nMinDepth = params[0].get_int();
152
153     int nMaxDepth = 9999999;
154     if (params.size() > 1)
155         nMaxDepth = params[1].get_int();
156
157     set<CBitcoinAddress> setAddress;
158     if (params.size() > 2)
159     {
160         Array inputs = params[2].get_array();
161         BOOST_FOREACH(Value& input, inputs)
162         {
163             CBitcoinAddress address(input.get_str());
164             if (!address.IsValid())
165                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+input.get_str());
166             if (setAddress.count(address))
167                 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str());
168            setAddress.insert(address);
169         }
170     }
171
172     Array results;
173     vector<COutput> vecOutputs;
174     assert(pwalletMain != NULL);
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         CTxDestination address;
197         if (ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
198         {
199             entry.push_back(Pair("address", CBitcoinAddress(address).ToString()));
200             if (pwalletMain->mapAddressBook.count(address))
201                 entry.push_back(Pair("account", pwalletMain->mapAddressBook[address].name));
202         }
203         entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end())));
204         if (pk.IsPayToScriptHash())
205         {
206             CTxDestination address;
207             if (ExtractDestination(pk, address))
208             {
209                 const CScriptID& hash = boost::get<const CScriptID&>(address);
210                 CScript redeemScript;
211                 if (pwalletMain->GetCScript(hash, redeemScript))
212                     entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())));
213             }
214         }
215         entry.push_back(Pair("amount",ValueFromAmount(nValue)));
216         entry.push_back(Pair("confirmations",out.nDepth));
217         results.push_back(entry);
218     }
219
220     return results;
221 }
222
223 Value createrawtransaction(const Array& params, bool fHelp)
224 {
225     if (fHelp || params.size() != 2)
226         throw runtime_error(
227             "createrawtransaction [{\"txid\":txid,\"vout\":n},...] {address:amount,...}\n"
228             "Create a transaction spending given inputs\n"
229             "(array of objects containing transaction id and output number),\n"
230             "sending to given address(es).\n"
231             "Returns hex-encoded raw transaction.\n"
232             "Note that the transaction's inputs are not signed, and\n"
233             "it is not stored in the wallet or transmitted to the network.");
234
235     RPCTypeCheck(params, list_of(array_type)(obj_type));
236
237     Array inputs = params[0].get_array();
238     Object sendTo = params[1].get_obj();
239
240     CTransaction rawTx;
241
242     BOOST_FOREACH(const Value& input, inputs)
243     {
244         const Object& o = input.get_obj();
245
246         uint256 txid = ParseHashO(o, "txid");
247
248         const Value& vout_v = find_value(o, "vout");
249         if (vout_v.type() != int_type)
250             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
251         int nOutput = vout_v.get_int();
252         if (nOutput < 0)
253             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
254
255         CTxIn in(COutPoint(txid, nOutput));
256         rawTx.vin.push_back(in);
257     }
258
259     set<CBitcoinAddress> setAddress;
260     BOOST_FOREACH(const Pair& s, sendTo)
261     {
262         CBitcoinAddress address(s.name_);
263         if (!address.IsValid())
264             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_);
265
266         if (setAddress.count(address))
267             throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
268         setAddress.insert(address);
269
270         CScript scriptPubKey;
271         scriptPubKey.SetDestination(address.Get());
272         int64 nAmount = AmountFromValue(s.value_);
273
274         CTxOut out(nAmount, scriptPubKey);
275         rawTx.vout.push_back(out);
276     }
277
278     CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
279     ss << rawTx;
280     return HexStr(ss.begin(), ss.end());
281 }
282
283 Value decoderawtransaction(const Array& params, bool fHelp)
284 {
285     if (fHelp || params.size() != 1)
286         throw runtime_error(
287             "decoderawtransaction <hex string>\n"
288             "Return a JSON object representing the serialized, hex-encoded transaction.");
289
290     vector<unsigned char> txData(ParseHexV(params[0], "argument"));
291     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
292     CTransaction tx;
293     try {
294         ssData >> tx;
295     }
296     catch (std::exception &e) {
297         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
298     }
299
300     Object result;
301     TxToJSON(tx, 0, result);
302
303     return result;
304 }
305
306 Value decodescript(const Array& params, bool fHelp)
307 {
308     if (fHelp || params.size() != 1)
309         throw runtime_error(
310             "decodescript <hex string>\n"
311             "Decode a hex-encoded script.");
312
313     RPCTypeCheck(params, list_of(str_type));
314
315     Object r;
316     CScript script;
317     if (params[0].get_str().size() > 0){
318         vector<unsigned char> scriptData(ParseHexV(params[0], "argument"));
319         script = CScript(scriptData.begin(), scriptData.end());
320     } else {
321         // Empty scripts are valid
322     }
323     ScriptPubKeyToJSON(script, r, false);
324
325     r.push_back(Pair("p2sh", CBitcoinAddress(script.GetID()).ToString()));
326     return r;
327 }
328
329 Value signrawtransaction(const Array& params, bool fHelp)
330 {
331     if (fHelp || params.size() < 1 || params.size() > 4)
332         throw runtime_error(
333             "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex,\"redeemScript\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n"
334             "Sign inputs for raw transaction (serialized, hex-encoded).\n"
335             "Second optional argument (may be null) is an array of previous transaction outputs that\n"
336             "this transaction depends on but may not yet be in the block chain.\n"
337             "Third optional argument (may be null) is an array of base58-encoded private\n"
338             "keys that, if given, will be the only keys used to sign the transaction.\n"
339             "Fourth optional argument is a string that is one of six values; ALL, NONE, SINGLE or\n"
340             "ALL|ANYONECANPAY, NONE|ANYONECANPAY, SINGLE|ANYONECANPAY.\n"
341             "Returns json object with keys:\n"
342             "  hex : raw transaction with signature(s) (hex-encoded string)\n"
343             "  complete : 1 if transaction has a complete set of signature (0 if not)"
344             + HelpRequiringPassphrase());
345
346     RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true);
347
348     vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
349     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
350     vector<CTransaction> txVariants;
351     while (!ssData.empty())
352     {
353         try {
354             CTransaction tx;
355             ssData >> tx;
356             txVariants.push_back(tx);
357         }
358         catch (std::exception &e) {
359             throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
360         }
361     }
362
363     if (txVariants.empty())
364         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
365
366     // mergedTx will end up with all the signatures; it
367     // starts as a clone of the rawtx:
368     CTransaction mergedTx(txVariants[0]);
369     bool fComplete = true;
370
371     // Fetch previous transactions (inputs):
372     CCoinsView viewDummy;
373     CCoinsViewCache view(viewDummy);
374     {
375         LOCK(mempool.cs);
376         CCoinsViewCache &viewChain = *pcoinsTip;
377         CCoinsViewMemPool viewMempool(viewChain, mempool);
378         view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
379
380         BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) {
381             const uint256& prevHash = txin.prevout.hash;
382             CCoins coins;
383             view.GetCoins(prevHash, coins); // this is certainly allowed to fail
384         }
385
386         view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
387     }
388
389     bool fGivenKeys = false;
390     CBasicKeyStore tempKeystore;
391     if (params.size() > 2 && params[2].type() != null_type)
392     {
393         fGivenKeys = true;
394         Array keys = params[2].get_array();
395         BOOST_FOREACH(Value k, keys)
396         {
397             CBitcoinSecret vchSecret;
398             bool fGood = vchSecret.SetString(k.get_str());
399             if (!fGood)
400                 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
401             CKey key = vchSecret.GetKey();
402             tempKeystore.AddKey(key);
403         }
404     }
405     else
406         EnsureWalletIsUnlocked();
407
408     // Add previous txouts given in the RPC call:
409     if (params.size() > 1 && params[1].type() != null_type)
410     {
411         Array prevTxs = params[1].get_array();
412         BOOST_FOREACH(Value& p, prevTxs)
413         {
414             if (p.type() != obj_type)
415                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
416
417             Object prevOut = p.get_obj();
418
419             RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type));
420
421             uint256 txid = ParseHashO(prevOut, "txid");
422
423             int nOut = find_value(prevOut, "vout").get_int();
424             if (nOut < 0)
425                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
426
427             vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
428             CScript scriptPubKey(pkData.begin(), pkData.end());
429
430             CCoins coins;
431             if (view.GetCoins(txid, coins)) {
432                 if (coins.IsAvailable(nOut) && coins.vout[nOut].scriptPubKey != scriptPubKey) {
433                     string err("Previous output scriptPubKey mismatch:\n");
434                     err = err + coins.vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
435                         scriptPubKey.ToString();
436                     throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
437                 }
438                 // what todo if txid is known, but the actual output isn't?
439             }
440             if ((unsigned int)nOut >= coins.vout.size())
441                 coins.vout.resize(nOut+1);
442             coins.vout[nOut].scriptPubKey = scriptPubKey;
443             coins.vout[nOut].nValue = 0; // we don't know the actual output value
444             view.SetCoins(txid, coins);
445
446             // if redeemScript given and not using the local wallet (private keys
447             // given), add redeemScript to the tempKeystore so it can be signed:
448             if (fGivenKeys && scriptPubKey.IsPayToScriptHash())
449             {
450                 RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type));
451                 Value v = find_value(prevOut, "redeemScript");
452                 if (!(v == Value::null))
453                 {
454                     vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
455                     CScript redeemScript(rsData.begin(), rsData.end());
456                     tempKeystore.AddCScript(redeemScript);
457                 }
458             }
459         }
460     }
461
462     const CKeyStore& keystore = ((fGivenKeys || !pwalletMain) ? tempKeystore : *pwalletMain);
463
464     int nHashType = SIGHASH_ALL;
465     if (params.size() > 3 && params[3].type() != null_type)
466     {
467         static map<string, int> mapSigHashValues =
468             boost::assign::map_list_of
469             (string("ALL"), int(SIGHASH_ALL))
470             (string("ALL|ANYONECANPAY"), int(SIGHASH_ALL|SIGHASH_ANYONECANPAY))
471             (string("NONE"), int(SIGHASH_NONE))
472             (string("NONE|ANYONECANPAY"), int(SIGHASH_NONE|SIGHASH_ANYONECANPAY))
473             (string("SINGLE"), int(SIGHASH_SINGLE))
474             (string("SINGLE|ANYONECANPAY"), int(SIGHASH_SINGLE|SIGHASH_ANYONECANPAY))
475             ;
476         string strHashType = params[3].get_str();
477         if (mapSigHashValues.count(strHashType))
478             nHashType = mapSigHashValues[strHashType];
479         else
480             throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
481     }
482
483     bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
484
485     // Sign what we can:
486     for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
487     {
488         CTxIn& txin = mergedTx.vin[i];
489         CCoins coins;
490         if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n))
491         {
492             fComplete = false;
493             continue;
494         }
495         const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey;
496
497         txin.scriptSig.clear();
498         // Only sign SIGHASH_SINGLE if there's a corresponding output:
499         if (!fHashSingle || (i < mergedTx.vout.size()))
500             SignSignature(keystore, prevPubKey, mergedTx, i, nHashType);
501
502         // ... and merge in other signatures:
503         BOOST_FOREACH(const CTransaction& txv, txVariants)
504         {
505             txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
506         }
507         if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0))
508             fComplete = false;
509     }
510
511     Object result;
512     CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
513     ssTx << mergedTx;
514     result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
515     result.push_back(Pair("complete", fComplete));
516
517     return result;
518 }
519
520 Value sendrawtransaction(const Array& params, bool fHelp)
521 {
522     if (fHelp || params.size() < 1 || params.size() > 2)
523         throw runtime_error(
524             "sendrawtransaction <hex string> [allowhighfees=false]\n"
525             "Submits raw transaction (serialized, hex-encoded) to local node and network.");
526
527     // parse hex string from parameter
528     vector<unsigned char> txData(ParseHexV(params[0], "parameter"));
529     CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
530     CTransaction tx;
531
532     bool fOverrideFees = false;
533     if (params.size() > 1)
534         fOverrideFees = params[1].get_bool();
535
536     // deserialize binary data stream
537     try {
538         ssData >> tx;
539     }
540     catch (std::exception &e) {
541         throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
542     }
543     uint256 hashTx = tx.GetHash();
544
545     bool fHave = false;
546     CCoinsViewCache &view = *pcoinsTip;
547     CCoins existingCoins;
548     {
549         fHave = view.GetCoins(hashTx, existingCoins);
550         if (!fHave) {
551             // push to local node
552             CValidationState state;
553             if (!mempool.accept(state, tx, false, NULL, !fOverrideFees))
554                 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state
555         }
556     }
557     if (fHave) {
558         if (existingCoins.nHeight < 1000000000)
559             throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "transaction already in block chain");
560         // Not in block, but already in the memory pool; will drop
561         // through to re-relay it.
562     } else {
563         SyncWithWallets(hashTx, tx, NULL);
564     }
565     RelayTransaction(tx, hashTx);
566
567     return hashTx.GetHex();
568 }
This page took 0.058314 seconds and 4 git commands to generate.