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.
6 #include <boost/assign/list_of.hpp>
9 #include "bitcoinrpc.h"
17 using namespace boost;
18 using namespace boost::assign;
19 using namespace json_spirit;
21 void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out, bool fIncludeHex)
24 vector<CTxDestination> addresses;
27 out.push_back(Pair("asm", scriptPubKey.ToString()));
30 out.push_back(Pair("hex", HexStr(scriptPubKey.begin(), scriptPubKey.end())));
32 if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired))
34 out.push_back(Pair("type", GetTxnOutputType(TX_NONSTANDARD)));
38 out.push_back(Pair("reqSigs", nRequired));
39 out.push_back(Pair("type", GetTxnOutputType(type)));
42 BOOST_FOREACH(const CTxDestination& addr, addresses)
43 a.push_back(CBitcoinAddress(addr).ToString());
44 out.push_back(Pair("addresses", a));
47 void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
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));
55 BOOST_FOREACH(const CTxIn& txin, tx.vin)
59 in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
62 in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
63 in.push_back(Pair("vout", (boost::int64_t)txin.prevout.n));
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));
69 in.push_back(Pair("sequence", (boost::int64_t)txin.nSequence));
72 entry.push_back(Pair("vin", vin));
74 for (unsigned int i = 0; i < tx.vout.size(); i++)
76 const CTxOut& txout = tx.vout[i];
78 out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
79 out.push_back(Pair("n", (boost::int64_t)i));
81 ScriptPubKeyToJSON(txout.scriptPubKey, o, false);
82 out.push_back(Pair("scriptPubKey", o));
85 entry.push_back(Pair("vout", vout));
89 entry.push_back(Pair("blockhash", hashBlock.GetHex()));
90 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(hashBlock);
91 if (mi != mapBlockIndex.end() && (*mi).second)
93 CBlockIndex* pindex = (*mi).second;
94 if (pindex->IsInMainChain())
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));
101 entry.push_back(Pair("confirmations", 0));
106 Value getrawtransaction(const Array& params, bool fHelp)
108 if (fHelp || params.size() < 1 || params.size() > 2)
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>.");
117 hash.SetHex(params[0].get_str());
119 bool fVerbose = false;
120 if (params.size() > 1)
121 fVerbose = (params[1].get_int() != 0);
124 uint256 hashBlock = 0; // trying to find transaction in the blockchain
125 if (!GetTransaction(hash, tx, hashBlock, true))
127 if (pwalletMain->mapWallet.count(hash))
128 tx = (CTransaction)pwalletMain->mapWallet[hash]; // get transaction from wallet
130 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
133 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
135 string strHex = HexStr(ssTx.begin(), ssTx.end());
141 result.push_back(Pair("hex", strHex));
142 TxToJSON(tx, hashBlock, result);
146 Value listunspent(const Array& params, bool fHelp)
148 if (fHelp || params.size() > 3)
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}");
157 RPCTypeCheck(params, list_of(int_type)(int_type)(array_type));
160 if (params.size() > 0)
161 nMinDepth = params[0].get_int();
163 int nMaxDepth = 9999999;
164 if (params.size() > 1)
165 nMaxDepth = params[1].get_int();
167 set<CBitcoinAddress> setAddress;
168 if (params.size() > 2)
170 Array inputs = params[2].get_array();
171 BOOST_FOREACH(Value& input, inputs)
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);
183 vector<COutput> vecOutputs;
184 pwalletMain->AvailableCoins(vecOutputs, false);
185 BOOST_FOREACH(const COutput& out, vecOutputs)
187 if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
190 if(setAddress.size())
192 CTxDestination address;
193 if(!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
196 if (!setAddress.count(address))
200 int64 nValue = out.tx->vout[out.i].nValue;
201 const CScript& pk = out.tx->vout[out.i].scriptPubKey;
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))
208 entry.push_back(Pair("address", CBitcoinAddress(address).ToString()));
209 if (pwalletMain->mapAddressBook.count(address))
210 entry.push_back(Pair("account", pwalletMain->mapAddressBook[address]));
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);
221 Value decodescript(const Array& params, bool fHelp)
223 if (fHelp || params.size() != 1)
225 "decodescript <hex string>\n"
226 "Decode a hex-encoded script.");
228 RPCTypeCheck(params, list_of(str_type));
232 if (params[0].get_str().size() > 0){
233 vector<unsigned char> scriptData(ParseHexV(params[0], "argument"));
234 script = CScript(scriptData.begin(), scriptData.end());
236 // Empty scripts are valid
238 ScriptPubKeyToJSON(script, r, false);
240 r.push_back(Pair("p2sh", CBitcoinAddress(script.GetID()).ToString()));
244 Value createrawtransaction(const Array& params, bool fHelp)
246 if (fHelp || params.size() != 2)
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.");
256 RPCTypeCheck(params, list_of(array_type)(obj_type));
258 Array inputs = params[0].get_array();
259 Object sendTo = params[1].get_obj();
263 BOOST_FOREACH(Value& input, inputs)
265 const Object& o = input.get_obj();
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();
272 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");
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();
279 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
281 CTxIn in(COutPoint(uint256(txid), nOutput));
282 rawTx.vin.push_back(in);
285 set<CBitcoinAddress> setAddress;
286 BOOST_FOREACH(const Pair& s, sendTo)
288 CBitcoinAddress address(s.name_);
289 if (!address.IsValid())
290 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_);
292 if (setAddress.count(address))
293 throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
294 setAddress.insert(address);
296 CScript scriptPubKey;
297 scriptPubKey.SetDestination(address.Get());
298 int64 nAmount = AmountFromValue(s.value_);
300 CTxOut out(nAmount, scriptPubKey);
301 rawTx.vout.push_back(out);
304 CDataStream ss(SER_NETWORK, PROTOCOL_VERSION);
306 return HexStr(ss.begin(), ss.end());
309 Value decoderawtransaction(const Array& params, bool fHelp)
311 if (fHelp || params.size() != 1)
313 "decoderawtransaction <hex string>\n"
314 "Return a JSON object representing the serialized, hex-encoded transaction.");
316 RPCTypeCheck(params, list_of(str_type));
318 vector<unsigned char> txData(ParseHex(params[0].get_str()));
319 CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
324 catch (std::exception &e) {
325 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
329 TxToJSON(tx, 0, result);
334 Value signrawtransaction(const Array& params, bool fHelp)
336 if (fHelp || params.size() < 1 || params.size() > 4)
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());
351 RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true);
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())
361 txVariants.push_back(tx);
363 catch (std::exception &e) {
364 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
368 if (txVariants.empty())
369 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
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;
376 // Fetch previous transactions (inputs):
377 CCoinsView viewDummy;
378 CCoinsViewCache view(viewDummy);
381 CCoinsViewCache &viewChain = *pcoinsTip;
382 CCoinsViewMemPool viewMempool(viewChain, mempool);
383 view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
385 BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) {
386 const uint256& prevHash = txin.prevout.hash;
388 view.GetCoins(prevHash, coins); // this is certainly allowed to fail
391 view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
394 // Add previous txouts given in the RPC call:
395 if (params.size() > 1 && params[1].type() != null_type)
397 Array prevTxs = params[1].get_array();
398 BOOST_FOREACH(Value& p, prevTxs)
400 if (p.type() != obj_type)
401 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
403 Object prevOut = p.get_obj();
405 RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type));
407 string txidHex = find_value(prevOut, "txid").get_str();
409 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "txid must be hexadecimal");
411 txid.SetHex(txidHex);
413 int nOut = find_value(prevOut, "vout").get_int();
415 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
417 string pkHex = find_value(prevOut, "scriptPubKey").get_str();
419 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "scriptPubKey must be hexadecimal");
420 vector<unsigned char> pkData(ParseHex(pkHex));
421 CScript scriptPubKey(pkData.begin(), pkData.end());
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);
431 // what todo if txid is known, but the actual output isn't?
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);
439 bool fGivenKeys = false;
440 CBasicKeyStore tempKeystore;
441 if (params.size() > 2 && params[2].type() != null_type)
444 Array keys = params[2].get_array();
445 BOOST_FOREACH(Value k, keys)
447 CBitcoinSecret vchSecret;
448 bool fGood = vchSecret.SetString(k.get_str());
450 throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY,"Invalid private key");
453 CSecret secret = vchSecret.GetSecret(fCompressed);
454 key.SetSecret(secret, fCompressed);
455 tempKeystore.AddKey(key);
459 EnsureWalletIsUnlocked();
461 const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);
463 int nHashType = SIGHASH_ALL;
464 if (params.size() > 3 && params[3].type() != null_type)
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))
475 string strHashType = params[3].get_str();
476 if (mapSigHashValues.count(strHashType))
477 nHashType = mapSigHashValues[strHashType];
479 throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
482 bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
485 for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
487 CTxIn& txin = mergedTx.vin[i];
489 if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n))
494 const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey;
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);
501 // ... and merge in other signatures:
502 BOOST_FOREACH(const CTransaction& txv, txVariants)
504 txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
506 if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, true, 0))
511 CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
513 result.push_back(Pair("hex", HexStr(ssTx.begin(), ssTx.end())));
514 result.push_back(Pair("complete", fComplete));
519 Value sendrawtransaction(const Array& params, bool fHelp)
521 if (fHelp || params.size() < 1 || params.size() > 1)
523 "sendrawtransaction <hex string>\n"
524 "Submits raw transaction (serialized, hex-encoded) to local node and network.");
526 RPCTypeCheck(params, list_of(str_type));
528 // parse hex string from parameter
529 vector<unsigned char> txData(ParseHex(params[0].get_str()));
530 CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
533 // deserialize binary data stream
537 catch (std::exception &e) {
538 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
540 uint256 hashTx = tx.GetHash();
543 CCoinsViewCache &view = *pcoinsTip;
544 CCoins existingCoins;
546 fHave = view.GetCoins(hashTx, existingCoins);
548 // push to local node
549 if (!tx.AcceptToMemoryPool())
550 throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected");
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.
559 SyncWithWallets(hashTx, tx, NULL, true);
561 RelayTransaction(tx, hashTx);
563 return hashTx.GetHex();