Merge branch 'rawtx_p2sh'

This commit is contained in:
Gavin Andresen 2012-10-29 15:49:46 -04:00
commit ae8fc19788
5 changed files with 232 additions and 75 deletions

View File

@ -234,6 +234,7 @@ static const CRPCCommand vRPCCommands[] =
{ "sendfrom", &sendfrom, false, false }, { "sendfrom", &sendfrom, false, false },
{ "sendmany", &sendmany, false, false }, { "sendmany", &sendmany, false, false },
{ "addmultisigaddress", &addmultisigaddress, false, false }, { "addmultisigaddress", &addmultisigaddress, false, false },
{ "createmultisig", &createmultisig, true, true },
{ "getrawmempool", &getrawmempool, true, false }, { "getrawmempool", &getrawmempool, true, false },
{ "getblock", &getblock, false, false }, { "getblock", &getblock, false, false },
{ "getblockhash", &getblockhash, false, false }, { "getblockhash", &getblockhash, false, false },
@ -1160,6 +1161,8 @@ Array RPCConvertValues(const std::string &strMethod, const std::vector<std::stri
if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]); if (strMethod == "sendmany" && n > 2) ConvertTo<boost::int64_t>(params[2]);
if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]); if (strMethod == "addmultisigaddress" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]); if (strMethod == "addmultisigaddress" && n > 1) ConvertTo<Array>(params[1]);
if (strMethod == "createmultisig" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "createmultisig" && n > 1) ConvertTo<Array>(params[1]);
if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]); if (strMethod == "listunspent" && n > 0) ConvertTo<boost::int64_t>(params[0]);
if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]); if (strMethod == "listunspent" && n > 1) ConvertTo<boost::int64_t>(params[1]);
if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]); if (strMethod == "listunspent" && n > 2) ConvertTo<Array>(params[2]);

View File

