Skip to content

Commit be23e15

Browse files
Merge #6782: backport: 0.25 batch 393
a8c09bc Merge bitcoin#25491: wallet: use Mutex for g_sqlite_mutex instead of GlobalMutex (PastaBot) 9ccf843 Merge bitcoin#26259: test: Test year 2106 block timestamps (PastaBot) 2d27f12 Merge bitcoin#26321: Adjust `.tx/config` for new Transifex CLI (PastaBot) 587294f Merge bitcoin#24851: init: ignore BIP-30 verification in DisconnectBlock for problematic blocks (PastaPastaPasta) f1c060d Merge bitcoin#27902: fuzz: wallet, add target for `CoinControl` (PastaPastaPasta) 4a8c861 Merge bitcoin#25666: refactor: wallet, do not translate init options names (fanquake) 3f8f85d Merge bitcoin#27221: test: Default timeout factor to 4 under --valgrind (PastaBot) 1e3d0fe Merge bitcoin#23897: refactor: Move calculation logic out from `CheckSequenceLocksAtTip()` (PastaBot) Pull request description: ## Issue being fixed or feature implemented ## What was done? ## How Has This Been Tested? ## Breaking Changes ## Checklist: _Go over all the following points, and put an `x` in all the boxes that apply._ - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have added or updated relevant unit/integration/functional/e2e tests - [ ] I have made corresponding changes to the documentation - [x] I have assigned this pull request to a milestone _(for repository code-owners and collaborators only)_ ACKs for top commit: UdjinM6: utACK a8c09bc Tree-SHA512: 2b982fdc329300aec2f61feadea8758bdf463b567156cef0a0763831e1b0694fe1b0018e489e70a20c930ff5b82f741e8e8f3aaa09baa4b6a5e2b57a29f39654
2 parents 99731db + a8c09bc commit be23e15

File tree

13 files changed

+271
-106
lines changed

13 files changed

+271
-106
lines changed

.tx/config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[main]
22
host = https://www.transifex.com
33

4-
[dash.dash_ents]
4+
[o:dash:p:dash:r:dash_ents]
55
file_filter = src/qt/locale/dash_<lang>.ts
66
source_file = src/qt/locale/dash_en.xlf
77
source_lang = en

src/Makefile.test.include

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ BITCOIN_TESTS += wallet/test/db_tests.cpp
222222
endif
223223

224224
FUZZ_WALLET_SRC = \
225+
wallet/test/fuzz/coincontrol.cpp \
225226
wallet/test/fuzz/coinselection.cpp
226227

227228
if USE_SQLITE

src/test/miner_tests.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,9 @@ struct MinerTestingSetup : public TestingSetup {
3939
bool TestSequenceLocks(const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(::cs_main, m_node.mempool->cs)
4040
{
4141
CCoinsViewMemPool view_mempool(&m_node.chainman->ActiveChainstate().CoinsTip(), *m_node.mempool);
42-
return CheckSequenceLocksAtTip(m_node.chainman->ActiveChain().Tip(), view_mempool, tx);
42+
CBlockIndex* tip{m_node.chainman->ActiveChain().Tip()};
43+
const std::optional<LockPoints> lock_points{CalculateLockPointsAtTip(tip, view_mempool, tx)};
44+
return lock_points.has_value() && CheckSequenceLocksAtTip(tip, *lock_points);
4345
}
4446
BlockAssembler AssemblerForTest(const CChainParams& params);
4547
};

src/validation.cpp

Lines changed: 115 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -185,11 +185,89 @@ bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction&
185185
return IsFinalTx(tx, nBlockHeight, nBlockTime);
186186
}
187187

