2019-05-06 14:05:28 -04:00
// Copyright (c) 2017-2019 The Bitcoin Core developers
2017-03-03 12:15:47 -03:00
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2017-07-26 10:23:01 -04:00
# include <interfaces/chain.h>
2017-11-09 21:57:53 -03:00
# include <wallet/coincontrol.h>
# include <wallet/feebumper.h>
# include <wallet/fees.h>
# include <wallet/wallet.h>
# include <policy/fees.h>
# include <policy/policy.h>
2018-10-22 19:51:11 -03:00
# include <util/moneystr.h>
2019-04-02 18:03:37 -03:00
# include <util/rbf.h>
2018-10-22 19:51:11 -03:00
# include <util/system.h>
2019-04-02 18:03:37 -03:00
# include <util/validation.h>
2017-03-03 12:15:47 -03:00
2017-06-15 10:34:17 -04:00
//! Check whether transaction has descendant in wallet or mempool, or has been
//! mined, or conflicts with a mined transaction. Return a feebumper::Result.
2019-04-29 09:52:01 -04:00
static feebumper : : Result PreconditionChecks ( const CWallet & wallet , const CWalletTx & wtx , std : : vector < std : : string > & errors ) EXCLUSIVE_LOCKS_REQUIRED ( wallet . cs_wallet )
2017-06-15 10:34:17 -04:00
{
2019-10-10 13:12:28 -03:00
if ( wallet . HasWalletSpend ( wtx . GetHash ( ) ) ) {
2017-09-20 17:04:05 -03:00
errors . push_back ( " Transaction has descendants in the wallet " ) ;
2017-06-15 10:34:17 -04:00
return feebumper : : Result : : INVALID_PARAMETER ;
2017-05-11 04:34:39 -03:00
}
{
2019-10-10 13:12:28 -03:00
if ( wallet . chain ( ) . hasDescendantsInMempool ( wtx . GetHash ( ) ) ) {
2017-09-20 17:04:05 -03:00
errors . push_back ( " Transaction has descendants in the mempool " ) ;
2017-06-15 10:34:17 -04:00
return feebumper : : Result : : INVALID_PARAMETER ;
2017-05-11 04:34:39 -03:00
}
}
2019-04-29 09:52:01 -04:00
if ( wtx . GetDepthInMainChain ( ) ! = 0 ) {
2017-09-20 17:04:05 -03:00
errors . push_back ( " Transaction has been mined, or is conflicted with a mined transaction " ) ;
2017-06-15 10:34:17 -04:00
return feebumper : : Result : : WALLET_ERROR ;
2017-05-11 04:34:39 -03:00
}
2018-01-23 15:57:10 -03:00
if ( ! SignalsOptInRBF ( * wtx . tx ) ) {
errors . push_back ( " Transaction is not BIP 125 replaceable " ) ;
return feebumper : : Result : : WALLET_ERROR ;
}
if ( wtx . mapValue . count ( " replaced_by_txid " ) ) {
errors . push_back ( strprintf ( " Cannot bump transaction %s which was already bumped by transaction %s " , wtx . GetHash ( ) . ToString ( ) , wtx . mapValue . at ( " replaced_by_txid " ) ) ) ;
return feebumper : : Result : : WALLET_ERROR ;
}
// check that original tx consists entirely of our inputs
// if not, we can't bump the fee, because the wallet has no way of knowing the value of the other inputs (thus the fee)
2019-10-10 13:12:28 -03:00
if ( ! wallet . IsAllFromMe ( * wtx . tx , ISMINE_SPENDABLE ) ) {
2018-01-23 15:57:10 -03:00
errors . push_back ( " Transaction contains inputs that don't belong to this wallet " ) ;
return feebumper : : Result : : WALLET_ERROR ;
}
2017-06-15 10:34:17 -04:00
return feebumper : : Result : : OK ;
2017-05-11 04:34:39 -03:00
}
2019-07-29 19:01:42 -04:00
//! Check if the user provided a valid feeRate
2019-10-10 13:12:28 -03:00
static feebumper : : Result CheckFeeRate ( const CWallet & wallet , const CWalletTx & wtx , const CFeeRate & newFeerate , const int64_t maxTxSize , std : : vector < std : : string > & errors ) {
2019-07-29 19:01:42 -04:00
// 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)
// This may occur if the user set FeeRate, TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps,
// in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
// moment earlier. In this case, we report an error to the user, who may adjust the fee.
2019-10-10 13:12:28 -03:00
CFeeRate minMempoolFeeRate = wallet . chain ( ) . mempoolMinFee ( ) ;
2019-07-29 19:01:42 -04:00
if ( newFeerate . GetFeePerK ( ) < minMempoolFeeRate . GetFeePerK ( ) ) {
errors . push_back ( strprintf (
" New fee rate (%s) is lower than the minimum fee rate (%s) to get into the mempool -- " ,
FormatMoney ( newFeerate . GetFeePerK ( ) ) ,
FormatMoney ( minMempoolFeeRate . GetFeePerK ( ) ) ) ) ;
return feebumper : : Result : : WALLET_ERROR ;
}
CAmount new_total_fee = newFeerate . GetFee ( maxTxSize ) ;
2019-10-10 13:12:28 -03:00
CFeeRate incrementalRelayFee = std : : max ( wallet . chain ( ) . relayIncrementalFee ( ) , CFeeRate ( WALLET_INCREMENTAL_RELAY_FEE ) ) ;
2019-07-29 19:01:42 -04:00
// Given old total fee and transaction size, calculate the old feeRate
CAmount old_fee = wtx . GetDebit ( ISMINE_SPENDABLE ) - wtx . tx - > GetValueOut ( ) ;
const int64_t txSize = GetVirtualTransactionSize ( * ( wtx . tx ) ) ;
CFeeRate nOldFeeRate ( old_fee , txSize ) ;
// Min total fee is old fee + relay fee
CAmount minTotalFee = nOldFeeRate . GetFee ( maxTxSize ) + incrementalRelayFee . GetFee ( maxTxSize ) ;
if ( new_total_fee < minTotalFee ) {
errors . push_back ( strprintf ( " Insufficient total fee %s, must be at least %s (oldFee %s + incrementalFee %s) " ,
FormatMoney ( new_total_fee ) , FormatMoney ( minTotalFee ) , FormatMoney ( nOldFeeRate . GetFee ( maxTxSize ) ) , FormatMoney ( incrementalRelayFee . GetFee ( maxTxSize ) ) ) ) ;
return feebumper : : Result : : INVALID_PARAMETER ;
}
2019-10-10 13:12:28 -03:00
CAmount requiredFee = GetRequiredFee ( wallet , maxTxSize ) ;
2019-07-29 19:01:42 -04:00
if ( new_total_fee < requiredFee ) {
errors . push_back ( strprintf ( " Insufficient total fee (cannot be less than required fee %s) " ,
FormatMoney ( requiredFee ) ) ) ;
return feebumper : : Result : : INVALID_PARAMETER ;
}
// Check that in all cases the new fee doesn't violate maxTxFee
2019-10-10 13:12:28 -03:00
const CAmount max_tx_fee = wallet . m_default_max_tx_fee ;
2019-07-29 19:01:42 -04:00
if ( new_total_fee > max_tx_fee ) {
errors . push_back ( strprintf ( " Specified or calculated fee %s is too high (cannot be higher than -maxtxfee %s) " ,
FormatMoney ( new_total_fee ) , FormatMoney ( max_tx_fee ) ) ) ;
return feebumper : : Result : : WALLET_ERROR ;
}
return feebumper : : Result : : OK ;
}
2019-12-01 11:46:44 -03:00
static CFeeRate EstimateFeeRate ( const CWallet & wallet , const CWalletTx & wtx , const CAmount old_fee , CCoinControl & coin_control )
2019-07-29 17:43:43 -04:00
{
// Get the fee rate of the original transaction. This is calculated from
// the tx fee/vsize, so it may have been rounded down. Add 1 satoshi to the
// result.
int64_t txSize = GetVirtualTransactionSize ( * ( wtx . tx ) ) ;
CFeeRate feerate ( old_fee , txSize ) ;
feerate + = CFeeRate ( 1 ) ;
// The node has a configurable incremental relay fee. Increment the fee by
// the minimum of that and the wallet's 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
2019-10-10 13:10:15 -03:00
// aware of. This ensures we're over the required relay fee rate
2019-07-29 17:43:43 -04:00
// (BIP 125 rule 4). The replacement tx will be at least as large as the
// original tx, so the total fee will be greater (BIP 125 rule 3)
2019-10-10 13:12:28 -03:00
CFeeRate node_incremental_relay_fee = wallet . chain ( ) . relayIncrementalFee ( ) ;
2019-07-29 17:43:43 -04:00
CFeeRate wallet_incremental_relay_fee = CFeeRate ( WALLET_INCREMENTAL_RELAY_FEE ) ;
feerate + = std : : max ( node_incremental_relay_fee , wallet_incremental_relay_fee ) ;
// Fee rate must also be at least the wallet's GetMinimumFeeRate
2019-10-10 13:12:28 -03:00
CFeeRate min_feerate ( GetMinimumFeeRate ( wallet , coin_control , /* feeCalc */ nullptr ) ) ;
2019-07-29 17:43:43 -04:00
// Set the required fee rate for the replacement transaction in coin control.
return std : : max ( feerate , min_feerate ) ;
}
2017-06-15 10:34:17 -04:00
namespace feebumper {
2019-10-10 13:12:28 -03:00
bool TransactionCanBeBumped ( const CWallet & wallet , const uint256 & txid )
2017-03-03 12:15:47 -03:00
{
2019-10-10 13:12:28 -03:00
auto locked_chain = wallet . chain ( ) . lock ( ) ;
LOCK ( wallet . cs_wallet ) ;
const CWalletTx * wtx = wallet . GetWalletTx ( txid ) ;
2018-01-29 20:25:35 -03:00
if ( wtx = = nullptr ) return false ;
std : : vector < std : : string > errors_dummy ;
2019-04-29 09:52:01 -04:00
feebumper : : Result res = PreconditionChecks ( wallet , * wtx , errors_dummy ) ;
2018-01-29 20:25:35 -03:00
return res = = feebumper : : Result : : OK ;
2017-06-15 10:34:17 -04:00
}
2019-03-06 18:30:00 -03:00
Result CreateTotalBumpTransaction ( const CWallet * wallet , const uint256 & txid , const CCoinControl & coin_control , CAmount total_fee , std : : vector < std : : string > & errors ,
CAmount & old_fee , CAmount & new_fee , CMutableTransaction & mtx )
2017-06-15 10:34:17 -04:00
{
2019-03-06 18:30:00 -03:00
new_fee = total_fee ;
2017-07-26 10:23:01 -04:00
auto locked_chain = wallet - > chain ( ) . lock ( ) ;
LOCK ( wallet - > cs_wallet ) ;
2017-09-20 17:04:05 -03:00
errors . clear ( ) ;
auto it = wallet - > mapWallet . find ( txid ) ;
if ( it = = wallet - > mapWallet . end ( ) ) {
errors . push_back ( " Invalid or non-wallet transaction id " ) ;
2017-06-15 10:34:17 -04:00
return Result : : INVALID_ADDRESS_OR_KEY ;
2017-03-03 12:15:47 -03:00
}
const CWalletTx & wtx = it - > second ;
2019-04-29 09:52:01 -04:00
Result result = PreconditionChecks ( * wallet , wtx , errors ) ;
2017-06-15 10:34:17 -04:00
if ( result ! = Result : : OK ) {
return result ;
2017-03-03 12:15:47 -03:00
}
// figure out which output was change
// if there was no change output or multiple change outputs, fail
int nOutput = - 1 ;
for ( size_t i = 0 ; i < wtx . tx - > vout . size ( ) ; + + i ) {
2017-09-20 17:04:05 -03:00
if ( wallet - > IsChange ( wtx . tx - > vout [ i ] ) ) {
2017-03-03 12:15:47 -03:00
if ( nOutput ! = - 1 ) {
2017-09-20 17:04:05 -03:00
errors . push_back ( " Transaction has multiple change outputs " ) ;
2017-06-15 10:34:17 -04:00
return Result : : WALLET_ERROR ;
2017-03-03 12:15:47 -03:00
}
nOutput = i ;
}
}
if ( nOutput = = - 1 ) {
2017-09-20 17:04:05 -03:00
errors . push_back ( " Transaction does not have a change output " ) ;
2017-06-15 10:34:17 -04:00
return Result : : WALLET_ERROR ;
2017-03-03 12:15:47 -03:00
}
// Calculate the expected size of the new transaction.
int64_t txSize = GetVirtualTransactionSize ( * ( wtx . tx ) ) ;
2017-09-20 17:04:05 -03:00
const int64_t maxNewTxSize = CalculateMaximumSignedTxSize ( * wtx . tx , wallet ) ;
2017-03-03 12:15:47 -03:00
if ( maxNewTxSize < 0 ) {
2017-09-20 17:04:05 -03:00
errors . push_back ( " Transaction contains inputs that cannot be signed " ) ;
2017-06-15 10:34:17 -04:00
return Result : : INVALID_ADDRESS_OR_KEY ;
2017-03-03 12:15:47 -03:00
}
// calculate the old fee and fee-rate
2017-09-20 17:04:05 -03:00
old_fee = wtx . GetDebit ( ISMINE_SPENDABLE ) - wtx . tx - > GetValueOut ( ) ;
CFeeRate nOldFeeRate ( old_fee , txSize ) ;
2017-03-03 12:15:47 -03:00
// 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.
2019-03-06 18:47:57 -03:00
CFeeRate nodeIncrementalRelayFee = wallet - > chain ( ) . relayIncrementalFee ( ) ;
2017-03-03 12:15:47 -03:00
CFeeRate walletIncrementalRelayFee = CFeeRate ( WALLET_INCREMENTAL_RELAY_FEE ) ;
2019-03-06 18:47:57 -03:00
if ( nodeIncrementalRelayFee > walletIncrementalRelayFee ) {
walletIncrementalRelayFee = nodeIncrementalRelayFee ;
2017-03-03 12:15:47 -03:00
}
2019-03-06 18:30:00 -03:00
CAmount minTotalFee = nOldFeeRate . GetFee ( maxNewTxSize ) + nodeIncrementalRelayFee . GetFee ( maxNewTxSize ) ;
if ( total_fee < minTotalFee ) {
errors . push_back ( strprintf ( " Insufficient totalFee, must be at least %s (oldFee %s + incrementalFee %s) " ,
FormatMoney ( minTotalFee ) , FormatMoney ( nOldFeeRate . GetFee ( maxNewTxSize ) ) , FormatMoney ( nodeIncrementalRelayFee . GetFee ( maxNewTxSize ) ) ) ) ;
return Result : : INVALID_PARAMETER ;
}
CAmount requiredFee = GetRequiredFee ( * wallet , maxNewTxSize ) ;
if ( total_fee < requiredFee ) {
errors . push_back ( strprintf ( " Insufficient totalFee (cannot be less than required fee %s) " ,
FormatMoney ( requiredFee ) ) ) ;
return Result : : INVALID_PARAMETER ;
2017-03-03 12:15:47 -03:00
}
// Check that in all cases the new fee doesn't violate maxTxFee
2019-02-20 15:45:16 -03:00
const CAmount max_tx_fee = wallet - > m_default_max_tx_fee ;
2019-03-06 18:47:57 -03:00
if ( new_fee > max_tx_fee ) {
2019-02-20 15:45:16 -03:00
errors . push_back ( strprintf ( " Specified or calculated fee %s is too high (cannot be higher than -maxtxfee %s) " ,
2019-03-06 18:47:57 -03:00
FormatMoney ( new_fee ) , FormatMoney ( max_tx_fee ) ) ) ;
2017-06-15 10:34:17 -04:00
return Result : : WALLET_ERROR ;
2017-03-03 12:15:47 -03:00
}
// 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)
// This may occur if the user set TotalFee or paytxfee too low, if fallbackfee is too low, or, perhaps,
// in a rare situation where the mempool minimum fee increased significantly since the fee estimation just a
2017-09-20 17:04:05 -03:00
// moment earlier. In this case, we report an error to the user, who may use total_fee to make an adjustment.
2019-03-22 01:42:17 -03:00
CFeeRate minMempoolFeeRate = wallet - > chain ( ) . mempoolMinFee ( ) ;
2019-03-06 18:30:00 -03:00
CFeeRate nNewFeeRate = CFeeRate ( total_fee , maxNewTxSize ) ;
2017-03-03 12:15:47 -03:00
if ( nNewFeeRate . GetFeePerK ( ) < minMempoolFeeRate . GetFeePerK ( ) ) {
2017-09-20 17:04:05 -03:00
errors . push_back ( strprintf (
2017-11-02 15:29:07 -03:00
" New fee rate (%s) is lower than the minimum fee rate (%s) to get into the mempool -- "
2019-03-06 18:30:00 -03:00
" the totalFee value should be at least %s to add transaction " ,
2017-11-02 15:29:07 -03:00
FormatMoney ( nNewFeeRate . GetFeePerK ( ) ) ,
FormatMoney ( minMempoolFeeRate . GetFeePerK ( ) ) ,
2019-03-06 18:30:00 -03:00
FormatMoney ( minMempoolFeeRate . GetFee ( maxNewTxSize ) ) ) ) ;
2017-06-15 10:34:17 -04:00
return Result : : WALLET_ERROR ;
2017-03-03 12:15:47 -03:00
}
// Now modify the output to increase the fee.
// If the output is not large enough to pay the fee, fail.
2017-09-20 17:04:05 -03:00
CAmount nDelta = new_fee - old_fee ;
2017-03-03 12:15:47 -03:00
assert ( nDelta > 0 ) ;
2018-05-04 18:38:49 -03:00
mtx = CMutableTransaction { * wtx . tx } ;
2017-03-03 12:15:47 -03:00
CTxOut * poutput = & ( mtx . vout [ nOutput ] ) ;
if ( poutput - > nValue < nDelta ) {
2017-09-20 17:04:05 -03:00
errors . push_back ( " Change output is too small to bump the fee " ) ;
2017-06-15 10:34:17 -04:00
return Result : : WALLET_ERROR ;
2017-03-03 12:15:47 -03:00
}
// If the output would become dust, discard it (converting the dust to fee)
poutput - > nValue - = nDelta ;
2017-07-28 21:40:29 -04:00
if ( poutput - > nValue < = GetDustThreshold ( * poutput , GetDiscardRate ( * wallet ) ) ) {
2018-06-15 19:02:52 -04:00
wallet - > WalletLogPrintf ( " Bumping fee and discarding dust output \n " ) ;
2017-09-20 17:04:05 -03:00
new_fee + = poutput - > nValue ;
2017-03-03 12:15:47 -03:00
mtx . vout . erase ( mtx . vout . begin ( ) + nOutput ) ;
}
// Mark new tx not replaceable, if requested.
2018-04-07 13:12:46 -03:00
if ( ! coin_control . m_signal_bip125_rbf . get_value_or ( wallet - > m_signal_rbf ) ) {
2017-03-03 12:15:47 -03:00
for ( auto & input : mtx . vin ) {
if ( input . nSequence < 0xfffffffe ) input . nSequence = 0xfffffffe ;
}
}
2019-03-06 18:30:00 -03:00
return Result : : OK ;
}
2019-10-10 13:12:28 -03:00
Result CreateRateBumpTransaction ( CWallet & wallet , const uint256 & txid , const CCoinControl & coin_control , std : : vector < std : : string > & errors ,
2019-03-06 18:30:00 -03:00
CAmount & old_fee , CAmount & new_fee , CMutableTransaction & mtx )
{
// We are going to modify coin control later, copy to re-use
CCoinControl new_coin_control ( coin_control ) ;
2019-10-10 13:12:28 -03:00
auto locked_chain = wallet . chain ( ) . lock ( ) ;
LOCK ( wallet . cs_wallet ) ;
2019-03-06 18:30:00 -03:00
errors . clear ( ) ;
2019-10-10 13:12:28 -03:00
auto it = wallet . mapWallet . find ( txid ) ;
if ( it = = wallet . mapWallet . end ( ) ) {
2019-03-06 18:30:00 -03:00
errors . push_back ( " Invalid or non-wallet transaction id " ) ;
return Result : : INVALID_ADDRESS_OR_KEY ;
}
const CWalletTx & wtx = it - > second ;
2019-04-29 09:52:01 -04:00
Result result = PreconditionChecks ( wallet , wtx , errors ) ;
2019-03-06 18:30:00 -03:00
if ( result ! = Result : : OK ) {
return result ;
}
// Fill in recipients(and preserve a single change key if there is one)
std : : vector < CRecipient > recipients ;
for ( const auto & output : wtx . tx - > vout ) {
2019-10-10 13:12:28 -03:00
if ( ! wallet . IsChange ( output ) ) {
2019-03-06 18:30:00 -03:00
CRecipient recipient = { output . scriptPubKey , output . nValue , false } ;
recipients . push_back ( recipient ) ;
} else {
CTxDestination change_dest ;
ExtractDestination ( output . scriptPubKey , change_dest ) ;
new_coin_control . destChange = change_dest ;
}
}
2019-12-01 11:46:44 -03:00
old_fee = wtx . GetDebit ( ISMINE_SPENDABLE ) - wtx . tx - > GetValueOut ( ) ;
2019-07-29 18:02:02 -04:00
if ( coin_control . m_feerate ) {
// The user provided a feeRate argument.
2019-07-29 19:01:42 -04:00
// We calculate this here to avoid compiler warning on the cs_wallet lock
2019-10-10 13:12:28 -03:00
const int64_t maxTxSize = CalculateMaximumSignedTxSize ( * wtx . tx , & wallet ) ;
2019-10-10 13:10:15 -03:00
Result res = CheckFeeRate ( wallet , wtx , * new_coin_control . m_feerate , maxTxSize , errors ) ;
2019-07-29 19:01:42 -04:00
if ( res ! = Result : : OK ) {
return res ;
}
2019-07-29 18:02:02 -04:00
} else {
// The user did not provide a feeRate argument
2019-12-01 11:46:44 -03:00
new_coin_control . m_feerate = EstimateFeeRate ( wallet , wtx , old_fee , new_coin_control ) ;
2019-07-29 18:02:02 -04:00
}
2019-03-06 18:30:00 -03:00
// Fill in required inputs we are double-spending(all of them)
// N.B.: bip125 doesn't require all the inputs in the replaced transaction to be
// used in the replacement transaction, but it's very important for wallets to make
// sure that happens. If not, it would be possible to bump a transaction A twice to
// A2 and A3 where A2 and A3 don't conflict (or alternatively bump A to A2 and A2
// to A3 where A and A3 don't conflict). If both later get confirmed then the sender
// has accidentally double paid.
for ( const auto & inputs : wtx . tx - > vin ) {
new_coin_control . Select ( COutPoint ( inputs . prevout ) ) ;
}
new_coin_control . fAllowOtherInputs = true ;
// We cannot source new unconfirmed inputs(bip125 rule 2)
new_coin_control . m_min_depth = 1 ;
CTransactionRef tx_new = MakeTransactionRef ( ) ;
CAmount fee_ret ;
int change_pos_in_out = - 1 ; // No requested location for change
std : : string fail_reason ;
2019-10-10 13:12:28 -03:00
if ( ! wallet . CreateTransaction ( * locked_chain , recipients , tx_new , fee_ret , change_pos_in_out , fail_reason , new_coin_control , false ) ) {
2019-03-06 18:30:00 -03:00
errors . push_back ( " Unable to create transaction: " + fail_reason ) ;
return Result : : WALLET_ERROR ;
}
// Write back new fee if successful
new_fee = fee_ret ;
// Write back transaction
mtx = CMutableTransaction ( * tx_new ) ;
// Mark new tx not replaceable, if requested.
2019-10-10 13:12:28 -03:00
if ( ! coin_control . m_signal_bip125_rbf . get_value_or ( wallet . m_signal_rbf ) ) {
2019-03-06 18:30:00 -03:00
for ( auto & input : mtx . vin ) {
if ( input . nSequence < 0xfffffffe ) input . nSequence = 0xfffffffe ;
}
}
2018-01-23 15:57:10 -03:00
2017-06-15 10:34:17 -04:00
return Result : : OK ;
2017-03-03 12:15:47 -03:00
}
2019-10-10 13:12:28 -03:00
bool SignTransaction ( CWallet & wallet , CMutableTransaction & mtx ) {
auto locked_chain = wallet . chain ( ) . lock ( ) ;
LOCK ( wallet . cs_wallet ) ;
return wallet . SignTransaction ( mtx ) ;
2017-03-30 04:26:21 -03:00
}
2019-10-10 13:12:28 -03:00
Result CommitTransaction ( CWallet & wallet , const uint256 & txid , CMutableTransaction & & mtx , std : : vector < std : : string > & errors , uint256 & bumped_txid )
2017-03-03 12:15:47 -03:00
{
2019-10-10 13:12:28 -03:00
auto locked_chain = wallet . chain ( ) . lock ( ) ;
LOCK ( wallet . cs_wallet ) ;
2017-06-15 10:34:17 -04:00
if ( ! errors . empty ( ) ) {
return Result : : MISC_ERROR ;
2017-03-28 04:08:01 -03:00
}
2019-10-10 13:12:28 -03:00
auto it = txid . IsNull ( ) ? wallet . mapWallet . end ( ) : wallet . mapWallet . find ( txid ) ;
if ( it = = wallet . mapWallet . end ( ) ) {
2017-09-20 17:04:05 -03:00
errors . push_back ( " Invalid or non-wallet transaction id " ) ;
2017-06-15 10:34:17 -04:00
return Result : : MISC_ERROR ;
2017-03-03 12:15:47 -03:00
}
2017-08-13 11:04:57 -03:00
CWalletTx & oldWtx = it - > second ;
2017-03-03 12:15:47 -03:00
2017-05-18 03:50:11 -04:00
// make sure the transaction still has no descendants and hasn't been mined in the meantime
2019-04-29 09:52:01 -04:00
Result result = PreconditionChecks ( wallet , oldWtx , errors ) ;
2017-06-15 10:34:17 -04:00
if ( result ! = Result : : OK ) {
return result ;
2017-05-11 04:34:39 -03:00
}
2017-03-03 12:15:47 -03:00
// commit/broadcast the tx
2017-02-02 17:30:03 -03:00
CTransactionRef tx = MakeTransactionRef ( std : : move ( mtx ) ) ;
mapValue_t mapValue = oldWtx . mapValue ;
mapValue [ " replaces_txid " ] = oldWtx . GetHash ( ) . ToString ( ) ;
2019-10-18 10:37:40 -03:00
wallet . CommitTransaction ( tx , std : : move ( mapValue ) , oldWtx . vOrderForm ) ;
2017-03-03 12:15:47 -03:00
// mark the original tx as bumped
2019-10-18 10:37:40 -03:00
bumped_txid = tx - > GetHash ( ) ;
2019-10-10 13:12:28 -03:00
if ( ! wallet . MarkReplaced ( oldWtx . GetHash ( ) , bumped_txid ) ) {
2017-03-03 12:15:47 -03:00
// TODO: see if JSON-RPC has a standard way of returning a response
// along with an exception. It would be good to return information about
// wtxBumped to the caller even if marking the original transaction
// replaced does not succeed for some reason.
2017-09-20 17:04:05 -03:00
errors . push_back ( " Created new bumpfee transaction but could not mark the original transaction as replaced " ) ;
2017-03-03 12:15:47 -03:00
}
2017-06-15 10:34:17 -04:00
return Result : : OK ;
2017-03-03 12:15:47 -03:00
}
2017-09-20 17:19:30 -03:00
} // namespace feebumper