aboutsummaryrefslogtreecommitdiff
path: root/src/wallet/wallet.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/wallet/wallet.cpp')
-rw-r--r--src/wallet/wallet.cpp113
1 files changed, 66 insertions, 47 deletions
diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp
index 08e480225d..b00fa851fd 100644
--- a/src/wallet/wallet.cpp
+++ b/src/wallet/wallet.cpp
@@ -13,7 +13,6 @@
#include <interfaces/wallet.h>
#include <key.h>
#include <key_io.h>
-#include <optional.h>
#include <outputtype.h>
#include <policy/fees.h>
#include <policy/policy.h>
@@ -40,6 +39,7 @@
#include <algorithm>
#include <assert.h>
+#include <optional>
#include <boost/algorithm/string/replace.hpp>
@@ -84,10 +84,10 @@ bool RemoveWalletSetting(interfaces::Chain& chain, const std::string& wallet_nam
static void UpdateWalletSetting(interfaces::Chain& chain,
const std::string& wallet_name,
- Optional<bool> load_on_startup,
+ std::optional<bool> load_on_startup,
std::vector<bilingual_str>& warnings)
{
- if (load_on_startup == nullopt) return;
+ if (!load_on_startup) return;
if (load_on_startup.value() && !AddWalletSetting(chain, wallet_name)) {
warnings.emplace_back(Untranslated("Wallet load on startup setting could not be updated, so wallet may not be loaded next node startup."));
} else if (!load_on_startup.value() && !RemoveWalletSetting(chain, wallet_name)) {
@@ -107,7 +107,7 @@ bool AddWallet(const std::shared_ptr<CWallet>& wallet)
return true;
}
-bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start, std::vector<bilingual_str>& warnings)
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start, std::vector<bilingual_str>& warnings)
{
assert(wallet);
@@ -127,7 +127,7 @@ bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on
return true;
}
-bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, Optional<bool> load_on_start)
+bool RemoveWallet(const std::shared_ptr<CWallet>& wallet, std::optional<bool> load_on_start)
{
std::vector<bilingual_str> warnings;
return RemoveWallet(wallet, load_on_start, warnings);
@@ -204,7 +204,7 @@ void UnloadWallet(std::shared_ptr<CWallet>&& wallet)
}
namespace {
-std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
try {
std::unique_ptr<WalletDatabase> database = MakeWalletDatabase(name, options, status, error);
@@ -234,11 +234,11 @@ std::shared_ptr<CWallet> LoadWalletInternal(interfaces::Chain& chain, const std:
}
} // namespace
-std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, const DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
auto result = WITH_LOCK(g_loading_wallet_mutex, return g_loading_wallet_set.insert(name));
if (!result.second) {
- error = Untranslated("Wallet already being loading.");
+ error = Untranslated("Wallet already loading.");
status = DatabaseStatus::FAILED_LOAD;
return nullptr;
}
@@ -247,7 +247,7 @@ std::shared_ptr<CWallet> LoadWallet(interfaces::Chain& chain, const std::string&
return wallet;
}
-std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, Optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
+std::shared_ptr<CWallet> CreateWallet(interfaces::Chain& chain, const std::string& name, std::optional<bool> load_on_start, DatabaseOptions& options, DatabaseStatus& status, bilingual_str& error, std::vector<bilingual_str>& warnings)
{
uint64_t wallet_creation_flags = options.create_flags;
const SecureString& passphrase = options.create_passphrase;
@@ -791,6 +791,12 @@ bool CWallet::MarkReplaced(const uint256& originalHash, const uint256& newHash)
wtx.mapValue["replaced_by_txid"] = newHash.ToString();
+ // Refresh mempool status without waiting for transactionRemovedFromMempool
+ // notification so the wallet is in an internally consistent state and
+ // immediately knows the old transaction should not be considered trusted
+ // and is eligible to be abandoned
+ wtx.fInMempool = chain().isInMempool(originalHash);
+
WalletBatch batch(GetDatabase());
bool success = true;
@@ -938,6 +944,14 @@ CWalletTx* CWallet::AddToWallet(CTransactionRef tx, const CWalletTx::Confirmatio
if (!strCmd.empty())
{
boost::replace_all(strCmd, "%s", hash.GetHex());
+ if (confirm.status == CWalletTx::Status::CONFIRMED)
+ {
+ boost::replace_all(strCmd, "%b", confirm.hashBlock.GetHex());
+ boost::replace_all(strCmd, "%h", ToString(confirm.block_height));
+ } else {
+ boost::replace_all(strCmd, "%b", "unconfirmed");
+ boost::replace_all(strCmd, "%h", "-1");
+ }
#ifndef WIN32
// Substituting the wallet name isn't currently supported on windows
// because windows shell escaping has not been implemented yet:
@@ -1611,14 +1625,15 @@ bool CWallet::ImportScriptPubKeys(const std::string& label, const std::set<CScri
return true;
}
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
+// Returns pair of vsize and weight
+std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, bool use_max_sig)
{
std::vector<CTxOut> txouts;
for (const CTxIn& input : tx.vin) {
const auto mi = wallet->mapWallet.find(input.prevout.hash);
// Can not estimate size without knowing the input details
if (mi == wallet->mapWallet.end()) {
- return -1;
+ return std::make_pair(-1, -1);
}
assert(input.prevout.n < mi->second.tx->vout.size());
txouts.emplace_back(mi->second.tx->vout[input.prevout.n]);
@@ -1627,13 +1642,16 @@ int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wall
}
// txouts needs to be in the order of tx.vin
-int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig)
+std::pair<int64_t, int64_t> CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, const std::vector<CTxOut>& txouts, bool use_max_sig)
{
CMutableTransaction txNew(tx);
if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) {
- return -1;
+ return std::make_pair(-1, -1);
}
- return GetVirtualTransactionSize(CTransaction(txNew));
+ CTransaction ctx(txNew);
+ int64_t vsize = GetVirtualTransactionSize(ctx);
+ int64_t weight = GetTransactionWeight(ctx);
+ return std::make_pair(vsize, weight);
}
int CalculateMaximumSignedInputSize(const CTxOut& txout, const CWallet* wallet, bool use_max_sig)
@@ -1753,7 +1771,7 @@ int64_t CWallet::RescanFromTime(int64_t startTime, const WalletRescanReserver& r
* the main chain after to the addition of any new keys you want to detect
* transactions for.
*/
-CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, int start_height, Optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate)
+CWallet::ScanResult CWallet::ScanForWalletTransactions(const uint256& start_block, int start_height, std::optional<int> max_height, const WalletRescanReserver& reserver, bool fUpdate)
{
int64_t nNow = GetTime();
int64_t start_time = GetTimeMillis();
@@ -2381,26 +2399,20 @@ bool CWallet::SelectCoinsMinConf(const CAmount& nTargetValue, const CoinEligibil
nValueRet = 0;
if (coin_selection_params.use_bnb) {
- // Get long term estimate
- FeeCalculation feeCalc;
- CCoinControl temp;
- temp.m_confirm_target = 1008;
- CFeeRate long_term_feerate = GetMinimumFeeRate(*this, temp, &feeCalc);
-
// Get the feerate for effective value.
// When subtracting the fee from the outputs, we want the effective feerate to be 0
CFeeRate effective_feerate{0};
if (!coin_selection_params.m_subtract_fee_outputs) {
- effective_feerate = coin_selection_params.effective_fee;
+ effective_feerate = coin_selection_params.m_effective_feerate;
}
- std::vector<OutputGroup> groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, effective_feerate, long_term_feerate, eligibility_filter, true /* positive_only */);
+ std::vector<OutputGroup> groups = GroupOutputs(coins, !coin_selection_params.m_avoid_partial_spends, effective_feerate, coin_selection_params.m_long_term_feerate, eligibility_filter, true /* positive_only */);
// Calculate cost of change
- 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);
+ CAmount cost_of_change = coin_selection_params.m_discard_feerate.GetFee(coin_selection_params.change_spend_size) + coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.change_output_size);
// Calculate the fees for things that aren't inputs
- CAmount not_input_fees = coin_selection_params.effective_fee.GetFee(coin_selection_params.tx_noinputs_size);
+ CAmount not_input_fees = coin_selection_params.m_effective_feerate.GetFee(coin_selection_params.tx_noinputs_size);
bnb_used = true;
return SelectCoinsBnB(groups, nTargetValue, cost_of_change, setCoinsRet, nValueRet, not_input_fees);
} else {
@@ -2454,7 +2466,7 @@ bool CWallet::SelectCoins(const std::vector<COutput>& vAvailableCoins, const CAm
if (coin.m_input_bytes <= 0) {
return false; // Not solvable, can't estimate size for fee
}
- coin.effective_value = coin.txout.nValue - coin_selection_params.effective_fee.GetFee(coin.m_input_bytes);
+ coin.effective_value = coin.txout.nValue - coin_selection_params.m_effective_feerate.GetFee(coin.m_input_bytes);
if (coin_selection_params.use_bnb) {
value_to_select -= coin.effective_value;
} else {
@@ -2715,7 +2727,7 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uin
return locktime;
}
-OutputType CWallet::TransactionChangeType(const Optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const
+OutputType CWallet::TransactionChangeType(const std::optional<OutputType>& change_type, const std::vector<CRecipient>& vecSend) const
{
// If -changetype is specified, always use that change type.
if (change_type) {
@@ -2779,6 +2791,7 @@ bool CWallet::CreateTransactionInternal(
CMutableTransaction txNew;
FeeCalculation feeCalc;
CAmount nFeeNeeded;
+ std::pair<int64_t, int64_t> tx_sizes;
int nBytes;
{
std::set<CInputCoin> setCoins;
@@ -2821,16 +2834,27 @@ bool CWallet::CreateTransactionInternal(
CTxOut change_prototype_txout(0, scriptChange);
coin_selection_params.change_output_size = GetSerializeSize(change_prototype_txout);
- CFeeRate discard_rate = GetDiscardRate(*this);
+ // Set discard feerate
+ coin_selection_params.m_discard_feerate = GetDiscardRate(*this);
// Get the fee rate to use effective values in coin selection
- CFeeRate nFeeRateNeeded = GetMinimumFeeRate(*this, coin_control, &feeCalc);
+ coin_selection_params.m_effective_feerate = GetMinimumFeeRate(*this, coin_control, &feeCalc);
// Do not, ever, assume that it's fine to change the fee rate if the user has explicitly
// provided one
- if (coin_control.m_feerate && nFeeRateNeeded > *coin_control.m_feerate) {
- error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), nFeeRateNeeded.ToString(FeeEstimateMode::SAT_VB));
+ if (coin_control.m_feerate && coin_selection_params.m_effective_feerate > *coin_control.m_feerate) {
+ error = strprintf(_("Fee rate (%s) is lower than the minimum fee rate setting (%s)"), coin_control.m_feerate->ToString(FeeEstimateMode::SAT_VB), coin_selection_params.m_effective_feerate.ToString(FeeEstimateMode::SAT_VB));
return false;
}
+ if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
+ // eventually allow a fallback fee
+ error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
+ return false;
+ }
+
+ // Get long term estimate
+ CCoinControl cc_temp;
+ cc_temp.m_confirm_target = chain().estimateMaxBlocks();
+ coin_selection_params.m_long_term_feerate = GetMinimumFeeRate(*this, cc_temp, nullptr);
nFeeRet = 0;
bool pick_new_inputs = true;
@@ -2905,7 +2929,6 @@ bool CWallet::CreateTransactionInternal(
} else {
coin_selection_params.change_spend_size = (size_t)change_spend_size;
}
- coin_selection_params.effective_fee = nFeeRateNeeded;
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coin_control, coin_selection_params, bnb_used))
{
// If BnB was used, it was the first pass. No longer the first pass and continue loop with knapsack.
@@ -2931,7 +2954,7 @@ bool CWallet::CreateTransactionInternal(
// Never create dust outputs; if we would, just
// add the dust to the fee.
// The nChange when BnB is used is always going to go to fees.
- if (IsDust(newTxOut, discard_rate) || bnb_used)
+ if (IsDust(newTxOut, coin_selection_params.m_discard_feerate) || bnb_used)
{
nChangePosInOut = -1;
nFeeRet += nChange;
@@ -2962,19 +2985,14 @@ bool CWallet::CreateTransactionInternal(
txNew.vin.push_back(CTxIn(coin.outpoint,CScript()));
}
- nBytes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), this, coin_control.fAllowWatchOnly);
+ nBytes = tx_sizes.first;
if (nBytes < 0) {
error = _("Signing transaction failed");
return false;
}
- nFeeNeeded = GetMinimumFee(*this, nBytes, coin_control, &feeCalc);
- if (feeCalc.reason == FeeReason::FALLBACK && !m_allow_fallback_fee) {
- // eventually allow a fallback fee
- error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
- return false;
- }
-
+ nFeeNeeded = coin_selection_params.m_effective_feerate.GetFee(nBytes);
if (nFeeRet >= nFeeNeeded) {
// Reduce fee to only the needed amount if possible. This
// prevents potential overpayment in fees if the coins
@@ -2988,8 +3006,8 @@ bool CWallet::CreateTransactionInternal(
// 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, nullptr);
- CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, discard_rate);
+ CAmount fee_needed_with_change = coin_selection_params.m_effective_feerate.GetFee(tx_size_with_change);
+ CAmount minimum_value_for_change = GetDustThreshold(change_prototype_txout, coin_selection_params.m_discard_feerate);
if (nFeeRet >= fee_needed_with_change + minimum_value_for_change) {
pick_new_inputs = false;
nFeeRet = fee_needed_with_change;
@@ -3072,7 +3090,8 @@ bool CWallet::CreateTransactionInternal(
tx = MakeTransactionRef(std::move(txNew));
// Limit size
- if (GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT)
+ if ((sign && GetTransactionWeight(*tx) > MAX_STANDARD_TX_WEIGHT) ||
+ (!sign && tx_sizes.second > MAX_STANDARD_TX_WEIGHT))
{
error = _("Transaction too large");
return false;
@@ -4054,13 +4073,13 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
WalletBatch batch(walletInstance->GetDatabase());
CBlockLocator locator;
if (batch.ReadBestBlock(locator)) {
- if (const Optional<int> fork_height = chain.findLocatorFork(locator)) {
+ if (const std::optional<int> fork_height = chain.findLocatorFork(locator)) {
rescan_height = *fork_height;
}
}
}
- const Optional<int> tip_height = chain.getHeight();
+ const std::optional<int> tip_height = chain.getHeight();
if (tip_height) {
walletInstance->m_last_block_processed = chain.getBlockHash(*tip_height);
walletInstance->m_last_block_processed_height = *tip_height;
@@ -4094,7 +4113,7 @@ std::shared_ptr<CWallet> CWallet::Create(interfaces::Chain& chain, const std::st
// No need to read and scan block if block was created before
// our wallet birthday (as adjusted for block time variability)
- Optional<int64_t> time_first_key;
+ std::optional<int64_t> time_first_key;
for (auto spk_man : walletInstance->GetAllScriptPubKeyMans()) {
int64_t time = spk_man->GetTimeFirstKey();
if (!time_first_key || time < *time_first_key) time_first_key = time;