Merge 45f434f44d into merged_master (Bitcoin PR #15288)

This commit is contained in:
Andrew Poelstra 2020-10-27 21:55:08 +00:00
commit edfdf46351
21 changed files with 302 additions and 160 deletions

View file

@ -8,9 +8,18 @@ MAPPING = {
'core_write.cpp': 'core_io.cpp',
}
# Directories with header-based modules, where the assumption that .cpp files
# define functions and variables declared in corresponding .h files is
# incorrect.
HEADER_MODULE_PATHS = [
'interfaces/'
]
def module_name(path):
if path in MAPPING:
path = MAPPING[path]
if any(path.startswith(dirpath) for dirpath in HEADER_MODULE_PATHS):
return path
if path.endswith(".h"):
return path[:-2]
if path.endswith(".c"):

View file

@ -603,12 +603,14 @@ elements_wallet_LDADD = \
$(LIBBITCOIN_UTIL) \
$(LIBBITCOIN_CRYPTO) \
$(LIBUNIVALUE) \
$(LIBBITCOIN_ZMQ) \
$(LIBLEVELDB) \
$(LIBLEVELDB_SSE42) \
$(LIBMEMENV) \
$(LIBSECP256K1)
$(LIBSECP256K1) \
$(LIBUNIVALUE)
elements_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_LIBS)
elements_wallet_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS) $(ZMQ_LIBS) $(EVENT_LIBS)
#
# bitcoinconsensus library #

View file

@ -57,7 +57,7 @@ if ENABLE_WALLET
bench_bench_bitcoin_SOURCES += bench/coin_selection.cpp
endif
bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(MINIUPNPC_LIBS) $(EVENT_LIBS)
bench_bench_bitcoin_LDADD += $(BOOST_LIBS) $(BDB_LIBS) $(CRYPTO_LIBS) $(EVENT_PTHREADS_LIBS) $(EVENT_LIBS) $(MINIUPNPC_LIBS)
bench_bench_bitcoin_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
CLEAN_BITCOIN_BENCH = bench/*.gcda bench/*.gcno $(GENERATED_BENCH_FILES)

View file

@ -6,8 +6,19 @@
#include <chain.h>
#include <chainparams.h>
#include <interfaces/wallet.h>
#include <net.h>
#include <policy/fees.h>
#include <policy/policy.h>
#include <policy/rbf.h>
#include <primitives/block.h>
#include <primitives/transaction.h>
#include <protocol.h>
#include <sync.h>
#include <threadsafety.h>
#include <timedata.h>
#include <txmempool.h>
#include <ui_interface.h>
#include <uint256.h>
#include <util/system.h>
#include <validation.h>
@ -132,6 +143,17 @@ class LockImpl : public Chain::Lock
}
return nullopt;
}
bool checkFinalTx(const CTransaction& tx) override
{
LockAnnotation lock(::cs_main);
return CheckFinalTx(tx);
}
bool submitToMemoryPool(CTransactionRef tx, CAmount absurd_fee, CValidationState& state) override
{
LockAnnotation lock(::cs_main);
return AcceptToMemoryPool(::mempool, state, tx, nullptr /* missing inputs */, nullptr /* txn replaced */,
false /* bypass limits */, absurd_fee);
}
};
class LockingStateImpl : public LockImpl, public UniqueLock<CCriticalSection>
@ -177,6 +199,62 @@ public:
LOCK(cs_main);
return GuessVerificationProgress(LookupBlockIndex(block_hash), Params().GetConsensus().nPowTargetSpacing);
}
RBFTransactionState isRBFOptIn(const CTransaction& tx) override
{
LOCK(::mempool.cs);
return IsRBFOptIn(tx, ::mempool);
}
bool hasDescendantsInMempool(const uint256& txid) override
{
LOCK(::mempool.cs);
auto it_mp = ::mempool.mapTx.find(txid);
return it_mp != ::mempool.mapTx.end() && it_mp->GetCountWithDescendants() > 1;
}
void relayTransaction(const uint256& txid) override
{
CInv inv(MSG_TX, txid);
g_connman->ForEachNode([&inv](CNode* node) { node->PushInventory(inv); });
}
void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) override
{
::mempool.GetTransactionAncestry(txid, ancestors, descendants);
}
bool checkChainLimits(CTransactionRef tx) override
{
LockPoints lp;
std::set<std::pair<uint256, COutPoint>> setPeginsSpent;
CTxMemPoolEntry entry(tx, 0, 0, 0, false, 0, lp, setPeginsSpent);
CTxMemPool::setEntries ancestors;
auto limit_ancestor_count = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
auto limit_ancestor_size = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT) * 1000;
auto limit_descendant_count = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
auto limit_descendant_size = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT) * 1000;
std::string unused_error_string;
LOCK(::mempool.cs);
return ::mempool.CalculateMemPoolAncestors(entry, ancestors, limit_ancestor_count, limit_ancestor_size,
limit_descendant_count, limit_descendant_size, unused_error_string);
}
CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc) override
{
return ::feeEstimator.estimateSmartFee(num_blocks, calc, conservative);
}
unsigned int estimateMaxBlocks() override
{
return ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
}
CFeeRate mempoolMinFee() override
{
return ::mempool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
}
CAmount maxTxFee() override { return ::maxTxFee; }
bool getPruneMode() override { return ::fPruneMode; }
bool p2pEnabled() override { return g_connman != nullptr; }
bool isInitialBlockDownload() override { return IsInitialBlockDownload(); }
int64_t getAdjustedTime() override { return GetAdjustedTime(); }
void initMessage(const std::string& message) override { ::uiInterface.InitMessage(message); }
void initWarning(const std::string& message) override { InitWarning(message); }
void initError(const std::string& message) override { InitError(message); }
void loadWallet(std::unique_ptr<Wallet> wallet) override { ::uiInterface.LoadWallet(wallet); }
};
} // namespace

View file

@ -5,20 +5,27 @@
#ifndef BITCOIN_INTERFACES_CHAIN_H
#define BITCOIN_INTERFACES_CHAIN_H
#include <optional.h>
#include <optional.h> // For Optional and nullopt
#include <policy/rbf.h> // For RBFTransactionState
#include <primitives/transaction.h> // For CTransactionRef
#include <memory>
#include <stddef.h>
#include <stdint.h>
#include <string>
#include <vector>
class CBlock;
class CScheduler;
class CValidationState;
class uint256;
struct CBlockLocator;
struct FeeCalculation;
namespace interfaces {
class Wallet;
//! Interface for giving wallet processes access to blockchain state.
class Chain
{
@ -102,6 +109,13 @@ public:
//! is guaranteed to be an ancestor of the block used to create the
//! locator.
virtual Optional<int> findLocatorFork(const CBlockLocator& locator) = 0;
//! Check if transaction will be final given chain height current time.
virtual bool checkFinalTx(const CTransaction& tx) = 0;
//! Add transaction to memory pool if the transaction fee is below the
//! amount specified by absurd_fee (as a safeguard). */
virtual bool submitToMemoryPool(CTransactionRef tx, CAmount absurd_fee, CValidationState& state) = 0;
};
//! Return Lock interface. Chain is locked when this is called, and
@ -127,6 +141,60 @@ public:
//! Estimate fraction of total transactions verified if blocks up to
//! the specified block hash are verified.
virtual double guessVerificationProgress(const uint256& block_hash) = 0;
//! Check if transaction is RBF opt in.
virtual RBFTransactionState isRBFOptIn(const CTransaction& tx) = 0;
//! Check if transaction has descendants in mempool.
virtual bool hasDescendantsInMempool(const uint256& txid) = 0;
//! Relay transaction.
virtual void relayTransaction(const uint256& txid) = 0;
//! Calculate mempool ancestor and descendant counts for the given transaction.
virtual void getTransactionAncestry(const uint256& txid, size_t& ancestors, size_t& descendants) = 0;
//! Check chain limits.
virtual bool checkChainLimits(CTransactionRef tx) = 0;
//! Estimate smart fee.
virtual CFeeRate estimateSmartFee(int num_blocks, bool conservative, FeeCalculation* calc = nullptr) = 0;
//! Fee estimator max target.
virtual unsigned int estimateMaxBlocks() = 0;
//! Pool min fee.
virtual CFeeRate mempoolMinFee() = 0;
//! Get node max tx fee setting (-maxtxfee).
//! This could be replaced by a per-wallet max fee, as proposed at
//! https://github.com/bitcoin/bitcoin/issues/15355
//! But for the time being, wallets call this to access the node setting.
virtual CAmount maxTxFee() = 0;
//! Check if pruning is enabled.
virtual bool getPruneMode() = 0;
//! Check if p2p enabled.
virtual bool p2pEnabled() = 0;
// Check if in IBD.
virtual bool isInitialBlockDownload() = 0;
//! Get adjusted time.
virtual int64_t getAdjustedTime() = 0;
//! Send init message.
virtual void initMessage(const std::string& message) = 0;
//! Send init warning.
virtual void initWarning(const std::string& message) = 0;
//! Send init error.
virtual void initError(const std::string& message) = 0;
//! Send wallet load notification to the GUI.
virtual void loadWallet(std::unique_ptr<Wallet> wallet) = 0;
};
//! Interface to let node manage chain clients (wallets, or maybe tools for

