using namespace boost::assign;
using namespace json_spirit;
-// These are all in bitcoinrpc.cpp:
-extern Object JSONRPCError(int code, const string& message);
-extern int64 AmountFromValue(const Value& value);
-extern Value ValueFromAmount(int64 amount);
-extern std::string HelpRequiringPassphrase();
-extern void EnsureWalletIsUnlocked();
-
-void
-ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out)
+//
+// Utilities: convert hex-encoded Values
+// (throws error if not hex).
+//
+uint256 ParseHashV(const Value& v, string strName)
+{
+ string strHex;
+ if (v.type() == str_type)
+ strHex = v.get_str();
+ if (!IsHex(strHex)) // Note: IsHex("") is false
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
+ uint256 result;
+ result.SetHex(strHex);
+ return result;
+}
+uint256 ParseHashO(const Object& o, string strKey)
+{
+ return ParseHashV(find_value(o, strKey), strKey);
+}
+vector<unsigned char> ParseHexV(const Value& v, string strName)
+{
+ string strHex;
+ if (v.type() == str_type)
+ strHex = v.get_str();
+ if (!IsHex(strHex))
+ throw JSONRPCError(RPC_INVALID_PARAMETER, strName+" must be hexadecimal string (not '"+strHex+"')");
+ return ParseHex(strHex);
+}
+vector<unsigned char> ParseHexO(const Object& o, string strKey)
+{
+ return ParseHexV(find_value(o, strKey), strKey);
+}
+
+void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out)
{
txnouttype type;
vector<CTxDestination> addresses;
out.push_back(Pair("addresses", a));
}
-void
-TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
+void TxToJSON(const CTransaction& tx, const uint256 hashBlock, Object& entry)
{
entry.push_back(Pair("txid", tx.GetHash().GetHex()));
entry.push_back(Pair("version", tx.nVersion));
{
entry.push_back(Pair("confirmations", 1 + nBestHeight - pindex->nHeight));
entry.push_back(Pair("time", (boost::int64_t)pindex->nTime));
+ entry.push_back(Pair("blocktime", (boost::int64_t)pindex->nTime));
}
else
entry.push_back(Pair("confirmations", 0));
"If verbose is non-zero, returns an Object\n"
"with information about <txid>.");
- uint256 hash;
- hash.SetHex(params[0].get_str());
+ uint256 hash = ParseHashV(params[0], "parameter 1");
bool fVerbose = false;
if (params.size() > 1)
CTransaction tx;
uint256 hashBlock = 0;
- if (!GetTransaction(hash, tx, hashBlock))
- throw JSONRPCError(-5, "No information available about transaction");
+ if (!GetTransaction(hash, tx, hashBlock, true))
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "No information available about transaction");
CDataStream ssTx(SER_NETWORK, PROTOCOL_VERSION);
ssTx << tx;
Value listunspent(const Array& params, bool fHelp)
{
- if (fHelp || params.size() > 2)
+ if (fHelp || params.size() > 3)
throw runtime_error(
- "listunspent [minconf=1] [maxconf=999999]\n"
+ "listunspent [minconf=1] [maxconf=9999999] [\"address\",...]\n"
"Returns array of unspent transaction outputs\n"
"with between minconf and maxconf (inclusive) confirmations.\n"
+ "Optionally filtered to only include txouts paid to specified addresses.\n"
"Results are an array of Objects, each of which has:\n"
"{txid, vout, scriptPubKey, amount, confirmations}");
- RPCTypeCheck(params, list_of(int_type)(int_type));
+ RPCTypeCheck(params, list_of(int_type)(int_type)(array_type));
int nMinDepth = 1;
if (params.size() > 0)
nMinDepth = params[0].get_int();
- int nMaxDepth = 999999;
+ int nMaxDepth = 9999999;
if (params.size() > 1)
nMaxDepth = params[1].get_int();
+ set<CBitcoinAddress> setAddress;
+ if (params.size() > 2)
+ {
+ Array inputs = params[2].get_array();
+ BOOST_FOREACH(Value& input, inputs)
+ {
+ CBitcoinAddress address(input.get_str());
+ if (!address.IsValid())
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+input.get_str());
+ if (setAddress.count(address))
+ throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+input.get_str());
+ setAddress.insert(address);
+ }
+ }
+
Array results;
vector<COutput> vecOutputs;
pwalletMain->AvailableCoins(vecOutputs, false);
if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
continue;
+ if (setAddress.size())
+ {
+ CTxDestination address;
+ if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
+ continue;
+
+ if (!setAddress.count(address))
+ continue;
+ }
+
int64 nValue = out.tx->vout[out.i].nValue;
const CScript& pk = out.tx->vout[out.i].scriptPubKey;
Object entry;
entry.push_back(Pair("txid", out.tx->GetHash().GetHex()));
entry.push_back(Pair("vout", out.i));
entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end())));
+ if (pk.IsPayToScriptHash())
+ {
+ CTxDestination address;
+ if (ExtractDestination(pk, address))
+ {
+ const CScriptID& hash = boost::get<const CScriptID&>(address);
+ CScript redeemScript;
+ if (pwalletMain->GetCScript(hash, redeemScript))
+ entry.push_back(Pair("redeemScript", HexStr(redeemScript.begin(), redeemScript.end())));
+ }
+ }
entry.push_back(Pair("amount",ValueFromAmount(nValue)));
entry.push_back(Pair("confirmations",out.nDepth));
results.push_back(entry);
CTransaction rawTx;
- BOOST_FOREACH(Value& input, inputs)
+ BOOST_FOREACH(const Value& input, inputs)
{
const Object& o = input.get_obj();
- const Value& txid_v = find_value(o, "txid");
- if (txid_v.type() != str_type)
- throw JSONRPCError(-8, "Invalid parameter, missing txid key");
- string txid = txid_v.get_str();
- if (!IsHex(txid))
- throw JSONRPCError(-8, "Invalid parameter, expected hex txid");
+ uint256 txid = ParseHashO(o, "txid");
const Value& vout_v = find_value(o, "vout");
if (vout_v.type() != int_type)
- throw JSONRPCError(-8, "Invalid parameter, missing vout key");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing vout key");
int nOutput = vout_v.get_int();
if (nOutput < 0)
- throw JSONRPCError(-8, "Invalid parameter, vout must be positive");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, vout must be positive");
- CTxIn in(COutPoint(uint256(txid), nOutput));
+ CTxIn in(COutPoint(txid, nOutput));
rawTx.vin.push_back(in);
}
{
CBitcoinAddress address(s.name_);
if (!address.IsValid())
- throw JSONRPCError(-5, string("Invalid Bitcoin address:")+s.name_);
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, string("Invalid Bitcoin address: ")+s.name_);
if (setAddress.count(address))
- throw JSONRPCError(-8, string("Invalid parameter, duplicated address: ")+s.name_);
+ throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, duplicated address: ")+s.name_);
setAddress.insert(address);
CScript scriptPubKey;
"decoderawtransaction <hex string>\n"
"Return a JSON object representing the serialized, hex-encoded transaction.");
- RPCTypeCheck(params, list_of(str_type));
-
- vector<unsigned char> txData(ParseHex(params[0].get_str()));
+ vector<unsigned char> txData(ParseHexV(params[0], "argument"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
CTransaction tx;
try {
ssData >> tx;
}
catch (std::exception &e) {
- throw JSONRPCError(-22, "TX decode failed");
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
Object result;
{
if (fHelp || params.size() < 1 || params.size() > 4)
throw runtime_error(
- "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n"
+ "signrawtransaction <hex string> [{\"txid\":txid,\"vout\":n,\"scriptPubKey\":hex,\"redeemScript\":hex},...] [<privatekey1>,...] [sighashtype=\"ALL\"]\n"
"Sign inputs for raw transaction (serialized, hex-encoded).\n"
"Second optional argument (may be null) is an array of previous transaction outputs that\n"
- "this transaction depends on but may not yet be in the blockchain.\n"
+ "this transaction depends on but may not yet be in the block chain.\n"
"Third optional argument (may be null) is an array of base58-encoded private\n"
"keys that, if given, will be the only keys used to sign the transaction.\n"
"Fourth optional argument is a string that is one of six values; ALL, NONE, SINGLE or\n"
RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true);
- vector<unsigned char> txData(ParseHex(params[0].get_str()));
+ vector<unsigned char> txData(ParseHexV(params[0], "argument 1"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
vector<CTransaction> txVariants;
while (!ssData.empty())
txVariants.push_back(tx);
}
catch (std::exception &e) {
- throw JSONRPCError(-22, "TX decode failed");
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
}
if (txVariants.empty())
- throw JSONRPCError(-22, "Missing transaction");
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "Missing transaction");
// mergedTx will end up with all the signatures; it
// starts as a clone of the rawtx:
bool fComplete = true;
// Fetch previous transactions (inputs):
- map<COutPoint, CScript> mapPrevOut;
- for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
+ CCoinsView viewDummy;
+ CCoinsViewCache view(viewDummy);
{
- CTransaction tempTx;
- MapPrevTx mapPrevTx;
- CTxDB txdb("r");
- map<uint256, CTxIndex> unused;
- bool fInvalid;
-
- // FetchInputs aborts on failure, so we go one at a time.
- tempTx.vin.push_back(mergedTx.vin[i]);
- tempTx.FetchInputs(txdb, unused, false, false, mapPrevTx, fInvalid);
-
- // Copy results into mapPrevOut:
- BOOST_FOREACH(const CTxIn& txin, tempTx.vin)
- {
+ LOCK(mempool.cs);
+ CCoinsViewCache &viewChain = *pcoinsTip;
+ CCoinsViewMemPool viewMempool(viewChain, mempool);
+ view.SetBackend(viewMempool); // temporarily switch cache backend to db+mempool view
+
+ BOOST_FOREACH(const CTxIn& txin, mergedTx.vin) {
const uint256& prevHash = txin.prevout.hash;
- if (mapPrevTx.count(prevHash) && mapPrevTx[prevHash].second.vout.size()>txin.prevout.n)
- mapPrevOut[txin.prevout] = mapPrevTx[prevHash].second.vout[txin.prevout.n].scriptPubKey;
+ CCoins coins;
+ view.GetCoins(prevHash, coins); // this is certainly allowed to fail
+ }
+
+ view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long
+ }
+
+ bool fGivenKeys = false;
+ CBasicKeyStore tempKeystore;
+ if (params.size() > 2 && params[2].type() != null_type)
+ {
+ fGivenKeys = true;
+ Array keys = params[2].get_array();
+ BOOST_FOREACH(Value k, keys)
+ {
+ CBitcoinSecret vchSecret;
+ bool fGood = vchSecret.SetString(k.get_str());
+ if (!fGood)
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid private key");
+ CKey key;
+ bool fCompressed;
+ CSecret secret = vchSecret.GetSecret(fCompressed);
+ key.SetSecret(secret, fCompressed);
+ tempKeystore.AddKey(key);
}
}
+ else
+ EnsureWalletIsUnlocked();
// Add previous txouts given in the RPC call:
if (params.size() > 1 && params[1].type() != null_type)
BOOST_FOREACH(Value& p, prevTxs)
{
if (p.type() != obj_type)
- throw JSONRPCError(-22, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "expected object with {\"txid'\",\"vout\",\"scriptPubKey\"}");
Object prevOut = p.get_obj();
RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type));
- string txidHex = find_value(prevOut, "txid").get_str();
- if (!IsHex(txidHex))
- throw JSONRPCError(-22, "txid must be hexadecimal");
- uint256 txid;
- txid.SetHex(txidHex);
+ uint256 txid = ParseHashO(prevOut, "txid");
int nOut = find_value(prevOut, "vout").get_int();
if (nOut < 0)
- throw JSONRPCError(-22, "vout must be positive");
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
- string pkHex = find_value(prevOut, "scriptPubKey").get_str();
- if (!IsHex(pkHex))
- throw JSONRPCError(-22, "scriptPubKey must be hexadecimal");
- vector<unsigned char> pkData(ParseHex(pkHex));
+ vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
CScript scriptPubKey(pkData.begin(), pkData.end());
- COutPoint outpoint(txid, nOut);
- if (mapPrevOut.count(outpoint))
- {
- // Complain if scriptPubKey doesn't match
- if (mapPrevOut[outpoint] != scriptPubKey)
- {
+ CCoins coins;
+ if (view.GetCoins(txid, coins)) {
+ if (coins.IsAvailable(nOut) && coins.vout[nOut].scriptPubKey != scriptPubKey) {
string err("Previous output scriptPubKey mismatch:\n");
- err = err + mapPrevOut[outpoint].ToString() + "\nvs:\n"+
+ err = err + coins.vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
scriptPubKey.ToString();
- throw JSONRPCError(-22, err);
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
+ }
+ // what todo if txid is known, but the actual output isn't?
+ }
+ if ((unsigned int)nOut >= coins.vout.size())
+ coins.vout.resize(nOut+1);
+ coins.vout[nOut].scriptPubKey = scriptPubKey;
+ coins.vout[nOut].nValue = 0; // we don't know the actual output value
+ view.SetCoins(txid, coins);
+
+ // if redeemScript given and not using the local wallet (private keys
+ // given), add redeemScript to the tempKeystore so it can be signed:
+ if (fGivenKeys && scriptPubKey.IsPayToScriptHash())
+ {
+ RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type));
+ Value v = find_value(prevOut, "redeemScript");
+ if (!(v == Value::null))
+ {
+ vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
+ CScript redeemScript(rsData.begin(), rsData.end());
+ tempKeystore.AddCScript(redeemScript);
}
}
- else
- mapPrevOut[outpoint] = scriptPubKey;
- }
- }
-
- bool fGivenKeys = false;
- CBasicKeyStore tempKeystore;
- if (params.size() > 2 && params[2].type() != null_type)
- {
- fGivenKeys = true;
- Array keys = params[2].get_array();
- BOOST_FOREACH(Value k, keys)
- {
- CBitcoinSecret vchSecret;
- bool fGood = vchSecret.SetString(k.get_str());
- if (!fGood)
- throw JSONRPCError(-5,"Invalid private key");
- CKey key;
- bool fCompressed;
- CSecret secret = vchSecret.GetSecret(fCompressed);
- key.SetSecret(secret, fCompressed);
- tempKeystore.AddKey(key);
}
}
- else
- EnsureWalletIsUnlocked();
const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);
if (mapSigHashValues.count(strHashType))
nHashType = mapSigHashValues[strHashType];
else
- throw JSONRPCError(-8, "Invalid sighash param");
+ throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid sighash param");
}
bool fHashSingle = ((nHashType & ~SIGHASH_ANYONECANPAY) == SIGHASH_SINGLE);
for (unsigned int i = 0; i < mergedTx.vin.size(); i++)
{
CTxIn& txin = mergedTx.vin[i];
- if (mapPrevOut.count(txin.prevout) == 0)
+ CCoins coins;
+ if (!view.GetCoins(txin.prevout.hash, coins) || !coins.IsAvailable(txin.prevout.n))
{
fComplete = false;
continue;
}
- const CScript& prevPubKey = mapPrevOut[txin.prevout];
+ const CScript& prevPubKey = coins.vout[txin.prevout.n].scriptPubKey;
txin.scriptSig.clear();
// Only sign SIGHASH_SINGLE if there's a corresponding output:
{
txin.scriptSig = CombineSignatures(prevPubKey, mergedTx, i, txin.scriptSig, txv.vin[i].scriptSig);
}
- if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, true, 0))
+ if (!VerifyScript(txin.scriptSig, prevPubKey, mergedTx, i, SCRIPT_VERIFY_P2SH | SCRIPT_VERIFY_STRICTENC, 0))
fComplete = false;
}
"sendrawtransaction <hex string>\n"
"Submits raw transaction (serialized, hex-encoded) to local node and network.");
- RPCTypeCheck(params, list_of(str_type));
-
// parse hex string from parameter
- vector<unsigned char> txData(ParseHex(params[0].get_str()));
+ vector<unsigned char> txData(ParseHexV(params[0], "parameter"));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
CTransaction tx;
ssData >> tx;
}
catch (std::exception &e) {
- throw JSONRPCError(-22, "TX decode failed");
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
}
uint256 hashTx = tx.GetHash();
- // See if the transaction is already in a block
- // or in the memory pool:
- CTransaction existingTx;
- uint256 hashBlock = 0;
- if (GetTransaction(hashTx, existingTx, hashBlock))
+ bool fHave = false;
+ CCoinsViewCache &view = *pcoinsTip;
+ CCoins existingCoins;
{
- if (hashBlock != 0)
- throw JSONRPCError(-5, string("transaction already in block ")+hashBlock.GetHex());
+ fHave = view.GetCoins(hashTx, existingCoins);
+ if (!fHave) {
+ // push to local node
+ CValidationState state;
+ if (!tx.AcceptToMemoryPool(state, true, false))
+ throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX rejected"); // TODO: report validation state
+ }
+ }
+ if (fHave) {
+ if (existingCoins.nHeight < 1000000000)
+ throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "transaction already in block chain");
// Not in block, but already in the memory pool; will drop
// through to re-relay it.
+ } else {
+ SyncWithWallets(hashTx, tx, NULL, true);
}
- else
- {
- // push to local node
- CTxDB txdb("r");
- if (!tx.AcceptToMemoryPool(txdb))
- throw JSONRPCError(-22, "TX rejected");
-
- SyncWithWallets(tx, NULL, true);
- }
- RelayMessage(CInv(MSG_TX, hashTx), tx);
+ RelayTransaction(tx, hashTx);
return hashTx.GetHex();
}