188+
namespace {
189+
/**
190+
* A helper which calculates heights of inputs of a given transaction.
191+
*
192+
* @param[in] tip The current chain tip. If an input belongs to a mempool
193+
* transaction, we assume it will be confirmed in the next block.
194+
* @param[in] coins Any CCoinsView that provides access to the relevant coins.
195+
* @param[in] tx The transaction being evaluated.
196+
*
197+
* @returns A vector of input heights or nullopt, in case of an error.
198+
*/
199+
std::optional<std::vector<int>> CalculatePrevHeights(
200+
const CBlockIndex& tip,
201+
const CCoinsView& coins,
202+
const CTransaction& tx)
203+
{
204+
std::vector<int> prev_heights;
205+
prev_heights.resize(tx.vin.size());
206+
for (size_t i = 0; i < tx.vin.size(); ++i) {
207+
const CTxIn& txin = tx.vin[i];
208+
Coin coin;
209+
if (!coins.GetCoin(txin.prevout, coin)) {
210+
LogPrintf("ERROR: %s: Missing input %d in transaction \'%s\'\n", __func__, i, tx.GetHash().GetHex());
211+
return std::nullopt;
212+
}
213+
if (coin.nHeight == MEMPOOL_HEIGHT) {
214+
// Assume all mempool transaction confirm in the next block.
215+
prev_heights[i] = tip.nHeight + 1;
216+
} else {
217+
prev_heights[i] = coin.nHeight;
218+
}
219+
}
220+
return prev_heights;
221+
}
222+
} // namespace
223+
224+
std::optional<LockPoints> CalculateLockPointsAtTip(
225+
CBlockIndex* tip,
226+
const CCoinsView& coins_view,
227+
const CTransaction& tx)
228+
{
229+
assert(tip);
230+
231+
auto prev_heights{CalculatePrevHeights(*tip, coins_view, tx)};
232+
if (!prev_heights.has_value()) return std::nullopt;
233+
234+
CBlockIndex next_tip;
235+
next_tip.pprev = tip;
236+
// When SequenceLocks() is called within ConnectBlock(), the height
237+
// of the block *being* evaluated is what is used.
238+
// Thus if we want to know if a transaction can be part of the
239+
// *next* block, we need to use one more than active_chainstate.m_chain.Height()
240+
next_tip.nHeight = tip->nHeight + 1;
241+
const auto [min_height, min_time] = CalculateSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, prev_heights.value(), next_tip);
242+
243+
// Also store the hash of the block with the highest height of
244+
// all the blocks which have sequence locked prevouts.
245+
// This hash needs to still be on the chain
246+
// for these LockPoint calculations to be valid
247+
// Note: It is impossible to correctly calculate a maxInputBlock
248+
// if any of the sequence locked inputs depend on unconfirmed txs,
249+
// except in the special case where the relative lock time/height
250+
// is 0, which is equivalent to no sequence lock. Since we assume
251+
// input height of tip+1 for mempool txs and test the resulting
252+
// min_height and min_time from CalculateSequenceLocks against tip+1.
253+
int max_input_height{0};
254+
for (const int height : prev_heights.value()) {
255+
// Can ignore mempool inputs since we'll fail if they had non-zero locks
256+
if (height != next_tip.nHeight) {
257+
max_input_height = std::max(max_input_height, height);
258+
}
259+
}
260+
261+
// tip->GetAncestor(max_input_height) should never return a nullptr
262+
// because max_input_height is always less than the tip height.
263+
// It would, however, be a bad bug to continue execution, since a
264+
// LockPoints object with the maxInputBlock member set to nullptr
265+
// signifies no relative lock time.
266+
return LockPoints{min_height, min_time, Assert(tip->GetAncestor(max_input_height))};
267+
}
268+
188269
bool CheckSequenceLocksAtTip(CBlockIndex* tip,
189-
const CCoinsView& coins_view,
190-
const CTransaction& tx,
191-
LockPoints* lp,
192-
bool useExistingLockPoints)
270+
const LockPoints& lock_points)
193271
{
194272
assert(tip != nullptr);
195273

@@ -203,61 +281,7 @@ bool CheckSequenceLocksAtTip(CBlockIndex* tip,
203281
// *next* block, we need to use one more than active_chainstate.m_chain.Height()
204282
index.nHeight = tip->nHeight + 1;
205283

206-
std::pair<int, int64_t> lockPair;
207-
if (useExistingLockPoints) {
208-
assert(lp);
209-
lockPair.first = lp->height;
210-
lockPair.second = lp->time;
211-
}
212-
else {
213-
std::vector<int> prevheights;
214-
prevheights.resize(tx.vin.size());
215-
for (size_t txinIndex = 0; txinIndex < tx.vin.size(); txinIndex++) {
216-
const CTxIn& txin = tx.vin[txinIndex];
217-
Coin coin;
218-
if (!coins_view.GetCoin(txin.prevout, coin)) {
219-
return error("%s: Missing input", __func__);
220-
}
221-
if (coin.nHeight == MEMPOOL_HEIGHT) {
222-
// Assume all mempool transaction confirm in the next block
223-
prevheights[txinIndex] = tip->nHeight + 1;
224-
} else {
225-
prevheights[txinIndex] = coin.nHeight;
226-
}
227-
}
228-
lockPair = CalculateSequenceLocks(tx, STANDARD_LOCKTIME_VERIFY_FLAGS, prevheights, index);
229-
if (lp) {
230-
lp->height = lockPair.first;
231-
lp->time = lockPair.second;
232-
// Also store the hash of the block with the highest height of
233-
// all the blocks which have sequence locked prevouts.
234-
// This hash needs to still be on the chain
235-
// for these LockPoint calculations to be valid
236-
// Note: It is impossible to correctly calculate a maxInputBlock
237-
// if any of the sequence locked inputs depend on unconfirmed txs,
238-
// except in the special case where the relative lock time/height
239-
// is 0, which is equivalent to no sequence lock. Since we assume
240-
// input height of tip+1 for mempool txs and test the resulting
241-
// lockPair from CalculateSequenceLocks against tip+1. We know
242-
// EvaluateSequenceLocks will fail if there was a non-zero sequence
243-
// lock on a mempool input, so we can use the return value of
244-
// CheckSequenceLocksAtTip to indicate the LockPoints validity
245-
int maxInputHeight = 0;
246-
for (const int height : prevheights) {
247-
// Can ignore mempool inputs since we'll fail if they had non-zero locks
248-
if (height != tip->nHeight+1) {
249-
maxInputHeight = std::max(maxInputHeight, height);
250-
}
251-
}
252-
// tip->GetAncestor(maxInputHeight) should never return a nullptr
253-
// because maxInputHeight is always less than the tip height.
254-
// It would, however, be a bad bug to continue execution, since a
255-
// LockPoints object with the maxInputBlock member set to nullptr
256-
// signifies no relative lock time.
257-
lp->maxInputBlock = Assert(tip->GetAncestor(maxInputHeight));
258-
}
259-
}
260-
return EvaluateSequenceLocks(index, lockPair);
284+
return EvaluateSequenceLocks(index, {lock_points.height, lock_points.time});
261285
}
262286

263287
bool GetUTXOCoin(CChainState& active_chainstate, const COutPoint& outpoint, Coin& coin)
@@ -405,20 +429,23 @@ void CChainState::MaybeUpdateMempoolForReorg(
405429

406430
// The transaction must be final.
407431
if (!CheckFinalTxAtTip(*Assert(m_chain.Tip()), tx)) return true;
408-
LockPoints lp = it->GetLockPoints();
409-
const bool validLP{TestLockPointValidity(m_chain, lp)};
410-
CCoinsViewMemPool view_mempool(&CoinsTip(), *m_mempool);
432+
433+
const LockPoints& lp = it->GetLockPoints();
411434
// CheckSequenceLocksAtTip checks if the transaction will be final in the next block to be
412-
// created on top of the new chain. We use useExistingLockPoints=false so that, instead of
413-
// using the information in lp (which might now refer to a block that no longer exists in
414-
// the chain), it will update lp to contain LockPoints relevant to the new chain.
415-
if (!CheckSequenceLocksAtTip(m_chain.Tip(), view_mempool, tx, &lp, validLP)) {
416-
// If CheckSequenceLocksAtTip fails, remove the tx and don't depend on the LockPoints.
417-
return true;
418-
} else if (!validLP) {
419-
// If CheckSequenceLocksAtTip succeeded, it also updated the LockPoints.
420-
// Now update the mempool entry lockpoints as well.
421-
m_mempool->mapTx.modify(it, [&lp](CTxMemPoolEntry& e) { e.UpdateLockPoints(lp); });
435+
// created on top of the new chain.
436+
if (TestLockPointValidity(m_chain, lp)) {
437+
if (!CheckSequenceLocksAtTip(m_chain.Tip(), lp)) {
438+
return true;
439+
}
440+
} else {
441+
const CCoinsViewMemPool view_mempool{&CoinsTip(), *m_mempool};
442+
const std::optional<LockPoints> new_lock_points{CalculateLockPointsAtTip(m_chain.Tip(), view_mempool, tx)};
443+
if (new_lock_points.has_value() && CheckSequenceLocksAtTip(m_chain.Tip(), *new_lock_points)) {
444+
// Now update the mempool entry lockpoints as well.
445+
m_mempool->mapTx.modify(it, [&new_lock_points](CTxMemPoolEntry& e) { e.UpdateLockPoints(*new_lock_points); });
446+
} else {
447+
return true;
448+
}
422449
}
423450

424451
// If the transaction spends any coinbase outputs, it must be mature.
@@ -781,7 +808,6 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
781808
}
782809
}
783810
}
784-
LockPoints lp;
785811
m_view.SetBackend(m_viewmempool);
786812

