mirror of
https://github.com/kvazar-network/kevacoin.git
synced 2025-01-11 15:48:05 +00:00
Add change options to fundrawtransaction
This commit is contained in:
parent
41e835dd50
commit
af4fe7fd12
@ -177,6 +177,83 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||||||
assert_equal(fee + totalOut, utx['amount']) #compare vin total and totalout+fee
|
assert_equal(fee + totalOut, utx['amount']) #compare vin total and totalout+fee
|
||||||
|
|
||||||
|
|
||||||
|
####################################################
|
||||||
|
# test a fundrawtransaction with an invalid option #
|
||||||
|
####################################################
|
||||||
|
utx = False
|
||||||
|
listunspent = self.nodes[2].listunspent()
|
||||||
|
for aUtx in listunspent:
|
||||||
|
if aUtx['amount'] == 5.0:
|
||||||
|
utx = aUtx
|
||||||
|
break
|
||||||
|
|
||||||
|
assert_equal(utx!=False, True)
|
||||||
|
|
||||||
|
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']} ]
|
||||||
|
outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) }
|
||||||
|
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||||
|
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||||
|
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.nodes[2].fundrawtransaction(rawtx, {'foo': 'bar'})
|
||||||
|
raise AssertionError("Accepted invalid option foo")
|
||||||
|
except JSONRPCException,e:
|
||||||
|
assert("Unexpected key foo" in e.error['message'])
|
||||||
|
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# test a fundrawtransaction with an invalid change address #
|
||||||
|
############################################################
|
||||||
|
utx = False
|
||||||
|
listunspent = self.nodes[2].listunspent()
|
||||||
|
for aUtx in listunspent:
|
||||||
|
if aUtx['amount'] == 5.0:
|
||||||
|
utx = aUtx
|
||||||
|
break
|
||||||
|
|
||||||
|
assert_equal(utx!=False, True)
|
||||||
|
|
||||||
|
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']} ]
|
||||||
|
outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) }
|
||||||
|
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||||
|
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||||
|
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.nodes[2].fundrawtransaction(rawtx, {'changeAddress': 'foobar'})
|
||||||
|
raise AssertionError("Accepted invalid bitcoin address")
|
||||||
|
except JSONRPCException,e:
|
||||||
|
assert("changeAddress must be a valid bitcoin address" in e.error['message'])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
############################################################
|
||||||
|
# test a fundrawtransaction with a provided change address #
|
||||||
|
############################################################
|
||||||
|
utx = False
|
||||||
|
listunspent = self.nodes[2].listunspent()
|
||||||
|
for aUtx in listunspent:
|
||||||
|
if aUtx['amount'] == 5.0:
|
||||||
|
utx = aUtx
|
||||||
|
break
|
||||||
|
|
||||||
|
assert_equal(utx!=False, True)
|
||||||
|
|
||||||
|
inputs = [ {'txid' : utx['txid'], 'vout' : utx['vout']} ]
|
||||||
|
outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) }
|
||||||
|
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||||
|
dec_tx = self.nodes[2].decoderawtransaction(rawtx)
|
||||||
|
assert_equal(utx['txid'], dec_tx['vin'][0]['txid'])
|
||||||
|
|
||||||
|
change = self.nodes[2].getnewaddress()
|
||||||
|
rawtxfund = self.nodes[2].fundrawtransaction(rawtx, {'changeAddress': change, 'changePosition': 0})
|
||||||
|
dec_tx = self.nodes[2].decoderawtransaction(rawtxfund['hex'])
|
||||||
|
out = dec_tx['vout'][0];
|
||||||
|
assert_equal(change, out['scriptPubKey']['addresses'][0])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#########################################################################
|
#########################################################################
|
||||||
# test a fundrawtransaction with a VIN smaller than the required amount #
|
# test a fundrawtransaction with a VIN smaller than the required amount #
|
||||||
#########################################################################
|
#########################################################################
|
||||||
@ -568,7 +645,7 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||||||
outputs = {self.nodes[2].getnewaddress() : watchonly_amount / 2}
|
outputs = {self.nodes[2].getnewaddress() : watchonly_amount / 2}
|
||||||
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
|
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
|
||||||
|
|
||||||
result = self.nodes[3].fundrawtransaction(rawtx, True)
|
result = self.nodes[3].fundrawtransaction(rawtx, {'includeWatching': True })
|
||||||
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
|
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
|
||||||
assert_equal(len(res_dec["vin"]), 1)
|
assert_equal(len(res_dec["vin"]), 1)
|
||||||
assert_equal(res_dec["vin"][0]["txid"], watchonly_txid)
|
assert_equal(res_dec["vin"][0]["txid"], watchonly_txid)
|
||||||
@ -584,6 +661,7 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||||||
outputs = {self.nodes[2].getnewaddress() : watchonly_amount}
|
outputs = {self.nodes[2].getnewaddress() : watchonly_amount}
|
||||||
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
|
rawtx = self.nodes[3].createrawtransaction(inputs, outputs)
|
||||||
|
|
||||||
|
# Backward compatibility test (2nd param is includeWatching)
|
||||||
result = self.nodes[3].fundrawtransaction(rawtx, True)
|
result = self.nodes[3].fundrawtransaction(rawtx, True)
|
||||||
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
|
res_dec = self.nodes[0].decoderawtransaction(result["hex"])
|
||||||
assert_equal(len(res_dec["vin"]), 2)
|
assert_equal(len(res_dec["vin"]), 2)
|
||||||
|
@ -2437,7 +2437,7 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
|
|||||||
|
|
||||||
if (fHelp || params.size() < 1 || params.size() > 2)
|
if (fHelp || params.size() < 1 || params.size() > 2)
|
||||||
throw runtime_error(
|
throw runtime_error(
|
||||||
"fundrawtransaction \"hexstring\" includeWatching\n"
|
"fundrawtransaction \"hexstring\" ( options )\n"
|
||||||
"\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
|
"\nAdd inputs to a transaction until it has enough in value to meet its out value.\n"
|
||||||
"This will not modify existing inputs, and will add one change output to the outputs.\n"
|
"This will not modify existing inputs, and will add one change output to the outputs.\n"
|
||||||
"Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
|
"Note that inputs which were signed may need to be resigned after completion since in/outputs have been added.\n"
|
||||||
@ -2447,8 +2447,14 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
|
|||||||
"in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
|
"in the wallet using importaddress or addmultisigaddress (to calculate fees).\n"
|
||||||
"Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n"
|
"Only pay-to-pubkey, multisig, and P2SH versions thereof are currently supported for watch-only\n"
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
"1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
|
"1. \"hexstring\" (string, required) The hex string of the raw transaction\n"
|
||||||
"2. includeWatching (boolean, optional, default false) Also select inputs which are watch only\n"
|
"2. options (object, optional)\n"
|
||||||
|
" {\n"
|
||||||
|
" \"changeAddress\" (string, optional, default pool address) The bitcoin address to receive the change\n"
|
||||||
|
" \"changePosition\" (numeric, optional, default random) The index of the change output\n"
|
||||||
|
" \"includeWatching\" (boolean, optional, default false) Also select inputs which are watch only\n"
|
||||||
|
" }\n"
|
||||||
|
" for backward compatibility: passing in a true instzead of an object will result in {\"includeWatching\":true}\n"
|
||||||
"\nResult:\n"
|
"\nResult:\n"
|
||||||
"{\n"
|
"{\n"
|
||||||
" \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
|
" \"hex\": \"value\", (string) The resulting raw transaction (hex-encoded string)\n"
|
||||||
@ -2467,7 +2473,40 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
|
|||||||
+ HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
|
+ HelpExampleCli("sendrawtransaction", "\"signedtransactionhex\"")
|
||||||
);
|
);
|
||||||
|
|
||||||
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VBOOL));
|
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR));
|
||||||
|
|
||||||
|
CTxDestination changeAddress = CNoDestination();
|
||||||
|
int changePosition = -1;
|
||||||
|
bool includeWatching = false;
|
||||||
|
|
||||||
|
if (params.size() > 1) {
|
||||||
|
if (params[1].type() == UniValue::VBOOL) {
|
||||||
|
// backward compatibility bool only fallback
|
||||||
|
includeWatching = params[1].get_bool();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
RPCTypeCheck(params, boost::assign::list_of(UniValue::VSTR)(UniValue::VOBJ));
|
||||||
|
|
||||||
|
UniValue options = params[1];
|
||||||
|
|
||||||
|
RPCTypeCheckObj(options, boost::assign::map_list_of("changeAddress", UniValue::VSTR)("changePosition", UniValue::VNUM)("includeWatching", UniValue::VBOOL), true, true);
|
||||||
|
|
||||||
|
if (options.exists("changeAddress")) {
|
||||||
|
CBitcoinAddress address(options["changeAddress"].get_str());
|
||||||
|
|
||||||
|
if (!address.IsValid())
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "changeAddress must be a valid bitcoin address");
|
||||||
|
|
||||||
|
changeAddress = address.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.exists("changePosition"))
|
||||||
|
changePosition = options["changePosition"].get_int();
|
||||||
|
|
||||||
|
if (options.exists("includeWatching"))
|
||||||
|
includeWatching = options["includeWatching"].get_bool();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// parse hex string from parameter
|
// parse hex string from parameter
|
||||||
CTransaction origTx;
|
CTransaction origTx;
|
||||||
@ -2477,20 +2516,19 @@ UniValue fundrawtransaction(const UniValue& params, bool fHelp)
|
|||||||
if (origTx.vout.size() == 0)
|
if (origTx.vout.size() == 0)
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "TX must have at least one output");
|
||||||
|
|
||||||
bool includeWatching = false;
|
if (changePosition != -1 && (changePosition < 0 || changePosition > origTx.vout.size()))
|
||||||
if (params.size() > 1)
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "changePosition out of bounds");
|
||||||
includeWatching = params[1].get_bool();
|
|
||||||
|
|
||||||
CMutableTransaction tx(origTx);
|
CMutableTransaction tx(origTx);
|
||||||
CAmount nFee;
|
CAmount nFee;
|
||||||
string strFailReason;
|
string strFailReason;
|
||||||
int nChangePos = -1;
|
|
||||||
if(!pwalletMain->FundTransaction(tx, nFee, nChangePos, strFailReason, includeWatching))
|
if(!pwalletMain->FundTransaction(tx, nFee, changePosition, strFailReason, includeWatching, changeAddress))
|
||||||
throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason);
|
throw JSONRPCError(RPC_INTERNAL_ERROR, strFailReason);
|
||||||
|
|
||||||
UniValue result(UniValue::VOBJ);
|
UniValue result(UniValue::VOBJ);
|
||||||
result.push_back(Pair("hex", EncodeHexTx(tx)));
|
result.push_back(Pair("hex", EncodeHexTx(tx)));
|
||||||
result.push_back(Pair("changepos", nChangePos));
|
result.push_back(Pair("changepos", changePosition));
|
||||||
result.push_back(Pair("fee", ValueFromAmount(nFee)));
|
result.push_back(Pair("fee", ValueFromAmount(nFee)));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -1932,7 +1932,7 @@ bool CWallet::SelectCoins(const vector<COutput>& vAvailableCoins, const CAmount&
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nChangePosRet, std::string& strFailReason, bool includeWatching)
|
bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, const CTxDestination& destChange)
|
||||||
{
|
{
|
||||||
vector<CRecipient> vecSend;
|
vector<CRecipient> vecSend;
|
||||||
|
|
||||||
@ -1944,6 +1944,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nC
|
|||||||
}
|
}
|
||||||
|
|
||||||
CCoinControl coinControl;
|
CCoinControl coinControl;
|
||||||
|
coinControl.destChange = destChange;
|
||||||
coinControl.fAllowOtherInputs = true;
|
coinControl.fAllowOtherInputs = true;
|
||||||
coinControl.fAllowWatchOnly = includeWatching;
|
coinControl.fAllowWatchOnly = includeWatching;
|
||||||
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
BOOST_FOREACH(const CTxIn& txin, tx.vin)
|
||||||
@ -1951,11 +1952,11 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nC
|
|||||||
|
|
||||||
CReserveKey reservekey(this);
|
CReserveKey reservekey(this);
|
||||||
CWalletTx wtx;
|
CWalletTx wtx;
|
||||||
if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosRet, strFailReason, &coinControl, false))
|
if (!CreateTransaction(vecSend, wtx, reservekey, nFeeRet, nChangePosInOut, strFailReason, &coinControl, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (nChangePosRet != -1)
|
if (nChangePosInOut != -1)
|
||||||
tx.vout.insert(tx.vout.begin() + nChangePosRet, wtx.vout[nChangePosRet]);
|
tx.vout.insert(tx.vout.begin() + nChangePosInOut, wtx.vout[nChangePosInOut]);
|
||||||
|
|
||||||
// Add new txins (keeping original txin scriptSig/order)
|
// Add new txins (keeping original txin scriptSig/order)
|
||||||
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
|
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
|
||||||
@ -1968,9 +1969,10 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount &nFeeRet, int& nC
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
|
bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet,
|
||||||
int& nChangePosRet, std::string& strFailReason, const CCoinControl* coinControl, bool sign)
|
int& nChangePosInOut, std::string& strFailReason, const CCoinControl* coinControl, bool sign)
|
||||||
{
|
{
|
||||||
CAmount nValue = 0;
|
CAmount nValue = 0;
|
||||||
|
int nChangePosRequest = nChangePosInOut;
|
||||||
unsigned int nSubtractFeeFromAmount = 0;
|
unsigned int nSubtractFeeFromAmount = 0;
|
||||||
BOOST_FOREACH (const CRecipient& recipient, vecSend)
|
BOOST_FOREACH (const CRecipient& recipient, vecSend)
|
||||||
{
|
{
|
||||||
@ -2036,10 +2038,10 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
|
|||||||
// Start with no fee and loop until there is enough fee
|
// Start with no fee and loop until there is enough fee
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
|
nChangePosInOut = nChangePosRequest;
|
||||||
txNew.vin.clear();
|
txNew.vin.clear();
|
||||||
txNew.vout.clear();
|
txNew.vout.clear();
|
||||||
wtxNew.fFromMe = true;
|
wtxNew.fFromMe = true;
|
||||||
nChangePosRet = -1;
|
|
||||||
bool fFirst = true;
|
bool fFirst = true;
|
||||||
|
|
||||||
CAmount nValueToSelect = nValue;
|
CAmount nValueToSelect = nValue;
|
||||||
@ -2159,14 +2161,24 @@ bool CWallet::CreateTransaction(const vector<CRecipient>& vecSend, CWalletTx& wt
|
|||||||
// add the dust to the fee.
|
// add the dust to the fee.
|
||||||
if (newTxOut.IsDust(::minRelayTxFee))
|
if (newTxOut.IsDust(::minRelayTxFee))
|
||||||
{
|
{
|
||||||
|
nChangePosInOut = -1;
|
||||||
nFeeRet += nChange;
|
nFeeRet += nChange;
|
||||||
reservekey.ReturnKey();
|
reservekey.ReturnKey();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Insert change txn at random position:
|
if (nChangePosInOut == -1)
|
||||||
nChangePosRet = GetRandInt(txNew.vout.size()+1);
|
{
|
||||||
vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosRet;
|
// Insert change txn at random position:
|
||||||
|
nChangePosInOut = GetRandInt(txNew.vout.size()+1);
|
||||||
|
}
|
||||||
|
else if (nChangePosInOut > txNew.vout.size())
|
||||||
|
{
|
||||||
|
strFailReason = _("Change index out of range");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
vector<CTxOut>::iterator position = txNew.vout.begin()+nChangePosInOut;
|
||||||
txNew.vout.insert(position, newTxOut);
|
txNew.vout.insert(position, newTxOut);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2842,13 +2854,13 @@ void CWallet::GetScriptForMining(boost::shared_ptr<CReserveScript> &script)
|
|||||||
script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
|
script->reserveScript = CScript() << ToByteVector(pubkey) << OP_CHECKSIG;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWallet::LockCoin(COutPoint& output)
|
void CWallet::LockCoin(const COutPoint& output)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_wallet); // setLockedCoins
|
AssertLockHeld(cs_wallet); // setLockedCoins
|
||||||
setLockedCoins.insert(output);
|
setLockedCoins.insert(output);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CWallet::UnlockCoin(COutPoint& output)
|
void CWallet::UnlockCoin(const COutPoint& output)
|
||||||
{
|
{
|
||||||
AssertLockHeld(cs_wallet); // setLockedCoins
|
AssertLockHeld(cs_wallet); // setLockedCoins
|
||||||
setLockedCoins.erase(output);
|
setLockedCoins.erase(output);
|
||||||
|
@ -739,13 +739,14 @@ public:
|
|||||||
* Insert additional inputs into the transaction by
|
* Insert additional inputs into the transaction by
|
||||||
* calling CreateTransaction();
|
* calling CreateTransaction();
|
||||||
*/
|
*/
|
||||||
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosRet, std::string& strFailReason, bool includeWatching);
|
bool FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nChangePosInOut, std::string& strFailReason, bool includeWatching, const CTxDestination& destChange = CNoDestination());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new transaction paying the recipients with a set of coins
|
* Create a new transaction paying the recipients with a set of coins
|
||||||
* selected by SelectCoins(); Also create the change output, when needed
|
* selected by SelectCoins(); Also create the change output, when needed
|
||||||
|
* @note passing nChangePosInOut as -1 will result in setting a random position
|
||||||
*/
|
*/
|
||||||
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosRet,
|
bool CreateTransaction(const std::vector<CRecipient>& vecSend, CWalletTx& wtxNew, CReserveKey& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
|
||||||
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
|
std::string& strFailReason, const CCoinControl *coinControl = NULL, bool sign = true);
|
||||||
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
|
bool CommitTransaction(CWalletTx& wtxNew, CReserveKey& reservekey);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user