Browse Source

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.
0.8
Gavin Andresen 12 years ago
parent
commit
03346a61b1
  1. 69
      src/rpcrawtransaction.cpp
  2. 20
      src/test/rpc_tests.cpp

69
src/rpcrawtransaction.cpp

@ -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,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)
{ {
@ -388,7 +421,7 @@ 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));
uint256 txid = ParseHashO(prevOut, "txid"); uint256 txid = ParseHashO(prevOut, "txid");
@ -409,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);

20
src/test/rpc_tests.cpp

@ -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…
Cancel
Save