787813
const CCoinsViewCache& coins_cache = m_active_chainstate.CoinsTip();
@@ -823,7 +849,8 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
823849
// be mined yet.
824850
// Pass in m_view which has all of the relevant inputs cached. Note that, since m_view's
825851
// backend was removed, it no longer pulls coins from the mempool.
826-
if (!CheckSequenceLocksAtTip(m_active_chainstate.m_chain.Tip(), m_view, tx, &lp)) {
852+
const std::optional<LockPoints> lock_points{CalculateLockPointsAtTip(m_active_chainstate.m_chain.Tip(), m_view, tx)};
853+
if (!lock_points.has_value() || !CheckSequenceLocksAtTip(m_active_chainstate.m_chain.Tip(), *lock_points)) {
827854
return state.Invalid(TxValidationResult::TX_PREMATURE_SPEND, "non-BIP68-final");
828855
}
829856

@@ -854,7 +881,7 @@ bool MemPoolAccept::PreChecks(ATMPArgs& args, Workspace& ws)
854881
}
855882

856883
entry.reset(new CTxMemPoolEntry(ptx, ws.m_base_fees, nAcceptTime, m_active_chainstate.m_chain.Height(),
857-
fSpendsCoinbase, nSigOps, lp));
884+
fSpendsCoinbase, nSigOps, lock_points.value()));
858885
ws.m_vsize = entry->GetTxSize();
859886