@ -158,6 +158,7 @@ extern json_spirit::Value movecmd(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value sendfrom(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value sendfrom(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value sendmany(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value sendmany(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value addmultisigaddress(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value addmultisigaddress(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value createmultisig(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value listreceivedbyaddress(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listreceivedbyaddress(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value listreceivedbyaccount(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listreceivedbyaccount(const json_spirit::Array& params, bool fHelp);
extern json_spirit::Value listtransactions(const json_spirit::Array& params, bool fHelp); extern json_spirit::Value listtransactions(const json_spirit::Array& params, bool fHelp);

View File

@ -18,6 +18,39 @@ using namespace boost;
using namespace boost::assign; using namespace boost::assign;
using namespace json_spirit; using namespace json_spirit;
//
// 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) void ScriptPubKeyToJSON(const CScript& scriptPubKey, Object& out)
{ {
txnouttype type; txnouttype type;
@ -109,8 +142,7 @@ Value getrawtransaction(const Array& params, bool fHelp)
"If verbose is non-zero, returns an Object\n" "If verbose is non-zero, returns an Object\n"
"with information about <txid>."); "with information about <txid>.");
uint256 hash; uint256 hash = ParseHashV(params[0], "parameter 1");
hash.SetHex(params[0].get_str());
bool fVerbose = false; bool fVerbose = false;
if (params.size() > 1) if (params.size() > 1)
@ -178,10 +210,10 @@ Value listunspent(const Array& params, bool fHelp)
if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth) if (out.nDepth < nMinDepth || out.nDepth > nMaxDepth)
continue; continue;
if(setAddress.size()) if (setAddress.size())
{ {
CTxDestination address; CTxDestination address;
if(!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address))
continue; continue;
if (!setAddress.count(address)) if (!setAddress.count(address))
@ -194,6 +226,17 @@ Value listunspent(const Array& params, bool fHelp)
entry.push_back(Pair("txid", out.tx->GetHash().GetHex())); entry.push_back(Pair("txid", out.tx->GetHash().GetHex()));
entry.push_back(Pair("vout", out.i)); entry.push_back(Pair("vout", out.i));
entry.push_back(Pair("scriptPubKey", HexStr(pk.begin(), pk.end()))); 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("amount",ValueFromAmount(nValue)));
entry.push_back(Pair("confirmations",out.nDepth)); entry.push_back(Pair("confirmations",out.nDepth));
results.push_back(entry); results.push_back(entry);
@ -221,16 +264,11 @@ Value createrawtransaction(const Array& params, bool fHelp)
CTransaction rawTx; CTransaction rawTx;
BOOST_FOREACH(Value& input, inputs) BOOST_FOREACH(const Value& input, inputs)
{ {
const Object& o = input.get_obj(); const Object& o = input.get_obj();
const Value& txid_v = find_value(o, "txid"); uint256 txid = ParseHashO(o, "txid");
if (txid_v.type() != str_type)
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, missing txid key");
string txid = txid_v.get_str();
if (!IsHex(txid))
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected hex txid");
const Value& vout_v = find_value(o, "vout"); const Value& vout_v = find_value(o, "vout");
if (vout_v.type() != int_type) if (vout_v.type() != int_type)
@ -239,7 +277,7 @@ Value createrawtransaction(const Array& params, bool fHelp)
if (nOutput < 0) if (nOutput < 0)
throw JSONRPCError(RPC_INVALID_PARAMETER, "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); rawTx.vin.push_back(in);
} }
@ -274,9 +312,7 @@ Value decoderawtransaction(const Array& params, bool fHelp)
"decoderawtransaction <hex string>\n" "decoderawtransaction <hex string>\n"
"Return a JSON object representing the serialized, hex-encoded transaction."); "Return a JSON object representing the serialized, hex-encoded transaction.");
RPCTypeCheck(params, list_of(str_type)); vector<unsigned char> txData(ParseHexV(params[0], "argument"));
vector<unsigned char> txData(ParseHex(params[0].get_str()));
CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
CTransaction tx; CTransaction tx;
try { try {
@ -296,7 +332,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
{ {
if (fHelp || params.size() < 1 || params.size() > 4) if (fHelp || params.size() < 1 || params.size() > 4)
throw runtime_error( 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" "Sign inputs for raw transaction (serialized, hex-encoded).\n"
"Second optional argument (may be null) is an array of previous transaction outputs that\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 block chain.\n" "this transaction depends on but may not yet be in the block chain.\n"
@ -311,7 +347,7 @@ Value signrawtransaction(const Array& params, bool fHelp)
RPCTypeCheck(params, list_of(str_type)(array_type)(array_type)(str_type), true); 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); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
vector<CTransaction> txVariants; vector<CTransaction> txVariants;
while (!ssData.empty()) while (!ssData.empty())
@ -352,6 +388,28 @@ Value signrawtransaction(const Array& params, bool fHelp)
view.SetBackend(viewDummy); // switch back to avoid locking mempool for too long 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: // Add previous txouts given in the RPC call:
if (params.size() > 1 && params[1].type() != null_type) if (params.size() > 1 && params[1].type() != null_type)
{ {
@ -363,22 +421,15 @@ Value signrawtransaction(const Array& params, bool fHelp)
Object prevOut = p.get_obj(); Object prevOut = p.get_obj();
RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)); RPCTypeCheck(prevOut, map_list_of("txid", str_type)("vout", int_type)("scriptPubKey", str_type)("redeemScript",str_type));
string txidHex = find_value(prevOut, "txid").get_str(); uint256 txid = ParseHashO(prevOut, "txid");
if (!IsHex(txidHex))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "txid must be hexadecimal");
uint256 txid;
txid.SetHex(txidHex);
int nOut = find_value(prevOut, "vout").get_int(); int nOut = find_value(prevOut, "vout").get_int();
if (nOut < 0) if (nOut < 0)
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive"); throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
string pkHex = find_value(prevOut, "scriptPubKey").get_str(); vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
if (!IsHex(pkHex))
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "scriptPubKey must be hexadecimal");
vector<unsigned char> pkData(ParseHex(pkHex));
CScript scriptPubKey(pkData.begin(), pkData.end()); CScript scriptPubKey(pkData.begin(), pkData.end());
CCoins coins; CCoins coins;
@ -391,33 +442,23 @@ Value signrawtransaction(const Array& params, bool fHelp)
} }
// what todo if txid is known, but the actual output isn't? // 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].scriptPubKey = scriptPubKey;
coins.vout[nOut].nValue = 0; // we don't know the actual output value coins.vout[nOut].nValue = 0; // we don't know the actual output value
view.SetCoins(txid, coins); view.SetCoins(txid, coins);
}
}
bool fGivenKeys = false; // if redeemScript given and not using the local wallet (private keys
CBasicKeyStore tempKeystore; // given), add redeemScript to the tempKeystore so it can be signed:
if (params.size() > 2 && params[2].type() != null_type) Value v = find_value(prevOut, "redeemScript");
{ if (fGivenKeys && scriptPubKey.IsPayToScriptHash() && !(v == Value::null))
fGivenKeys = true; {
Array keys = params[2].get_array(); vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
BOOST_FOREACH(Value k, keys) CScript redeemScript(rsData.begin(), rsData.end());
{ tempKeystore.AddCScript(redeemScript);
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();
const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain); const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);
@ -484,10 +525,8 @@ Value sendrawtransaction(const Array& params, bool fHelp)
"sendrawtransaction <hex string>\n" "sendrawtransaction <hex string>\n"
"Submits raw transaction (serialized, hex-encoded) to local node and network."); "Submits raw transaction (serialized, hex-encoded) to local node and network.");
RPCTypeCheck(params, list_of(str_type));
// parse hex string from parameter // 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); CDataStream ssData(txData, SER_NETWORK, PROTOCOL_VERSION);
CTransaction tx; CTransaction tx;

View File

@ -702,22 +702,13 @@ Value sendmany(const Array& params, bool fHelp)
return wtx.GetHash().GetHex(); return wtx.GetHash().GetHex();
} }
Value addmultisigaddress(const Array& params, bool fHelp) //
// Used by addmultisigaddress / createmultisig:
//
static CScript _createmultisig(const Array& params)
{ {
if (fHelp || params.size() < 2 || params.size() > 3)
{
string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
"Add a nrequired-to-sign multisignature address to the wallet\"\n"
"each key is a Bitcoin address or hex-encoded public key\n"
"If [account] is specified, assign address to [account].";
throw runtime_error(msg);
}
int nRequired = params[0].get_int(); int nRequired = params[0].get_int();
const Array& keys = params[1].get_array(); const Array& keys = params[1].get_array();
string strAccount;
if (params.size() > 2)
strAccount = AccountFromValue(params[2]);
// Gather public keys // Gather public keys
if (nRequired < 1) if (nRequired < 1)
@ -760,10 +751,28 @@ Value addmultisigaddress(const Array& params, bool fHelp)
throw runtime_error(" Invalid public key: "+ks); throw runtime_error(" Invalid public key: "+ks);
} }
} }
CScript result;
result.SetMultisig(nRequired, pubkeys);
return result;
}
Value addmultisigaddress(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 2 || params.size() > 3)
{
string msg = "addmultisigaddress <nrequired> <'[\"key\",\"key\"]'> [account]\n"
"Add a nrequired-to-sign multisignature address to the wallet\"\n"
"each key is a Bitcoin address or hex-encoded public key\n"
"If [account] is specified, assign address to [account].";
throw runtime_error(msg);
}
string strAccount;
if (params.size() > 2)
strAccount = AccountFromValue(params[2]);
// Construct using pay-to-script-hash: // Construct using pay-to-script-hash:
CScript inner; CScript inner = _createmultisig(params);
inner.SetMultisig(nRequired, pubkeys);
CScriptID innerID = inner.GetID(); CScriptID innerID = inner.GetID();
pwalletMain->AddCScript(inner); pwalletMain->AddCScript(inner);
@ -771,6 +780,30 @@ Value addmultisigaddress(const Array& params, bool fHelp)
return CBitcoinAddress(innerID).ToString(); return CBitcoinAddress(innerID).ToString();
} }
Value createmultisig(const Array& params, bool fHelp)
{
if (fHelp || params.size() < 2 || params.size() > 2)
{
string msg = "createmultisig <nrequired> <'[\"key\",\"key\"]'>\n"
"Creates a multi-signature address and returns a json object\n"
"with keys:\n"
"address : bitcoin address\n"
"redeemScript : hex-encoded redemption script";
throw runtime_error(msg);
}
// Construct using pay-to-script-hash:
CScript inner = _createmultisig(params);
CScriptID innerID = inner.GetID();
CBitcoinAddress address(innerID);
Object result;
result.push_back(Pair("address", address.ToString()));
result.push_back(Pair("redeemScript", HexStr(inner.begin(), inner.end())));
return result;
}
struct tallyitem struct tallyitem
{ {

View File

@ -1,5 +1,6 @@
#include <boost/test/unit_test.hpp> #include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp> #include <boost/foreach.hpp>
#include <boost/test/unit_test.hpp>
#include "base58.h" #include "base58.h"
#include "util.h" #include "util.h"
@ -22,14 +23,7 @@ createArgs(int nRequired, const char* address1=NULL, const char* address2=NULL)
return result; return result;
} }
// This can be removed this when addmultisigaddress is enabled on main net: BOOST_AUTO_TEST_CASE(rpc_addmultisig)
struct TestNetFixture
{
TestNetFixture() { fTestNet = true; }
~TestNetFixture() { fTestNet = false; }
};
BOOST_FIXTURE_TEST_CASE(rpc_addmultisig, TestNetFixture)
{ {
rpcfn_type addmultisig = tableRPC["addmultisigaddress"]->actor; rpcfn_type addmultisig = tableRPC["addmultisigaddress"]->actor;
@ -66,4 +60,91 @@ BOOST_FIXTURE_TEST_CASE(rpc_addmultisig, TestNetFixture)
BOOST_CHECK_THROW(addmultisig(createArgs(2, short2.c_str()), false), runtime_error); BOOST_CHECK_THROW(addmultisig(createArgs(2, short2.c_str()), false), runtime_error);
} }
static Value CallRPC(string args)
{
vector<string> vArgs;
boost::split(vArgs, args, boost::is_any_of(" \t"));
string strMethod = vArgs[0];
vArgs.erase(vArgs.begin());
Array params = RPCConvertValues(strMethod, vArgs);
rpcfn_type method = tableRPC[strMethod]->actor;
try {
Value result = (*method)(params, false);
return result;
}
catch (Object& objError)
{
throw runtime_error(find_value(objError, "message").get_str());
}
}
BOOST_AUTO_TEST_CASE(rpc_rawparams)
{
// Test raw transaction API argument handling
Value r;
BOOST_CHECK_THROW(CallRPC("getrawtransaction"), runtime_error);
BOOST_CHECK_THROW(CallRPC("getrawtransaction not_hex"), runtime_error);
BOOST_CHECK_THROW(CallRPC("getrawtransaction a3b807410df0b60fcb9736768df5823938b2f838694939ba45f3c0a1bff150ed not_int"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC("listunspent"));
BOOST_CHECK_THROW(CallRPC("listunspent string"), runtime_error);
BOOST_CHECK_THROW(CallRPC("listunspent 0 string"), runtime_error);
BOOST_CHECK_THROW(CallRPC("listunspent 0 1 not_array"), runtime_error);
BOOST_CHECK_NO_THROW(r=CallRPC("listunspent 0 1 []"));
BOOST_CHECK_THROW(r=CallRPC("listunspent 0 1 [] extra"), runtime_error);
BOOST_CHECK(r.get_array().empty());
BOOST_CHECK_THROW(CallRPC("createrawtransaction"), runtime_error);
BOOST_CHECK_THROW(CallRPC("createrawtransaction null null"), runtime_error);
BOOST_CHECK_THROW(CallRPC("createrawtransaction not_array"), runtime_error);
BOOST_CHECK_THROW(CallRPC("createrawtransaction [] []"), runtime_error);
BOOST_CHECK_THROW(CallRPC("createrawtransaction {} {}"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC("createrawtransaction [] {}"));
BOOST_CHECK_THROW(CallRPC("createrawtransaction [] {} extra"), runtime_error);
BOOST_CHECK_THROW(CallRPC("decoderawtransaction"), runtime_error);
BOOST_CHECK_THROW(CallRPC("decoderawtransaction null"), runtime_error);
BOOST_CHECK_THROW(CallRPC("decoderawtransaction DEADBEEF"), runtime_error);
string rawtx = "0100000001a15d57094aa7a21a28cb20b59aab8fc7d1149a3bdbcddba9c622e4f5f6a99ece010000006c493046022100f93bb0e7d8db7bd46e40132d1f8242026e045f03a0efe71bbb8e3f475e970d790221009337cd7f1f929f00cc6ff01f03729b069a7c21b59b1736ddfee5db5946c5da8c0121033b9b137ee87d5a812d6f506efdd37f0affa7ffc310711c06c7f3e097c9447c52ffffffff0100e1f505000000001976a9140389035a9225b3839e2bbf32d826a1e222031fd888ac00000000";
BOOST_CHECK_NO_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx));
BOOST_CHECK_EQUAL(find_value(r.get_obj(), "version").get_int(), 1);
BOOST_CHECK_EQUAL(find_value(r.get_obj(), "locktime").get_int(), 0);
BOOST_CHECK_THROW(r = CallRPC(string("decoderawtransaction ")+rawtx+" extra"), runtime_error);
BOOST_CHECK_THROW(CallRPC("signrawtransaction"), runtime_error);
BOOST_CHECK_THROW(CallRPC("signrawtransaction null"), runtime_error);
BOOST_CHECK_THROW(CallRPC("signrawtransaction ff00"), runtime_error);
BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx));
BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx+" null null NONE|ANYONECANPAY"));
BOOST_CHECK_NO_THROW(CallRPC(string("signrawtransaction ")+rawtx+" [] [] NONE|ANYONECANPAY"));
BOOST_CHECK_THROW(CallRPC(string("signrawtransaction ")+rawtx+" null null badenum"), runtime_error);
// Only check failure cases for sendrawtransaction, there's no network to send to...
BOOST_CHECK_THROW(CallRPC("sendrawtransaction"), runtime_error);
BOOST_CHECK_THROW(CallRPC("sendrawtransaction null"), runtime_error);
BOOST_CHECK_THROW(CallRPC("sendrawtransaction DEADBEEF"), runtime_error);
BOOST_CHECK_THROW(CallRPC(string("sendrawtransaction ")+rawtx+" extra"), runtime_error);
}
BOOST_AUTO_TEST_CASE(rpc_rawsign)
{
Value r;
// input is a 1-of-2 multisig (so is output):
string prevout =
"[{\"txid\":\"b4cc287e58f87cdae59417329f710f3ecd75a4ee1d2872b7248f50977c8493f3\","
"\"vout\":1,\"scriptPubKey\":\"a914b10c9df5f7edf436c697f02f1efdba4cf399615187\","
"\"redeemScript\":\"512103debedc17b3df2badbcdd86d5feb4562b86fe182e5998abd8bcd4f122c6155b1b21027e940bb73ab8732bfdf7f9216ecefca5b94d6df834e77e108f68e66f126044c052ae\"}]";
r = CallRPC(string("createrawtransaction ")+prevout+" "+
"{\"3HqAe9LtNBjnsfM4CyYaWTnvCaUYT7v4oZ\":11}");
string notsigned = r.get_str();
string privkey1 = "\"KzsXybp9jX64P5ekX1KUxRQ79Jht9uzW7LorgwE65i5rWACL6LQe\"";
string privkey2 = "\"Kyhdf5LuKTRx4ge69ybABsiUAWjVRK4XGxAKk2FQLp2HjGMy87Z4\"";
r = CallRPC(string("signrawtransaction ")+notsigned+" "+prevout+" "+"[]");
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == false);
r = CallRPC(string("signrawtransaction ")+notsigned+" "+prevout+" "+"["+privkey1+","+privkey2+"]");
BOOST_CHECK(find_value(r.get_obj(), "complete").get_bool() == true);
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()