View file

@ -275,7 +275,7 @@ public:
}
std::unique_ptr<Handler> handleLoadWallet(LoadWalletFn fn) override
{
return MakeHandler(::uiInterface.LoadWallet_connect([fn](std::shared_ptr<CWallet> wallet) { fn(MakeWallet(wallet)); }));
return MakeHandler(::uiInterface.LoadWallet_connect([fn](std::unique_ptr<Wallet>& wallet) { fn(std::move(wallet)); }));
}
std::unique_ptr<Handler> handleNotifyNumConnectionsChanged(NotifyNumConnectionsChangedFn fn) override
{

View file

@ -56,7 +56,7 @@ public:
auto locked_chain = m_wallet.chain().lock();
LOCK(m_wallet.cs_wallet);
CValidationState state;
if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_keys, g_connman.get(), state, g_con_elementsmode ? &m_blind_details : nullptr)) {
if (!m_wallet.CommitTransaction(m_tx, std::move(value_map), std::move(order_form), m_keys, state, g_con_elementsmode ? &m_blind_details : nullptr)) {
reject_reason = state.GetRejectReason();
return false;
}
@ -112,7 +112,7 @@ WalletTx MakeWalletTx(interfaces::Chain::Lock& locked_chain, CWallet& wallet, co
//! Construct wallet tx status struct.
WalletTxStatus MakeWalletTxStatus(interfaces::Chain::Lock& locked_chain, const CWalletTx& wtx)
{
LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
LockAnnotation lock(::cs_main); // Temporary, for mapBlockIndex below. Removed in upcoming commit.
WalletTxStatus result;
auto mi = ::mapBlockIndex.find(wtx.hashBlock);
@ -122,7 +122,7 @@ WalletTxStatus MakeWalletTxStatus(interfaces::Chain::Lock& locked_chain, const C
result.depth_in_main_chain = wtx.GetDepthInMainChain(locked_chain);
result.time_received = wtx.nTimeReceived;
result.lock_time = wtx.tx->nLockTime;
result.is_final = CheckFinalTx(*wtx.tx);
result.is_final = locked_chain.checkFinalTx(*wtx.tx);
result.is_trusted = wtx.IsTrusted(locked_chain);
result.is_abandoned = wtx.isAbandoned();
result.is_coinbase = wtx.IsCoinBase();
@ -484,7 +484,7 @@ public:
{
FeeCalculation fee_calc;
CAmount result;
result = GetMinimumFee(*m_wallet, tx_bytes, coin_control, ::mempool, ::feeEstimator, &fee_calc);
result = GetMinimumFee(*m_wallet, tx_bytes, coin_control, &fee_calc);
if (returned_target) *returned_target = fee_calc.returnedTarget;
if (reason) *reason = fee_calc.reason;
return result;

View file

@ -855,7 +855,8 @@ static UniValue estimatesmartfee(const JSONRPCRequest& request)
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VSTR});
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
unsigned int conf_target = ParseConfirmTarget(request.params[0]);
unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
bool conservative = true;
if (!request.params[1].isNull()) {
FeeEstimateMode fee_mode;
@ -927,7 +928,8 @@ static UniValue estimaterawfee(const JSONRPCRequest& request)
RPCTypeCheck(request.params, {UniValue::VNUM, UniValue::VNUM}, true);
RPCTypeCheckArgument(request.params[0], UniValue::VNUM);
unsigned int conf_target = ParseConfirmTarget(request.params[0]);
unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
unsigned int conf_target = ParseConfirmTarget(request.params[0], max_target);
double threshold = 0.95;
if (!request.params[1].isNull()) {
threshold = request.params[1].get_real();

View file

@ -4,14 +4,12 @@
#include <key_io.h>
#include <keystore.h>
#include <policy/fees.h>
#include <rpc/util.h>
#include <tinyformat.h>
#include <util/strencodings.h>
#include <policy/policy.h>
#include <assetsdir.h>
#include <core_io.h>
#include <validation.h>
InitInterfaces* g_rpc_interfaces = nullptr;
@ -141,10 +139,9 @@ UniValue DescribeAddress(const CTxDestination& dest)
return boost::apply_visitor(DescribeAddressVisitor(), dest);
}
unsigned int ParseConfirmTarget(const UniValue& value)
unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target)
{
int target = value.get_int();
unsigned int max_target = ::feeEstimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
if (target < 1 || (unsigned int)target > max_target) {
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid conf_target, must be between %u - %u", 1, max_target));
}

View file

@ -38,7 +38,7 @@ bool IsBlindDestination(const CTxDestination& dest);
UniValue AmountMapToUniv(const CAmountMap& balanceOrig, std::string strasset);
//! Parse a confirm target option and raise an RPC error if it is invalid.
unsigned int ParseConfirmTarget(const UniValue& value);
unsigned int ParseConfirmTarget(const UniValue& value, unsigned int max_target);
RPCErrorCode RPCErrorFromTransactionError(TransactionError terr);
UniValue JSONRPCTransactionError(TransactionError terr, const std::string& err_string = "");

View file

@ -52,7 +52,7 @@ void CClientUIInterface::InitMessage(const std::string& message) { return g_ui_s
void CClientUIInterface::NotifyNumConnectionsChanged(int newNumConnections) { return g_ui_signals.NotifyNumConnectionsChanged(newNumConnections); }
void CClientUIInterface::NotifyNetworkActiveChanged(bool networkActive) { return g_ui_signals.NotifyNetworkActiveChanged(networkActive); }
void CClientUIInterface::NotifyAlertChanged() { return g_ui_signals.NotifyAlertChanged(); }
void CClientUIInterface::LoadWallet(std::shared_ptr<CWallet> wallet) { return g_ui_signals.LoadWallet(wallet); }
void CClientUIInterface::LoadWallet(std::unique_ptr<interfaces::Wallet>& wallet) { return g_ui_signals.LoadWallet(wallet); }
void CClientUIInterface::ShowProgress(const std::string& title, int nProgress, bool resume_possible) { return g_ui_signals.ShowProgress(title, nProgress, resume_possible); }
void CClientUIInterface::NotifyBlockTip(bool b, const CBlockIndex* i) { return g_ui_signals.NotifyBlockTip(b, i); }
void CClientUIInterface::NotifyHeaderTip(bool b, const CBlockIndex* i) { return g_ui_signals.NotifyHeaderTip(b, i); }

View file

@ -11,7 +11,6 @@
#include <stdint.h>
#include <string>
class CWallet;
class CBlockIndex;
namespace boost {
namespace signals2 {
@ -19,6 +18,10 @@ class connection;
}
} // namespace boost
namespace interfaces {
class Wallet;
} // namespace interfaces
/** General change type (added, updated, removed). */
enum ChangeType
{
@ -102,7 +105,7 @@ public:
ADD_SIGNALS_DECL_WRAPPER(NotifyAlertChanged, void, );
/** A wallet has been loaded. */
ADD_SIGNALS_DECL_WRAPPER(LoadWallet, void, std::shared_ptr<CWallet> wallet);
ADD_SIGNALS_DECL_WRAPPER(LoadWallet, void, std::unique_ptr<interfaces::Wallet>& wallet);
/**
* Show progress e.g. for verifychain.

View file

@ -27,9 +27,7 @@ static feebumper::Result PreconditionChecks(interfaces::Chain::Lock& locked_chai
}
{
LOCK(mempool.cs);
auto it_mp = mempool.mapTx.find(wtx.GetHash());
if (it_mp != mempool.mapTx.end() && it_mp->GetCountWithDescendants() > 1) {
if (wallet->chain().hasDescendantsInMempool(wtx.GetHash())) {
errors.push_back("Transaction has descendants in the mempool");
return feebumper::Result::INVALID_PARAMETER;
}
@ -167,7 +165,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
new_fee = total_fee;
nNewFeeRate = CFeeRate(total_fee, maxNewTxSize);
} else {
new_fee = GetMinimumFee(*wallet, maxNewTxSize, coin_control, mempool, ::feeEstimator, nullptr /* FeeCalculation */);
new_fee = GetMinimumFee(*wallet, maxNewTxSize, coin_control, nullptr /* FeeCalculation */);
nNewFeeRate = CFeeRate(new_fee, maxNewTxSize);
// New fee rate must be at least old rate + minimum incremental relay rate
@ -219,7 +217,7 @@ Result CreateTransaction(const CWallet* wallet, const uint256& txid, const CCoin
// If the output would become dust, discard it (converting the dust to fee)
poutput->nValue = poutput->nValue.GetAmount() - nDelta;
if (poutput->nValue.GetAmount() <= GetDustThreshold(*poutput, GetDiscardRate(*wallet, ::feeEstimator))) {
if (poutput->nValue.GetAmount() <= GetDustThreshold(*poutput, GetDiscardRate(*wallet))) {
wallet->WalletLogPrintf("Bumping fee and discarding dust output\n");
new_fee += poutput->nValue.GetAmount();
mtx.vout.erase(mtx.vout.begin() + nOutput);
@ -289,7 +287,7 @@ Result CommitTransaction(CWallet* wallet, const uint256& txid, CMutableTransacti
reservekeys.push_back(std::unique_ptr<CReserveKey>(new CReserveKey(wallet)));
//reservekeys.push_back(std::unique_ptr<CReserveKey>(wallet));
CValidationState state;
if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, reservekeys, g_connman.get(), state)) {
if (!wallet->CommitTransaction(tx, std::move(mapValue), oldWtx.vOrderForm, reservekeys, state)) {
// NOTE: CommitTransaction never returns false, so this should never happen.
errors.push_back(strprintf("The transaction was rejected: %s", FormatStateMessage(state)));
return Result::WALLET_ERROR;

View file

@ -6,7 +6,6 @@
#include <wallet/fees.h>
#include <policy/policy.h>
#include <txmempool.h>
#include <util/system.h>
#include <validation.h>
#include <wallet/coincontrol.h>
@ -19,9 +18,9 @@ CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes)
}
CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc)
CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, FeeCalculation* feeCalc)
{
CAmount fee_needed = GetMinimumFeeRate(wallet, coin_control, pool, estimator, feeCalc).GetFee(nTxBytes);
CAmount fee_needed = GetMinimumFeeRate(wallet, coin_control, feeCalc).GetFee(nTxBytes);
// Always obey the maximum
if (fee_needed > maxTxFee) {
fee_needed = maxTxFee;
@ -35,7 +34,7 @@ CFeeRate GetRequiredFeeRate(const CWallet& wallet)
return std::max(wallet.m_min_fee, ::minRelayTxFee);
}
CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc)
CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, FeeCalculation* feeCalc)
{
/* User control of how to calculate fee uses the following parameter precedence:
1. coin_control.m_feerate
@ -64,7 +63,7 @@ CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_contr
if (coin_control.m_fee_mode == FeeEstimateMode::CONSERVATIVE) conservative_estimate = true;
else if (coin_control.m_fee_mode == FeeEstimateMode::ECONOMICAL) conservative_estimate = false;
feerate_needed = estimator.estimateSmartFee(target, feeCalc, conservative_estimate);
feerate_needed = wallet.chain().estimateSmartFee(target, conservative_estimate, feeCalc);
if (feerate_needed == CFeeRate(0)) {
// if we don't have enough data for estimateSmartFee, then use fallback fee
feerate_needed = wallet.m_fallback_fee;
@ -74,7 +73,7 @@ CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_contr
if (wallet.m_fallback_fee == CFeeRate(0)) return feerate_needed;
}
// Obey mempool min fee when using smart fee estimation
CFeeRate min_mempool_feerate = pool.GetMinFee(gArgs.GetArg("-maxmempool", DEFAULT_MAX_MEMPOOL_SIZE) * 1000000);
CFeeRate min_mempool_feerate = wallet.chain().mempoolMinFee();
if (feerate_needed < min_mempool_feerate) {
feerate_needed = min_mempool_feerate;
if (feeCalc) feeCalc->reason = FeeReason::MEMPOOL_MIN;
@ -90,10 +89,10 @@ CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_contr
return feerate_needed;
}
CFeeRate GetDiscardRate(const CWallet& wallet, const CBlockPolicyEstimator& estimator)
CFeeRate GetDiscardRate(const CWallet& wallet)
{
unsigned int highest_target = estimator.HighestTargetTracked(FeeEstimateHorizon::LONG_HALFLIFE);
CFeeRate discard_rate = estimator.estimateSmartFee(highest_target, nullptr /* FeeCalculation */, false /* conservative */);
unsigned int highest_target = wallet.chain().estimateMaxBlocks();
CFeeRate discard_rate = wallet.chain().estimateSmartFee(highest_target, false /* conservative */);
// Don't let discard_rate be greater than longest possible fee estimate if we get a valid fee estimate
discard_rate = (discard_rate == CFeeRate(0)) ? wallet.m_discard_rate : std::min(discard_rate, wallet.m_discard_rate);
// Discard rate must be at least dustRelayFee

View file

@ -8,10 +8,8 @@
#include <amount.h>
class CBlockPolicyEstimator;
class CCoinControl;
class CFeeRate;
class CTxMemPool;
class CWallet;
struct FeeCalculation;
@ -25,7 +23,7 @@ CAmount GetRequiredFee(const CWallet& wallet, unsigned int nTxBytes);
* Estimate the minimum fee considering user set parameters
* and the required fee
*/
CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc);
CAmount GetMinimumFee(const CWallet& wallet, unsigned int nTxBytes, const CCoinControl& coin_control, FeeCalculation* feeCalc);
/**
* Return the minimum required feerate taking into account the
@ -37,11 +35,11 @@ CFeeRate GetRequiredFeeRate(const CWallet& wallet);
* Estimate the minimum fee rate considering user set parameters
* and the required fee
*/
CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, const CTxMemPool& pool, const CBlockPolicyEstimator& estimator, FeeCalculation* feeCalc);
CFeeRate GetMinimumFeeRate(const CWallet& wallet, const CCoinControl& coin_control, FeeCalculation* feeCalc);
/**
* Return the maximum feerate for discarding change.
*/
CFeeRate GetDiscardRate(const CWallet& wallet, const CBlockPolicyEstimator& estimator);
CFeeRate GetDiscardRate(const CWallet& wallet);
#endif // BITCOIN_WALLET_FEES_H

View file

@ -138,12 +138,15 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
// The canonical path cleans the path, preventing >1 Berkeley environment instances for the same directory
fs::path canonical_wallet_dir = fs::canonical(wallet_dir, error);
if (error || !fs::exists(wallet_dir)) {
return InitError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
chain.initError(strprintf(_("Specified -walletdir \"%s\" does not exist"), wallet_dir.string()));
return false;
} else if (!fs::is_directory(wallet_dir)) {
return InitError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
chain.initError(strprintf(_("Specified -walletdir \"%s\" is not a directory"), wallet_dir.string()));
return false;
// The canonical path transforms relative paths into absolute ones, so we check the non-canonical version
} else if (!wallet_dir.is_absolute()) {
return InitError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
chain.initError(strprintf(_("Specified -walletdir \"%s\" is a relative path"), wallet_dir.string()));
return false;
}
gArgs.ForceSetArg("-walletdir", canonical_wallet_dir.string());
}
@ -164,14 +167,15 @@ bool VerifyWallets(interfaces::Chain& chain, const std::vector<std::string>& wal
WalletLocation location(wallet_file);
if (!wallet_paths.insert(location.GetPath()).second) {
return InitError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
chain.initError(strprintf(_("Error loading wallet %s. Duplicate -wallet filename specified."), wallet_file));
return false;
}
std::string error_string;
std::string warning_string;
bool verify_success = CWallet::Verify(chain, location, salvage_wallet, error_string, warning_string);
if (!error_string.empty()) InitError(error_string);
if (!warning_string.empty()) InitWarning(warning_string);
if (!error_string.empty()) chain.initError(error_string);
if (!warning_string.empty()) chain.initWarning(warning_string);
if (!verify_success) return false;
}

View file

@ -160,8 +160,9 @@ UniValue importprivkey(const JSONRPCRequest& request)
if (!request.params[2].isNull())
fRescan = request.params[2].get_bool();
if (fRescan && fPruneMode)
if (fRescan && pwallet->chain().getPruneMode()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
}
if (fRescan && !reserver.reserve()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Wallet is currently rescanning. Abort existing rescan or wait.");
@ -316,8 +317,9 @@ UniValue importaddress(const JSONRPCRequest& request)
if (!request.params[2].isNull())
fRescan = request.params[2].get_bool();
if (fRescan && fPruneMode)
if (fRescan && pwallet->chain().getPruneMode()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
}
WalletRescanReserver reserver(pwallet);
if (fRescan && !reserver.reserve()) {
@ -504,8 +506,9 @@ UniValue importpubkey(const JSONRPCRequest& request)
if (!request.params[2].isNull())
fRescan = request.params[2].get_bool();
if (fRescan && fPruneMode)
if (fRescan && pwallet->chain().getPruneMode()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Rescan is disabled in pruned mode");
}
WalletRescanReserver reserver(pwallet);
if (fRescan && !reserver.reserve()) {
@ -565,8 +568,9 @@ UniValue importwallet(const JSONRPCRequest& request)
},
}.ToString());
if (fPruneMode)
if (pwallet->chain().getPruneMode()) {
throw JSONRPCError(RPC_WALLET_ERROR, "Importing wallets is disabled in pruned mode");
}
WalletRescanReserver reserver(pwallet);
if (!reserver.reserve()) {

View file

@ -140,8 +140,7 @@ static void WalletTxToJSON(interfaces::Chain& chain, interfaces::Chain::Lock& lo
// Add opt-in RBF status
std::string rbfStatus = "no";
if (confirms <= 0) {
LOCK(mempool.cs);
RBFTransactionState rbfState = IsRBFOptIn(*wtx.tx, mempool);
RBFTransactionState rbfState = chain.isRBFOptIn(*wtx.tx);
if (rbfState == RBFTransactionState::UNKNOWN)
rbfStatus = "unknown";
else if (rbfState == RBFTransactionState::REPLACEABLE_BIP125)
@ -347,7 +346,7 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
if (nValue > curBalance[asset])
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds");
if (pwallet->GetBroadcastTransactions() && !g_connman) {
if (pwallet->GetBroadcastTransactions() && !pwallet->chain().p2pEnabled()) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}
@ -379,7 +378,7 @@ static CTransactionRef SendMoney(interfaces::Chain::Lock& locked_chain, CWallet
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
CValidationState state;
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, reserveKeys, g_connman.get(), state, blind_details)) {
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, reserveKeys, state, blind_details)) {
strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
@ -465,7 +464,7 @@ static UniValue sendtoaddress(const JSONRPCRequest& request)
}
if (!request.params[6].isNull()) {
coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]);
coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
}
if (!request.params[7].isNull()) {
@ -662,7 +661,6 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@ -685,8 +683,9 @@ static UniValue getreceivedbyaddress(const JSONRPCRequest& request)
CAmountMap amounts;
for (auto& pairWtx : pwallet->mapWallet) {
CWalletTx& wtx = pairWtx.second;
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
if (wtx.IsCoinBase() || !locked_chain->checkFinalTx(*wtx.tx)) {
continue;
}
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
const CTxOut& txout = wtx.tx->vout[i];
@ -750,7 +749,6 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
// the user could have gotten from another RPC command prior to now
pwallet->BlockUntilSyncedToCurrentChain();
LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@ -767,8 +765,9 @@ static UniValue getreceivedbylabel(const JSONRPCRequest& request)
CAmountMap amounts;
for (auto& pairWtx : pwallet->mapWallet) {
CWalletTx& wtx = pairWtx.second;
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
if (wtx.IsCoinBase() || !locked_chain->checkFinalTx(*wtx.tx)) {
continue;
}
for (unsigned int i = 0; i < wtx.tx->vout.size(); i++) {
const CTxOut& txout = wtx.tx->vout[i];
@ -956,7 +955,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
if (pwallet->GetBroadcastTransactions() && !g_connman) {
if (pwallet->GetBroadcastTransactions() && !pwallet->chain().p2pEnabled()) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}
@ -982,7 +981,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
}
if (!request.params[6].isNull()) {
coin_control.m_confirm_target = ParseConfirmTarget(request.params[6]);
coin_control.m_confirm_target = ParseConfirmTarget(request.params[6], pwallet->chain().estimateMaxBlocks());
}
if (!request.params[7].isNull()) {
@ -1089,7 +1088,7 @@ static UniValue sendmany(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, strFailReason);
}
CValidationState state;
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, change_keys, g_connman.get(), state, blind_details)) {
if (!pwallet->CommitTransaction(tx, std::move(mapValue), {} /* orderForm */, change_keys, state, blind_details)) {
strFailReason = strprintf("Transaction commit failed:: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strFailReason);
}
@ -1249,8 +1248,6 @@ struct tallyitem
static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * const pwallet, const UniValue& params, bool by_label) EXCLUSIVE_LOCKS_REQUIRED(pwallet->cs_wallet)
{
LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
// Minimum confirmations
int nMinDepth = 1;
if (!params[0].isNull())
@ -1290,8 +1287,9 @@ static UniValue ListReceived(interfaces::Chain::Lock& locked_chain, CWallet * co
for (const std::pair<const uint256, CWalletTx>& pairWtx : pwallet->mapWallet) {
const CWalletTx& wtx = pairWtx.second;
if (wtx.IsCoinBase() || !CheckFinalTx(*wtx.tx))
if (wtx.IsCoinBase() || !locked_chain.checkFinalTx(*wtx.tx)) {
continue;
}
int nDepth = wtx.GetDepthInMainChain(locked_chain);
if (nDepth < nMinDepth)
@ -2933,8 +2931,9 @@ static UniValue resendwallettransactions(const JSONRPCRequest& request)
}.ToString()
);
if (!g_connman)
if (!pwallet->chain().p2pEnabled()) {
throw JSONRPCError(RPC_CLIENT_P2P_DISABLED, "Error: Peer-to-peer functionality missing or disabled");
}
auto locked_chain = pwallet->chain().lock();
LOCK(pwallet->cs_wallet);
@ -2943,7 +2942,7 @@ static UniValue resendwallettransactions(const JSONRPCRequest& request)
throw JSONRPCError(RPC_WALLET_ERROR, "Error: Wallet transaction broadcasting is disabled with -walletbroadcast");
}
std::vector<uint256> txids = pwallet->ResendWalletTransactionsBefore(*locked_chain, GetTime(), g_connman.get());
std::vector<uint256> txids = pwallet->ResendWalletTransactionsBefore(*locked_chain, GetTime());
UniValue result(UniValue::VARR);
for (const uint256& txid : txids)
{
@ -3293,7 +3292,7 @@ void FundTransaction(CWallet* const pwallet, CMutableTransaction& tx, CAmount& f
if (options.exists("feeRate")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both conf_target and feeRate");
}
coinControl.m_confirm_target = ParseConfirmTarget(options["conf_target"]);
coinControl.m_confirm_target = ParseConfirmTarget(options["conf_target"], pwallet->chain().estimateMaxBlocks());
}
if (options.exists("estimate_mode")) {
if (options.exists("feeRate")) {
@ -3585,7 +3584,7 @@ static UniValue bumpfee(const JSONRPCRequest& request)
if (options.exists("confTarget") && options.exists("totalFee")) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "confTarget and totalFee options should not both be set. Please provide either a confirmation target for fee estimation or an explicit total fee for the transaction.");
} else if (options.exists("confTarget")) { // TODO: alias this to conf_target
coin_control.m_confirm_target = ParseConfirmTarget(options["confTarget"]);
coin_control.m_confirm_target = ParseConfirmTarget(options["confTarget"], pwallet->chain().estimateMaxBlocks());
} else if (options.exists("totalFee")) {
totalFee = options["totalFee"].get_int64();
if (totalFee <= 0) {
@ -4232,7 +4231,7 @@ UniValue sethdseed(const JSONRPCRequest& request)
}.ToString());
}
if (IsInitialBlockDownload()) {
if (pwallet->chain().isInitialBlockDownload()) {
throw JSONRPCError(RPC_CLIENT_IN_INITIAL_DOWNLOAD, "Cannot set a new HD seed while still in Initial Block Download");
}
@ -5386,7 +5385,7 @@ static UniValue createrawpegin(const JSONRPCRequest& request, T_tx_ref& txBTCRef
unsigned int nBytes = GetVirtualTransactionSize(CTransaction(mtx)) +
(1+1+72+1+33/WITNESS_SCALE_FACTOR);
CCoinControl coin_control;
CAmount nFeeNeeded = GetMinimumFee(*pwallet, nBytes, coin_control, mempool, ::feeEstimator, nullptr);
CAmount nFeeNeeded = GetMinimumFee(*pwallet, nBytes, coin_control, nullptr);
mtx.vout[0].nValue = mtx.vout[0].nValue.GetAmount() - nFeeNeeded;
mtx.vout[1].nValue = mtx.vout[1].nValue.GetAmount() + nFeeNeeded;
@ -5501,7 +5500,7 @@ UniValue claimpegin(const JSONRPCRequest& request)
mapValue_t mapValue;
std::vector<std::unique_ptr<CReserveKey>> reservekeys;
reservekeys.push_back(std::unique_ptr<CReserveKey>(new CReserveKey(pwallet)));
if (!pwallet->CommitTransaction(MakeTransactionRef(mtx), mapValue, {} /* orderForm */, reservekeys, g_connman.get(), state)) {
if (!pwallet->CommitTransaction(MakeTransactionRef(mtx), mapValue, {} /* orderForm */, reservekeys, state)) {
std::string strError = strprintf("Error: The transaction was rejected! Reason given: %s", FormatStateMessage(state));
throw JSONRPCError(RPC_WALLET_ERROR, strError);
}
@ -5875,7 +5874,7 @@ static CTransactionRef SendGenerationTransaction(const CScript& asset_script, co
CValidationState state;
mapValue_t map_value;
if (!pwallet->CommitTransaction(tx_ref, std::move(map_value), {} /* orderForm */, change_keys, g_connman.get(), state, &blind_details)) {
if (!pwallet->CommitTransaction(tx_ref, std::move(map_value), {} /* orderForm */, change_keys, state, &blind_details)) {
throw JSONRPCError(RPC_WALLET_ERROR, "Error: The transaction was rejected! This might happen if some of the coins in your wallet were already spent, such as if you used a copy of the wallet and coins were spent in the copy but not marked as spent here.");
}

View file

@ -369,7 +369,7 @@ public:
CCoinControl dummy;
BOOST_CHECK(wallet->CreateTransaction(*m_locked_chain, {recipient}, tx, reservekeys, fee, changePos, error, dummy));
CValidationState state;
BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekeys, nullptr, state));
BOOST_CHECK(wallet->CommitTransaction(tx, {}, {}, reservekeys, state));
CMutableTransaction blocktx;
{
LOCK(wallet->cs_wallet);

View file

@ -12,6 +12,7 @@
#include <consensus/validation.h>
#include <fs.h>
#include <interfaces/chain.h>
#include <interfaces/wallet.h>
#include <key.h>
#include <key_io.h>
#include <keystore.h>
@ -945,7 +946,7 @@ bool CWallet::AddToWallet(const CWalletTx& wtxIn, bool fFlushOnClose)
wtx.BindWallet(this);
bool fInsertedNew = ret.second;
if (fInsertedNew) {
wtx.nTimeReceived = GetAdjustedTime();
wtx.nTimeReceived = chain().getAdjustedTime();
wtx.nOrderPos = IncOrderPosNext(&batch);
wtx.m_it_wtxOrdered = wtxOrdered.insert(std::make_pair(wtx.nOrderPos, &wtx));
wtx.nTimeSmart = ComputeTimeSmart(wtx);
@ -1945,25 +1946,21 @@ void CWallet::ReacceptWalletTransactions()
for (const std::pair<const int64_t, CWalletTx*>& item : mapSorted) {
CWalletTx& wtx = *(item.second);
CValidationState state;
wtx.AcceptToMemoryPool(*locked_chain, maxTxFee, state);
wtx.AcceptToMemoryPool(*locked_chain, state);
}
}
bool CWalletTx::RelayWalletTransaction(interfaces::Chain::Lock& locked_chain, CConnman* connman)
bool CWalletTx::RelayWalletTransaction(interfaces::Chain::Lock& locked_chain)
{
assert(pwallet->GetBroadcastTransactions());
if (!IsCoinBase() && !isAbandoned() && GetDepthInMainChain(locked_chain) == 0)
{
CValidationState state;
/* GetDepthInMainChain already catches known conflicts. */
if (InMempool() || AcceptToMemoryPool(locked_chain, maxTxFee, state)) {
if (InMempool() || AcceptToMemoryPool(locked_chain, state)) {
pwallet->WalletLogPrintf("Relaying wtx %s\n", GetHash().ToString());
if (connman) {
CInv inv(MSG_TX, GetHash());
connman->ForEachNode([&inv](CNode* pnode)
{
pnode->PushInventory(inv);
});
if (pwallet->chain().p2pEnabled()) {
pwallet->chain().relayTransaction(GetHash());
return true;
}
}
@ -2139,11 +2136,10 @@ bool CWalletTx::InMempool() const
bool CWalletTx::IsTrusted(interfaces::Chain::Lock& locked_chain) const
{
LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
// Quick answer in most cases
if (!CheckFinalTx(*tx))
if (!locked_chain.checkFinalTx(*tx)) {
return false;
}
int nDepth = GetDepthInMainChain(locked_chain);
if (nDepth >= 1)
return true;
@ -2192,7 +2188,7 @@ CAmountMap CWalletTx::GetIssuanceAssets(unsigned int input_index) const {
return ret;
}
std::vector<uint256> CWallet::ResendWalletTransactionsBefore(interfaces::Chain::Lock& locked_chain, int64_t nTime, CConnman* connman)
std::vector<uint256> CWallet::ResendWalletTransactionsBefore(interfaces::Chain::Lock& locked_chain, int64_t nTime)
{
std::vector<uint256> result;
@ -2211,8 +2207,9 @@ std::vector<uint256> CWallet::ResendWalletTransactionsBefore(interfaces::Chain::
for (const std::pair<const unsigned int, CWalletTx*>& item : mapSorted)
{
CWalletTx& wtx = *item.second;
if (wtx.RelayWalletTransaction(locked_chain, connman))
if (wtx.RelayWalletTransaction(locked_chain)) {
result.push_back(wtx.GetHash());
}
}
return result;
}
@ -2236,7 +2233,7 @@ void CWallet::ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman
// Rebroadcast unconfirmed txes older than 5 minutes before the last
// block was found:
auto locked_chain = chain().assumeLocked(); // Temporary. Removed in upcoming lock cleanup
std::vector<uint256> relayed = ResendWalletTransactionsBefore(*locked_chain, nBestBlockTime-5*60, connman);
std::vector<uint256> relayed = ResendWalletTransactionsBefore(*locked_chain, nBestBlockTime-5*60);
if (!relayed.empty())
WalletLogPrintf("%s: rebroadcast %u unconfirmed transactions\n", __func__, relayed.size());
}
@ -2340,7 +2337,6 @@ CAmountMap CWallet::GetImmatureWatchOnlyBalance() const
// trusted.
CAmountMap CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) const
{
LockAnnotation lock(::cs_main); // Temporary, for CheckFinalTx below. Removed in upcoming commit.
auto locked_chain = chain().lock();
LOCK(cs_wallet);
@ -2348,7 +2344,7 @@ CAmountMap CWallet::GetLegacyBalance(const isminefilter& filter, int minDepth) c
for (const auto& entry : mapWallet) {
const CWalletTx& wtx = entry.second;
const int depth = wtx.GetDepthInMainChain(*locked_chain);
if (depth < 0 || !CheckFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase(*locked_chain)) {
if (depth < 0 || !locked_chain->checkFinalTx(*wtx.tx) || wtx.IsImmatureCoinBase(*locked_chain)) {
continue;
}
@ -2415,8 +2411,9 @@ void CWallet::AvailableCoins(interfaces::Chain::Lock& locked_chain, std::vector<
const uint256& wtxid = entry.first;
const CWalletTx* pcoin = &entry.second;
if (!CheckFinalTx(*pcoin->tx))
if (!locked_chain.checkFinalTx(*pcoin->tx)) {
continue;
}
if (pcoin->IsImmatureCoinBase(locked_chain))
continue;
@ -2603,10 +2600,10 @@ bool CWallet::SelectCoinsMinConf(const CAmountMap& mapTargetValue, const CoinEli
FeeCalculation feeCalc;
CCoinControl temp;
temp.m_confirm_target = 1008;
CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, ::mempool, ::feeEstimator, &feeCalc);
CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, &feeCalc);
// Calculate cost of change
CAmount cost_of_change = GetDiscardRate(*this, ::feeEstimator).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size);
CAmount cost_of_change = GetDiscardRate(*this).GetFee(coin_selection_params.change_spend_size) + coin_selection_params.effective_fee.GetFee(coin_selection_params.change_output_size);
// Filter by the min conf specs and add to utxo_pool and calculate effective value
for (OutputGroup& group : asset_groups) {
@ -3204,10 +3201,10 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
coin_selection_params.change_output_size += DEFAULT_RANGEPROOF_SIZE/WITNESS_SCALE_FACTOR;
}
CFeeRate discard_rate = GetDiscardRate(*this, ::feeEstimator);
CFeeRate discard_rate = GetDiscardRate(*this);
// Get the fee rate to use effective values in coin selection
CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, ::mempool, ::feeEstimator, &feeCalc);
CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, &feeCalc);
// ELEMENTS:
// Start with tiny non-zero fee for issuance entropy and loop until there is enough fee
@ -3526,7 +3523,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
txNew = blind_details->tx_unblinded_unsigned;
}
nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, ::mempool, ::feeEstimator, &feeCalc);
nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, &feeCalc);
if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
// eventually allow a fallback fee
strFailReason = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
@ -3554,7 +3551,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
// change output. Only try this once.
if (nChangePosInOut == -1 && nSubtractFeeFromAmount == 0 && pick_new_inputs) {
unsigned int tx_size_with_change = nBytes + coin_selection_params.change_output_size + 2; // Add 2 as a buffer in case increasing # of outputs changes compact size
CAmount fee_needed_with_change = GetMinimumFee(*this, tx_size_with_change, coin_control, ::mempool, ::feeEstimator, nullptr);
CAmount fee_needed_with_change = GetMinimumFee(*this, tx_size_with_change, coin_control, nullptr);
CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) {
pick_new_inputs = false;
@ -3780,17 +3777,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
if (gArgs.GetBoolArg("-walletrejectlongchains", DEFAULT_WALLET_REJECT_LONG_CHAINS)) {
// Lastly, ensure this tx will pass the mempool's chain limits
LockPoints lp;
std::set<std::pair<uint256, COutPoint>> setPeginsSpent;
CTxMemPoolEntry entry(tx, 0, 0, 0, false, 0, lp, setPeginsSpent);
CTxMemPool::setEntries setAncestors;
size_t nLimitAncestors = gArgs.GetArg("-limitancestorcount", DEFAULT_ANCESTOR_LIMIT);
size_t nLimitAncestorSize = gArgs.GetArg("-limitancestorsize", DEFAULT_ANCESTOR_SIZE_LIMIT)*1000;
size_t nLimitDescendants = gArgs.GetArg("-limitdescendantcount", DEFAULT_DESCENDANT_LIMIT);
size_t nLimitDescendantSize = gArgs.GetArg("-limitdescendantsize", DEFAULT_DESCENDANT_SIZE_LIMIT)*1000;
std::string errString;
LOCK(::mempool.cs);
if (!::mempool.CalculateMemPoolAncestors(entry, setAncestors, nLimitAncestors, nLimitAncestorSize, nLimitDescendants, nLimitDescendantSize, errString)) {
if (!chain().checkChainLimits(tx)) {
strFailReason = _("Transaction has too long of a mempool chain");
return false;
}
@ -3810,7 +3797,7 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
/**
* Call after CreateTransaction unless you want to abort
*/
bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, std::vector<std::unique_ptr<CReserveKey>>& reservekeys, CConnman* connman, CValidationState& state, const BlindDetails* blind_details)
bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, std::vector<std::unique_ptr<CReserveKey>>& reservekeys, CValidationState& state, const BlindDetails* blind_details)
{
{
auto locked_chain = chain().lock();
@ -3863,11 +3850,11 @@ bool CWallet::CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::ve
if (fBroadcastTransactions)
{
// Broadcast
if (!wtx.AcceptToMemoryPool(*locked_chain, maxTxFee, state)) {
if (!wtx.AcceptToMemoryPool(*locked_chain, state)) {
WalletLogPrintf("CommitTransaction(): Transaction cannot be broadcast immediately, %s\n", FormatStateMessage(state));
// TODO: if we expect the failure to be long term or permanent, instead delete wtx from the wallet and return failure.
} else {
wtx.RelayWalletTransaction(*locked_chain, connman);
wtx.RelayWalletTransaction(*locked_chain);
}
}
}
@ -4763,17 +4750,17 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
std::vector<CWalletTx> vWtx;
if (gArgs.GetBoolArg("-zapwallettxes", false)) {
uiInterface.InitMessage(_("Zapping all transactions from wallet..."));
chain.initMessage(_("Zapping all transactions from wallet..."));
std::unique_ptr<CWallet> tempWallet = MakeUnique<CWallet>(chain, location, WalletDatabase::Create(location.GetPath()));
DBErrors nZapWalletRet = tempWallet->ZapWalletTx(vWtx);
if (nZapWalletRet != DBErrors::LOAD_OK) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
chain.initError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
return nullptr;
}
}
uiInterface.InitMessage(_("Loading wallet..."));
chain.initMessage(_("Loading wallet..."));
int64_t nStart = GetTimeMillis();
bool fFirstRun = true;
@ -4784,26 +4771,26 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (nLoadWalletRet != DBErrors::LOAD_OK)
{
if (nLoadWalletRet == DBErrors::CORRUPT) {
InitError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
chain.initError(strprintf(_("Error loading %s: Wallet corrupted"), walletFile));
return nullptr;
}
else if (nLoadWalletRet == DBErrors::NONCRITICAL_ERROR)
{
InitWarning(strprintf(_("Error reading %s! All keys read correctly, but transaction data"
" or address book entries might be missing or incorrect."),
chain.initWarning(strprintf(_("Error reading %s! All keys read correctly, but transaction data"
" or address book entries might be missing or incorrect."),
walletFile));
}
else if (nLoadWalletRet == DBErrors::TOO_NEW) {
InitError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME)));
chain.initError(strprintf(_("Error loading %s: Wallet requires newer version of %s"), walletFile, _(PACKAGE_NAME)));
return nullptr;
}
else if (nLoadWalletRet == DBErrors::NEED_REWRITE)
{
InitError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME)));
chain.initError(strprintf(_("Wallet needed to be rewritten: restart %s to complete"), _(PACKAGE_NAME)));
return nullptr;
}
else {
InitError(strprintf(_("Error loading %s"), walletFile));
chain.initError(strprintf(_("Error loading %s"), walletFile));
return nullptr;
}
}
@ -4822,7 +4809,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
walletInstance->WalletLogPrintf("Allowing wallet upgrade up to %i\n", nMaxVersion);
if (nMaxVersion < walletInstance->GetVersion())
{
InitError(_("Cannot downgrade wallet"));
chain.initError(_("Cannot downgrade wallet"));
return nullptr;
}
walletInstance->SetMaxVersion(nMaxVersion);
@ -4835,7 +4822,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// Do not upgrade versions to any version between HD_SPLIT and FEATURE_PRE_SPLIT_KEYPOOL unless already supporting HD_SPLIT
int max_version = walletInstance->nWalletVersion;
if (!walletInstance->CanSupportFeature(FEATURE_HD_SPLIT) && max_version >=FEATURE_HD_SPLIT && max_version < FEATURE_PRE_SPLIT_KEYPOOL) {
InitError(_("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified."));
chain.initError(_("Cannot upgrade a non HD split wallet without upgrading to support pre split keypool. Please use -upgradewallet=169900 or -upgradewallet with no version specified."));
return nullptr;
}
@ -4863,7 +4850,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// Regenerate the keypool if upgraded to HD
if (hd_upgrade) {
if (!walletInstance->TopUpKeyPool()) {
InitError(_("Unable to generate keys"));
chain.initError(_("Unable to generate keys"));
return nullptr;
}
}
@ -4887,7 +4874,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
// Top up the keypool
if (walletInstance->CanGenerateKeys() && !walletInstance->TopUpKeyPool()) {
InitError(_("Unable to generate initial keys"));
chain.initError(_("Unable to generate initial keys"));
return nullptr;
}
@ -4895,34 +4882,34 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
walletInstance->ChainStateFlushed(locked_chain->getTipLocator());
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
// Make it impossible to disable private keys after creation
InitError(strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile));
chain.initError(strprintf(_("Error loading %s: Private keys can only be disabled during creation"), walletFile));
return NULL;
} else if (walletInstance->IsWalletFlagSet(WALLET_FLAG_DISABLE_PRIVATE_KEYS)) {
LOCK(walletInstance->cs_KeyStore);
if (!walletInstance->mapKeys.empty() || !walletInstance->mapCryptedKeys.empty()) {
InitWarning(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
chain.initWarning(strprintf(_("Warning: Private keys detected in wallet {%s} with disabled private keys"), walletFile));
}
}
if (!gArgs.GetArg("-addresstype", "").empty() && !ParseOutputType(gArgs.GetArg("-addresstype", ""), walletInstance->m_default_address_type)) {
InitError(strprintf("Unknown address type '%s'", gArgs.GetArg("-addresstype", "")));
chain.initError(strprintf("Unknown address type '%s'", gArgs.GetArg("-addresstype", "")));
return nullptr;
}
if (!gArgs.GetArg("-changetype", "").empty() && !ParseOutputType(gArgs.GetArg("-changetype", ""), walletInstance->m_default_change_type)) {
InitError(strprintf("Unknown change type '%s'", gArgs.GetArg("-changetype", "")));
chain.initError(strprintf("Unknown change type '%s'", gArgs.GetArg("-changetype", "")));
return nullptr;
}
if (gArgs.IsArgSet("-mintxfee")) {
CAmount n = 0;
if (!ParseMoney(gArgs.GetArg("-mintxfee", ""), n) || 0 == n) {
InitError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")));
chain.initError(AmountErrMsg("mintxfee", gArgs.GetArg("-mintxfee", "")));
return nullptr;
}
if (n > HIGH_TX_FEE_PER_KB) {
InitWarning(AmountHighWarn("-mintxfee") + " " +
_("This is the minimum transaction fee you pay on every transaction."));
chain.initWarning(AmountHighWarn("-mintxfee") + " " +
_("This is the minimum transaction fee you pay on every transaction."));
}
walletInstance->m_min_fee = CFeeRate(n);
}
@ -4931,12 +4918,12 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (gArgs.IsArgSet("-fallbackfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-fallbackfee", ""), nFeePerK)) {
InitError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", "")));
chain.initError(strprintf(_("Invalid amount for -fallbackfee=<amount>: '%s'"), gArgs.GetArg("-fallbackfee", "")));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
InitWarning(AmountHighWarn("-fallbackfee") + " " +
_("This is the transaction fee you may pay when fee estimates are not available."));
chain.initWarning(AmountHighWarn("-fallbackfee") + " " +
_("This is the transaction fee you may pay when fee estimates are not available."));
}
walletInstance->m_fallback_fee = CFeeRate(nFeePerK);
walletInstance->m_allow_fallback_fee = nFeePerK != 0; //disable fallback fee in case value was set to 0, enable if non-null value
@ -4944,28 +4931,28 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
if (gArgs.IsArgSet("-discardfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-discardfee", ""), nFeePerK)) {
InitError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", "")));
chain.initError(strprintf(_("Invalid amount for -discardfee=<amount>: '%s'"), gArgs.GetArg("-discardfee", "")));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
InitWarning(AmountHighWarn("-discardfee") + " " +
_("This is the transaction fee you may discard if change is smaller than dust at this level"));
chain.initWarning(AmountHighWarn("-discardfee") + " " +
_("This is the transaction fee you may discard if change is smaller than dust at this level"));
}
walletInstance->m_discard_rate = CFeeRate(nFeePerK);
}
if (gArgs.IsArgSet("-paytxfee")) {
CAmount nFeePerK = 0;
if (!ParseMoney(gArgs.GetArg("-paytxfee", ""), nFeePerK)) {
InitError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")));
chain.initError(AmountErrMsg("paytxfee", gArgs.GetArg("-paytxfee", "")));
return nullptr;
}
if (nFeePerK > HIGH_TX_FEE_PER_KB) {
InitWarning(AmountHighWarn("-paytxfee") + " " +
_("This is the transaction fee you will pay if you send a transaction."));
chain.initWarning(AmountHighWarn("-paytxfee") + " " +
_("This is the transaction fee you will pay if you send a transaction."));
}
walletInstance->m_pay_tx_fee = CFeeRate(nFeePerK, 1000);
if (walletInstance->m_pay_tx_fee < ::minRelayTxFee) {
InitError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
chain.initError(strprintf(_("Invalid amount for -paytxfee=<amount>: '%s' (must be at least %s)"),
gArgs.GetArg("-paytxfee", ""), ::minRelayTxFee.ToString()));
return nullptr;
}
@ -5006,20 +4993,19 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
//We can't rescan beyond non-pruned blocks, stop and throw an error
//this might happen if a user uses an old wallet within a pruned node
// or if he ran -disablewallet for a longer time, then decided to re-enable
if (fPruneMode)
{
if (chain.getPruneMode()) {
int block_height = *tip_height;
while (block_height > 0 && locked_chain->haveBlockOnDisk(block_height - 1) && rescan_height != block_height) {
--block_height;
}
if (rescan_height != block_height) {
InitError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"));
chain.initError(_("Prune: last wallet synchronisation goes beyond pruned data. You need to -reindex (download the whole blockchain again in case of pruned node)"));
return nullptr;
}
}
uiInterface.InitMessage(_("Rescanning..."));
chain.initMessage(_("Rescanning..."));
walletInstance->WalletLogPrintf("Rescanning last %i blocks (from block %i)...\n", *tip_height - rescan_height, rescan_height);
// No need to read and scan block if block was created before
@ -5034,7 +5020,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
{
WalletRescanReserver reserver(walletInstance.get());
if (!reserver.reserve() || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions(locked_chain->getBlockHash(rescan_height), {} /* stop block */, reserver, true /* update */).status)) {
InitError(_("Failed to rescan the wallet during initialization"));
chain.initError(_("Failed to rescan the wallet during initialization"));
return nullptr;
}
}
@ -5067,7 +5053,7 @@ std::shared_ptr<CWallet> CWallet::CreateWalletFromFile(interfaces::Chain& chain,
}
}
uiInterface.LoadWallet(walletInstance);
chain.loadWallet(interfaces::MakeWallet(walletInstance));
// Register with the validation interface. It's ok to do this after rescan since we're still holding cs_main.
RegisterValidationInterface(walletInstance.get());
@ -5150,17 +5136,14 @@ bool CMerkleTx::IsImmatureCoinBase(interfaces::Chain::Lock& locked_chain) const
return GetBlocksToMaturity(locked_chain) > 0;
}
bool CWalletTx::AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, const CAmount& nAbsurdFee, CValidationState& state)
bool CWalletTx::AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, CValidationState& state)
{
LockAnnotation lock(::cs_main); // Temporary, for AcceptToMemoryPool below. Removed in upcoming commit.
// We must set fInMempool here - while it will be re-set to true by the
// entered-mempool callback, if we did not there would be a race where a
// user could call sendmoney in a loop and hit spurious out of funds errors
// because we think that this newly generated transaction's change is
// unavailable as we're not yet aware that it is in the mempool.
bool ret = ::AcceptToMemoryPool(mempool, state, tx, nullptr /* pfMissingInputs */,
nullptr /* plTxnReplaced */, false /* bypass_limits */, nAbsurdFee);
bool ret = locked_chain.submitToMemoryPool(tx, pwallet->chain().maxTxFee(), state);
fInMempool |= ret;
return ret;
}
@ -5192,7 +5175,7 @@ std::vector<OutputGroup> CWallet::GroupOutputs(const std::vector<COutput>& outpu
dst.first = input_coin.asset;
size_t ancestors, descendants;
mempool.GetTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
chain().getTransactionAncestry(output.tx->GetHash(), ancestors, descendants);
if (!single_coin && ExtractDestination(output.tx->tx->vout[output.i].scriptPubKey, dst.second)) {
// Limit output groups to no more than 10 entries, to protect
// against inadvertently creating a too-large transaction

View file

@ -101,8 +101,6 @@ class CCoinControl;
class COutput;
class CReserveKey;
class CScript;
class CTxMemPool;
class CBlockPolicyEstimator;
class CWalletTx;
struct FeeCalculation;
enum class FeeEstimateMode;
@ -544,10 +542,10 @@ public:
int64_t GetTxTime() const;
// RelayWalletTransaction may only be called if fBroadcastTransactions!
bool RelayWalletTransaction(interfaces::Chain::Lock& locked_chain, CConnman* connman);
bool RelayWalletTransaction(interfaces::Chain::Lock& locked_chain);
/** Pass this transaction to the mempool. Fails if absolute fee exceeds absurd fee. */
bool AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, const CAmount& nAbsurdFee, CValidationState& state);
bool AcceptToMemoryPool(interfaces::Chain::Lock& locked_chain, CValidationState& state);
// ELEMENTS:
@ -1054,7 +1052,7 @@ public:
void ReacceptWalletTransactions();
void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) override EXCLUSIVE_LOCKS_REQUIRED(cs_main);
// ResendWalletTransactionsBefore may only be called if fBroadcastTransactions!
std::vector<uint256> ResendWalletTransactionsBefore(interfaces::Chain::Lock& locked_chain, int64_t nTime, CConnman* connman);
std::vector<uint256> ResendWalletTransactionsBefore(interfaces::Chain::Lock& locked_chain, int64_t nTime);
CAmountMap GetBalance(const isminefilter& filter=ISMINE_SPENDABLE, const int min_depth=0) const;
CAmountMap GetUnconfirmedBalance() const;
CAmountMap GetImmatureBalance() const;
@ -1079,7 +1077,7 @@ public:
*/
bool CreateTransaction(interfaces::Chain::Lock& locked_chain, const std::vector<CRecipient>& vecSend, CTransactionRef& tx, std::vector<std::unique_ptr<CReserveKey>>& reservekey, CAmount& nFeeRet, int& nChangePosInOut,
std::string& strFailReason, const CCoinControl& coin_control, bool sign = true, BlindDetails* blind_details = nullptr, const IssuanceDetails* issuance_details = nullptr);
bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, std::vector<std::unique_ptr<CReserveKey>>& reservekey, CConnman* connman, CValidationState& state, const BlindDetails* blind_details = nullptr);
bool CommitTransaction(CTransactionRef tx, mapValue_t mapValue, std::vector<std::pair<std::string, std::string>> orderForm, std::vector<std::unique_ptr<CReserveKey>>& reservekey, CValidationState& state, const BlindDetails* blind_details = nullptr);
bool DummySignTx(CMutableTransaction &txNew, const std::set<CTxOut> &txouts, bool use_max_sig = false) const
{