@ -2682,8 +2682,8 @@ UniValue bumpfee(const JSONRPCRequest& request)
" By default, the new fee will be calculated automatically using estimatefee. \n "
" By default, the new fee will be calculated automatically using estimatefee. \n "
" The user can specify a confirmation target for estimatefee. \n "
" The user can specify a confirmation target for estimatefee. \n "
" Alternatively, the user can specify totalFee, or use RPC setpaytxfee to set a higher fee rate. \n "
" Alternatively, the user can specify totalFee, or use RPC setpaytxfee to set a higher fee rate. \n "
" At a minimum, the new fee rate must be high enough to pay a new relay fee (relay fee amount returned \n "
" At a minimum, the new fee rate must be high enough to pay an additional new relay fee (incrementalfee \n "
" by getnetworkinfo RPC ) and to enter the node's mempool. \n "
" returned by getnetworkinfo) to enter the node's mempool.\n "
" \n Arguments: \n "
" \n Arguments: \n "
" 1. txid (string, required) The txid to be bumped \n "
" 1. txid (string, required) The txid to be bumped \n "
" 2. options (object, optional) \n "
" 2. options (object, optional) \n "
@ -2704,8 +2704,8 @@ UniValue bumpfee(const JSONRPCRequest& request)
" \n Result: \n "
" \n Result: \n "
" { \n "
" { \n "
" \" txid \" : \" value \" , (string) The id of the new transaction \n "
" \" txid \" : \" value \" , (string) The id of the new transaction \n "
" \" old fee \" : n, (numeric) Fee of the replaced transaction \n "
" \" orig fee \" : n, (numeric) Fee of the replaced transaction \n "
" \" fee \" : n, (numeric) Fee of the new transaction \n "
" \" fee \" : n, (numeric) Fee of the new transaction \n "
" } \n "
" } \n "
" \n Examples: \n "
" \n Examples: \n "
" \n Bump the fee, get the new transaction \' s txid \n " +
" \n Bump the fee, get the new transaction \' s txid \n " +
@ -2769,6 +2769,10 @@ UniValue bumpfee(const JSONRPCRequest& request)
throw JSONRPCError ( RPC_MISC_ERROR , " Transaction does not have a change output " ) ;
throw JSONRPCError ( RPC_MISC_ERROR , " Transaction does not have a change output " ) ;
}
}
// signature sizes can vary by a byte, so add 1 for each input when calculating the new fee
int64_t txSize = GetVirtualTransactionSize ( * ( wtx . tx ) ) ;
const int64_t maxNewTxSize = txSize + wtx . tx - > vin . size ( ) ;
// optional parameters
// optional parameters
bool specifiedConfirmTarget = false ;
bool specifiedConfirmTarget = false ;
int newConfirmTarget = nTxConfirmTarget ;
int newConfirmTarget = nTxConfirmTarget ;
@ -2794,10 +2798,11 @@ UniValue bumpfee(const JSONRPCRequest& request)
}
}
} else if ( options . exists ( " totalFee " ) ) {
} else if ( options . exists ( " totalFee " ) ) {
totalFee = options [ " totalFee " ] . get_int64 ( ) ;
totalFee = options [ " totalFee " ] . get_int64 ( ) ;
if ( totalFee < = 0 ) {
CAmount requiredFee = CWallet : : GetRequiredFee ( maxNewTxSize ) ;
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid totalFee (cannot be <= 0) " ) ;
if ( totalFee < requiredFee ) {
} else if ( totalFee > maxTxFee ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER ,
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid totalFee (cannot be higher than maxTxFee) " ) ;
strprintf ( " Insufficient totalFee (cannot be less than required fee %s) " ,
FormatMoney ( requiredFee ) ) ) ;
}
}
}
}
@ -2806,42 +2811,53 @@ UniValue bumpfee(const JSONRPCRequest& request)
}
}
}
}
// signature sizes can vary by a byte, so add 1 for each input when calculating the new fee
int64_t txSize = GetVirtualTransactionSize ( * ( wtx . tx ) ) ;
const int64_t maxNewTxSize = txSize + wtx . tx - > vin . size ( ) ;
// calculate the old fee and fee-rate
// calculate the old fee and fee-rate
CAmount nOldFee = wtx . GetDebit ( ISMINE_SPENDABLE ) - wtx . tx - > GetValueOut ( ) ;
CAmount nOldFee = wtx . GetDebit ( ISMINE_SPENDABLE ) - wtx . tx - > GetValueOut ( ) ;
CFeeRate nOldFeeRate ( nOldFee , txSize ) ;
CFeeRate nOldFeeRate ( nOldFee , txSize ) ;
CAmount nNewFee ;
CAmount nNewFee ;
CFeeRate nNewFeeRate ;
CFeeRate nNewFeeRate ;
// The wallet uses a conservative WALLET_INCREMENTAL_RELAY_FEE value to
// future proof against changes to network wide policy for incremental relay
// fee that our node may not be aware of.
CFeeRate walletIncrementalRelayFee = CFeeRate ( WALLET_INCREMENTAL_RELAY_FEE ) ;
if ( : : incrementalRelayFee > walletIncrementalRelayFee ) {
walletIncrementalRelayFee = : : incrementalRelayFee ;
}
if ( totalFee > 0 ) {
if ( totalFee > 0 ) {
CAmount minTotalFee = nOldFeeRate . GetFee ( maxNewTxSize ) + minRelayTxFee . GetFee ( maxNewTxSize ) ;
CAmount minTotalFee = nOldFeeRate . GetFee ( maxNewTxSize ) + : : incrementalRelay Fee. GetFee ( maxNewTxSize ) ;
if ( totalFee < minTotalFee ) {
if ( totalFee < minTotalFee ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Invalid totalFee, must be at least %s (oldFee %s + relayFee %s) " , FormatMoney(minTotalFee), nOldFeeRate.GetFee(maxNewTxSize), minRelayTxFee.GetFee(maxNewTxSize))) ;
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s) " ,
FormatMoney ( minTotalFee ) , FormatMoney ( nOldFeeRate . GetFee ( maxNewTxSize ) ) , FormatMoney ( : : incrementalRelayFee . GetFee ( maxNewTxSize ) ) ) ) ;
}
}
nNewFee = totalFee ;
nNewFee = totalFee ;
nNewFeeRate = CFeeRate ( totalFee , maxNewTxSize ) ;
nNewFeeRate = CFeeRate ( totalFee , maxNewTxSize ) ;
} else {
} else {
// use the user-defined payTxFee if possible, otherwise use smartfee / fallbackfee
// if user specified a confirm target then don't consider any global payTxFee
if ( ! specifiedConfirmTarget & & payTxFee . GetFeePerK ( ) ! = 0 ) {
if ( specifiedConfirmTarget ) {
nNewFeeRate = payTxFee ;
nNewFee = CWallet : : GetMinimumFee ( maxNewTxSize , newConfirmTarget , mempool , CAmount ( 0 ) ) ;
} else {
nNewFeeRate = mempool . estimateSmartFee ( newConfirmTarget ) ;
}
}
if ( nNewFeeRate . GetFeePerK ( ) = = 0 ) {
// otherwise use the regular wallet logic to select payTxFee or default confirm target
nNewFeeRate = CWallet : : fallbackFee ;
else {
nNewFee = CWallet : : GetMinimumFee ( maxNewTxSize , newConfirmTarget , mempool ) ;
}
}
// new fee rate must be at least old rate + minimum relay rate
nNewFeeRate = CFeeRate ( nNewFee , maxNewTxSize ) ;
if ( nNewFeeRate . GetFeePerK ( ) < nOldFeeRate . GetFeePerK ( ) + : : minRelayTxFee . GetFeePerK ( ) ) {
nNewFeeRate = CFeeRate ( nOldFeeRate . GetFeePerK ( ) + : : minRelayTxFee . GetFeePerK ( ) ) ;
}
nNewFee = nNewFeeRate . GetFee ( maxNewTxSize ) ;
// New fee rate must be at least old rate + minimum incremental relay rate
if ( nNewFeeRate . GetFeePerK ( ) < nOldFeeRate . GetFeePerK ( ) + walletIncrementalRelayFee . GetFeePerK ( ) ) {
nNewFeeRate = CFeeRate ( nOldFeeRate . GetFeePerK ( ) + walletIncrementalRelayFee . GetFeePerK ( ) ) ;
nNewFee = nNewFeeRate . GetFee ( maxNewTxSize ) ;
}
}
}
// Check that in all cases the new fee doesn't violate maxTxFee
if ( nNewFee > maxTxFee ) {
throw JSONRPCError ( RPC_MISC_ERROR ,
strprintf ( " Specified or calculated fee %s is too high (cannot be higher than maxTxFee %s) " ,
FormatMoney ( nNewFee ) , FormatMoney ( maxTxFee ) ) ) ;
}
// check that fee rate is higher than mempool's minimum fee
// check that fee rate is higher than mempool's minimum fee
// (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
// (no point in bumping fee if we know that the new tx won't be accepted to the mempool)
// This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps,
// This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps,
@ -2864,7 +2880,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
// If the output would become dust, discard it (converting the dust to fee)
// If the output would become dust, discard it (converting the dust to fee)
poutput - > nValue - = nDelta ;
poutput - > nValue - = nDelta ;
if ( poutput - > nValue < = poutput - > GetDustThreshold ( : : minRelayTx Fee) ) {
if ( poutput - > nValue < = poutput - > GetDustThreshold ( : : dustRelay Fee) ) {
LogPrint ( " rpc " , " Bumping fee and discarding dust output \n " ) ;
LogPrint ( " rpc " , " Bumping fee and discarding dust output \n " ) ;
nNewFee + = poutput - > nValue ;
nNewFee + = poutput - > nValue ;
tx . vout . erase ( tx . vout . begin ( ) + nOutput ) ;
tx . vout . erase ( tx . vout . begin ( ) + nOutput ) ;
@ -2913,7 +2929,7 @@ UniValue bumpfee(const JSONRPCRequest& request)
UniValue result ( UniValue : : VOBJ ) ;
UniValue result ( UniValue : : VOBJ ) ;
result . push_back ( Pair ( " txid " , wtxBumped . GetHash ( ) . GetHex ( ) ) ) ;
result . push_back ( Pair ( " txid " , wtxBumped . GetHash ( ) . GetHex ( ) ) ) ;
result . push_back ( Pair ( " old fee " , ValueFromAmount ( nOldFee ) ) ) ;
result . push_back ( Pair ( " orig fee " , ValueFromAmount ( nOldFee ) ) ) ;
result . push_back ( Pair ( " fee " , ValueFromAmount ( nNewFee ) ) ) ;
result . push_back ( Pair ( " fee " , ValueFromAmount ( nNewFee ) ) ) ;
return result ;
return result ;