Add redeemScript to listunspent output and signrawtransaction input
signrawtransaction was unable to sign pay-to-script-hash inputs when given the list of private keys to use. With this commit you can provide the p2sh redemption script in the list of inputs.
This commit is contained in:
parent
2d43f88e1f
commit
03346a61b1
|
@ -226,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);
|
||||||
|
@ -321,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"
|
||||||
|
@ -377,44 +388,6 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add previous txouts given in the RPC call:
|
|
||||||
if (params.size() > 1 && params[1].type() != null_type)
|
|
||||||
{
|
|
||||||
Array prevTxs = params[1].get_array();
|
|
||||||
BOOST_FOREACH(Value& p, prevTxs)
|
|
||||||
{
|
|
||||||
if (p.type() != obj_type)
|
|
||||||
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));
|
|
||||||
|
|
||||||
uint256 txid = ParseHashO(prevOut, "txid");
|
|
||||||
|
|
||||||
int nOut = find_value(prevOut, "vout").get_int();
|
|
||||||
if (nOut < 0)
|
|
||||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
|
|
||||||
|
|
||||||
vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
|
|
||||||
CScript scriptPubKey(pkData.begin(), pkData.end());
|
|
||||||
|
|
||||||
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 + coins.vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
|
|
||||||
scriptPubKey.ToString();
|
|
||||||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, err);
|
|
||||||
}
|
|
||||||
// what todo if txid is known, but the actual output isn't?
|
|
||||||
}
|
|
||||||
coins.vout[nOut].scriptPubKey = scriptPubKey;
|
|
||||||
coins.vout[nOut].nValue = 0; // we don't know the actual output value
|
|
||||||
view.SetCoins(txid, coins);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fGivenKeys = false;
|
bool fGivenKeys = false;
|
||||||
CBasicKeyStore tempKeystore;
|
CBasicKeyStore tempKeystore;
|
||||||
if (params.size() > 2 && params[2].type() != null_type)
|
if (params.size() > 2 && params[2].type() != null_type)
|
||||||
|
@ -437,6 +410,56 @@ Value signrawtransaction(const Array& params, bool fHelp)
|
||||||
else
|
else
|
||||||
EnsureWalletIsUnlocked();
|
EnsureWalletIsUnlocked();
|
||||||
|
|
||||||
|
// Add previous txouts given in the RPC call:
|
||||||
|
if (params.size() > 1 && params[1].type() != null_type)
|
||||||
|
{
|
||||||
|
Array prevTxs = params[1].get_array();
|
||||||
|
BOOST_FOREACH(Value& p, prevTxs)
|
||||||
|
{
|
||||||
|
if (p.type() != obj_type)
|
||||||
|
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)("redeemScript",str_type));
|
||||||
|
|
||||||
|
uint256 txid = ParseHashO(prevOut, "txid");
|
||||||
|
|
||||||
|
int nOut = find_value(prevOut, "vout").get_int();
|
||||||
|
if (nOut < 0)
|
||||||
|
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "vout must be positive");
|
||||||
|
|
||||||
|
vector<unsigned char> pkData(ParseHexO(prevOut, "scriptPubKey"));
|
||||||
|
CScript scriptPubKey(pkData.begin(), pkData.end());
|
||||||
|
|
||||||
|
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 + coins.vout[nOut].scriptPubKey.ToString() + "\nvs:\n"+
|
||||||
|
scriptPubKey.ToString();
|
||||||
|
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:
|
||||||
|
Value v = find_value(prevOut, "redeemScript");
|
||||||
|
if (fGivenKeys && scriptPubKey.IsPayToScriptHash() && !(v == Value::null))
|
||||||
|
{
|
||||||
|
vector<unsigned char> rsData(ParseHexV(v, "redeemScript"));
|
||||||
|
CScript redeemScript(rsData.begin(), rsData.end());
|
||||||
|
tempKeystore.AddCScript(redeemScript);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);
|
const CKeyStore& keystore = (fGivenKeys ? tempKeystore : *pwalletMain);
|
||||||
|
|
||||||
int nHashType = SIGHASH_ALL;
|
int nHashType = SIGHASH_ALL;
|
||||||
|
|
|
@ -127,4 +127,24 @@ BOOST_AUTO_TEST_CASE(rpc_rawparams)
|
||||||
BOOST_CHECK_THROW(CallRPC("sendrawtransaction DEADBEEF"), runtime_error);
|
BOOST_CHECK_THROW(CallRPC("sendrawtransaction DEADBEEF"), runtime_error);
|
||||||
BOOST_CHECK_THROW(CallRPC(string("sendrawtransaction ")+rawtx+" extra"), 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()
|
||||||
|
|
Loading…
Reference in New Issue