860887
if (nSigOps > MAX_STANDARD_TX_SIGOPS)
@@ -1930,11 +1957,21 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
19301957
return DISCONNECT_FAILED;
19311958
}
19321959

1960+
// Ignore blocks that contain transactions which are 'overwritten' by later transactions,
1961+
// unless those are already completely spent.
1962+
// See https://github.com/bitcoin/bitcoin/issues/22596 for additional information.
1963+
// Note: the blocks specified here are different than the ones used in ConnectBlock because DisconnectBlock
1964+
// unwinds the blocks in reverse. As a result, the inconsistency is not discovered until the earlier
1965+
// blocks with the duplicate coinbase transactions are disconnected.
1966+
bool fEnforceBIP30 = !((pindex->nHeight==91722 && pindex->GetBlockHash() == uint256S("0x00000000000271a2dc26e7667f8419f2e15416dc6955e5a6c6cdf3f2574dd08e")) ||
1967+
(pindex->nHeight==91812 && pindex->GetBlockHash() == uint256S("0x00000000000af0aed4792b1acee3d966af36cf5def14935db8de83d6f9306f2f")));
1968+
19331969
// undo transactions in reverse order
19341970
for (int i = block.vtx.size() - 1; i >= 0; i--) {
19351971
const CTransaction &tx = *(block.vtx[i]);
19361972
uint256 hash = tx.GetHash();
19371973
bool is_coinbase = tx.IsCoinBase();
1974+
bool is_bip30_exception = (is_coinbase && !fEnforceBIP30);
19381975

19391976
if (fAddressIndex) {
19401977
for (unsigned int k = tx.vout.size(); k-- > 0;) {
@@ -1963,7 +2000,9 @@ DisconnectResult CChainState::DisconnectBlock(const CBlock& block, const CBlockI
19632000
Coin coin;
19642001
bool is_spent = view.SpendCoin(out, &coin);
19652002
if (!is_spent || tx.vout[o] != coin.out || pindex->nHeight != coin.nHeight || is_coinbase != coin.fCoinBase) {
1966-
fClean = false; // transaction output mismatch
2003+
if (!is_bip30_exception) {
2004+
fClean = false; // transaction output mismatch
2005+
}
19672006
}
19682007
}
19692008
}

src/validation.h

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -281,27 +281,39 @@ int GetUTXOConfirmations(CChainState& active_chainstate, const COutPoint& outpoi
281281
bool CheckFinalTxAtTip(const CBlockIndex& active_chain_tip, const CTransaction& tx) EXCLUSIVE_LOCKS_REQUIRED(::cs_main);
282282

283283
/**
284-
* Check if transaction will be BIP68 final in the next block to be created on top of tip.
285-
* @param[in] tip Chain tip to check tx sequence locks against. For example,
286-
* the tip of the current active chain.
284+
* Calculate LockPoints required to check if transaction will be BIP68 final in the next block
285+
* to be created on top of tip.
286+
*
287+
* @param[in] tip Chain tip for which tx sequence locks are calculated. For
288+
* example, the tip of the current active chain.
287289
* @param[in] coins_view Any CCoinsView that provides access to the relevant coins for
288290
* checking sequence locks. For example, it can be a CCoinsViewCache
289291
* that isn't connected to anything but contains all the relevant
290292
* coins, or a CCoinsViewMemPool that is connected to the
291-
* mempool and chainstate UTXO set. In the latter case, the caller is
292-
* responsible for holding the appropriate locks to ensure that
293+
* mempool and chainstate UTXO set. In the latter case, the caller
294+
* is responsible for holding the appropriate locks to ensure that
293295
* calls to GetCoin() return correct coins.
296+
* @param[in] tx The transaction being evaluated.
297+
*
298+
* @returns The resulting height and time calculated and the hash of the block needed for
299+
* calculation, or std::nullopt if there is an error.
300+
*/
301+
std::optional<LockPoints> CalculateLockPointsAtTip(
302+
CBlockIndex* tip,
303+
const CCoinsView& coins_view,
304+
const CTransaction& tx);
305+
306+
/**
307+
* Check if transaction will be BIP68 final in the next block to be created on top of tip.
308+
* @param[in] tip Chain tip to check tx sequence locks against. For example,
309+
* the tip of the current active chain.
310+
* @param[in] lock_points LockPoints containing the height and time at which this
311+
* transaction is final.
294312
* Simulates calling SequenceLocks() with data from the tip passed in.
295-
* Optionally stores in LockPoints the resulting height and time calculated and the hash
296-
* of the block needed for calculation or skips the calculation and uses the LockPoints
297-
* passed in for evaluation.
298313
* The LockPoints should not be considered valid if CheckSequenceLocksAtTip returns false.
299314
*/
300315
bool CheckSequenceLocksAtTip(CBlockIndex* tip,
301-
const CCoinsView& coins_view,
302-
const CTransaction& tx,
303-
LockPoints* lp = nullptr,
304-
bool useExistingLockPoints = false);
316+
const LockPoints& lock_points);
305317

306318
/**
307319
* Closure representing one script verification

src/wallet/spend.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -778,7 +778,7 @@ static std::optional<CreatedTransactionResult> CreateTransactionInternal(
778778
}
779779
if (feeCalc.reason == FeeReason::FALLBACK && !wallet.m_allow_fallback_fee) {
780780
// eventually allow a fallback fee
781-
error = _("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable -fallbackfee.");
781+
error = strprintf(_("Fee estimation failed. Fallbackfee is disabled. Wait a few blocks or enable %s."), "-fallbackfee");
782782
return std::nullopt;
783783
}
784784

src/wallet/sqlite.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,6 @@
2323
namespace wallet {
2424
static constexpr int32_t WALLET_SCHEMA_VERSION = 0;
2525

26-
static Mutex g_sqlite_mutex;
27-
static int g_sqlite_count GUARDED_BY(g_sqlite_mutex) = 0;
28-
2926
static Span<const std::byte> SpanFromBlob(sqlite3_stmt* stmt, int col)
3027
{
3128
return {reinterpret_cast<const std::byte*>(sqlite3_column_blob(stmt, col)),
@@ -104,6 +101,9 @@ static void SetPragma(sqlite3* db, const std::string& key, const std::string& va
104101
}
105102
}
106103

104+
Mutex SQLiteDatabase::g_sqlite_mutex;
105+
int SQLiteDatabase::g_sqlite_count = 0;
106+
107107
SQLiteDatabase::SQLiteDatabase(const fs::path& dir_path, const fs::path& file_path, const DatabaseOptions& options, bool mock)
108108
: WalletDatabase(), m_mock(mock), m_dir_path(fs::PathToString(dir_path)), m_file_path(fs::PathToString(file_path)), m_use_unsafe_sync(options.use_unsafe_sync)
109109
{
@@ -167,6 +167,8 @@ SQLiteDatabase::~SQLiteDatabase()
167167

168168
void SQLiteDatabase::Cleanup() noexcept
169169
{
170+
AssertLockNotHeld(g_sqlite_mutex);
171+
170172
Close();
171173

172174
LOCK(g_sqlite_mutex);

src/wallet/sqlite.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef BITCOIN_WALLET_SQLITE_H
66
#define BITCOIN_WALLET_SQLITE_H
77

8+
#include <sync.h>
89
#include <wallet/db.h>
910

1011
#include <sqlite3.h>
@@ -63,7 +64,16 @@ class SQLiteDatabase : public WalletDatabase
6364

6465
const std::string m_file_path;
6566

66-
void Cleanup() noexcept;
67+
/**
68+
* This mutex protects SQLite initialization and shutdown.
69+
* sqlite3_config() and sqlite3_shutdown() are not thread-safe (sqlite3_initialize() is).
70+
* Concurrent threads that execute SQLiteDatabase::SQLiteDatabase() should have just one
71+
* of them do the init and the rest wait for it to complete before all can proceed.
72+
*/
73+
static Mutex g_sqlite_mutex;
74+
static int g_sqlite_count GUARDED_BY(g_sqlite_mutex);
75+
76+
void Cleanup() noexcept EXCLUSIVE_LOCKS_REQUIRED(!g_sqlite_mutex);
6777

6878
public:
6979
SQLiteDatabase() = delete;

0 commit comments

Comments
 (0)