aboutsummaryrefslogtreecommitdiff
path: root/src/wallet
diff options
context:
space:
mode:
authorMarcoFalke <falke.marco@gmail.com>2022-02-01 15:00:29 +0100
committerMarcoFalke <falke.marco@gmail.com>2022-02-02 10:11:21 +0100
commitfa8e76bb9002a126b3645bd9533c282f5dfff111 (patch)
treea6e03dbf509357781404f4bd7276ae7a43519af4 /src/wallet
parenta41976ab770d4453ccb043f72e48a6eb15a7106a (diff)
downloadbitcoin-fa8e76bb9002a126b3645bd9533c282f5dfff111.tar.xz
wallet: Add sanity checks to AntiFeeSnipe
Diffstat (limited to 'src/wallet')
-rw-r--r--src/wallet/spend.cpp37
1 files changed, 25 insertions, 12 deletions
diff --git a/src/wallet/spend.cpp b/src/wallet/spend.cpp
index 3d8ae2da69..667fac0e1a 100644
--- a/src/wallet/spend.cpp
+++ b/src/wallet/spend.cpp
@@ -583,12 +583,13 @@ static bool IsCurrentForAntiFeeSniping(interfaces::Chain& chain, const uint256&
}
/**
- * Return a height-based locktime for new transactions (uses the height of the
+ * Set a height-based locktime for new transactions (uses the height of the
* current chain tip unless we are not synced with the current chain
*/
-static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uint256& block_hash, int block_height)
+static void DiscourageFeeSniping(CMutableTransaction& tx, interfaces::Chain& chain, const uint256& block_hash, int block_height)
{
- uint32_t locktime;
+ // All inputs must be added by now
+ assert(!tx.vin.empty());
// Discourage fee sniping.
//
// For a large miner the value of the transactions in the best block and
@@ -610,22 +611,34 @@ static uint32_t GetLocktimeForNewTransaction(interfaces::Chain& chain, const uin
// now we ensure code won't be written that makes assumptions about
// nLockTime that preclude a fix later.
if (IsCurrentForAntiFeeSniping(chain, block_hash)) {
- locktime = block_height;
+ tx.nLockTime = block_height;
// Secondly occasionally randomly pick a nLockTime even further back, so
// that transactions that are delayed after signing for whatever reason,
// e.g. high-latency mix networks and some CoinJoin implementations, have
// better privacy.
- if (GetRandInt(10) == 0)
- locktime = std::max(0, (int)locktime - GetRandInt(100));
+ if (GetRandInt(10) == 0) {
+ tx.nLockTime = std::max(0, int(tx.nLockTime) - GetRandInt(100));
+ }
} else {
// If our chain is lagging behind, we can't discourage fee sniping nor help
// the privacy of high-latency transactions. To avoid leaking a potentially
// unique "nLockTime fingerprint", set nLockTime to a constant.
- locktime = 0;
+ tx.nLockTime = 0;
+ }
+ // Sanity check all values
+ assert(tx.nLockTime < LOCKTIME_THRESHOLD); // Type must be block height
+ assert(tx.nLockTime <= uint64_t(block_height));
+ for (const auto& in : tx.vin) {
+ // Can not be FINAL for locktime to work
+ assert(in.nSequence != CTxIn::SEQUENCE_FINAL);
+ // May be MAX NONFINAL to disable both BIP68 and BIP125
+ if (in.nSequence == CTxIn::MAX_SEQUENCE_NONFINAL) continue;
+ // May be MAX BIP125 to disable BIP68 and enable BIP125
+ if (in.nSequence == MAX_BIP125_RBF_SEQUENCE) continue;
+ // The wallet does not support any other sequence-use right now.
+ assert(false);
}
- assert(locktime < LOCKTIME_THRESHOLD);
- return locktime;
}
static bool CreateTransactionInternal(
@@ -642,7 +655,6 @@ static bool CreateTransactionInternal(
AssertLockHeld(wallet.cs_wallet);
CMutableTransaction txNew; // The resulting transaction that we make
- txNew.nLockTime = GetLocktimeForNewTransaction(wallet.chain(), wallet.GetLastBlockHash(), wallet.GetLastBlockHeight());
CoinSelectionParams coin_selection_params; // Parameters for coin selection, init with dummy
coin_selection_params.m_avoid_partial_spends = coin_control.m_avoid_partial_spends;
@@ -788,8 +800,8 @@ static bool CreateTransactionInternal(
// Shuffle selected coins and fill in final vin
std::vector<CInputCoin> selected_coins = result->GetShuffledInputVector();
- // Note how the sequence number is set to non-maxint so that
- // the nLockTime set above actually works.
+ // The sequence number is set to non-maxint so that DiscourageFeeSniping
+ // works.
//
// BIP125 defines opt-in RBF as any nSequence < maxint-1, so
// we use the highest possible value in that range (maxint-2)
@@ -800,6 +812,7 @@ static bool CreateTransactionInternal(
for (const auto& coin : selected_coins) {
txNew.vin.push_back(CTxIn(coin.outpoint, CScript(), nSequence));
}
+ DiscourageFeeSniping(txNew, wallet.chain(), wallet.GetLastBlockHash(), wallet.GetLastBlockHeight());
// Calculate the transaction fee
TxSize tx_sizes = CalculateMaximumSignedTxSize(CTransaction(txNew), &wallet, &coin_control);