@@ -164,6 +164,7 @@ bool RemoveWallet(WalletContext& context, const std::shared_ptr<CWallet>& wallet
164
164
165
165
interfaces::Chain& chain = wallet->chain ();
166
166
std::string name = wallet->GetName ();
167
+ WITH_LOCK (wallet->cs_wallet , wallet->WriteBestBlock ());
167
168
168
169
// Unregister with the validation interface which also drops shared pointers.
169
170
wallet->m_chain_notifications_handler .reset ();
@@ -652,15 +653,20 @@ bool CWallet::ChangeWalletPassphrase(const SecureString& strOldWalletPassphrase,
652
653
return false ;
653
654
}
654
655
655
- void CWallet::chainStateFlushed (ChainstateRole role, const CBlockLocator& loc )
656
+ void CWallet::SetLastBlockProcessedInMem ( int block_height, uint256 block_hash )
656
657
{
657
- // Don't update the best block until the chain is attached so that in case of a shutdown,
658
- // the rescan will be restarted at next startup.
659
- if (m_attaching_chain || role == ChainstateRole::BACKGROUND) {
660
- return ;
661
- }
662
- WalletBatch batch (GetDatabase ());
663
- batch.WriteBestBlock (loc);
658
+ AssertLockHeld (cs_wallet);
659
+
660
+ m_last_block_processed = block_hash;
661
+ m_last_block_processed_height = block_height;
662
+ }
663
+
664
+ void CWallet::SetLastBlockProcessed (int block_height, uint256 block_hash)
665
+ {
666
+ AssertLockHeld (cs_wallet);
667
+
668
+ SetLastBlockProcessedInMem (block_height, block_hash);
669
+ WriteBestBlock ();
664
670
}
665
671
666
672
void CWallet::SetMinVersion (enum WalletFeature nVersion, WalletBatch* batch_in)
@@ -1377,15 +1383,16 @@ void CWallet::RecursiveUpdateTxState(WalletBatch* batch, const Txid& tx_hash, co
1377
1383
}
1378
1384
}
1379
1385
1380
- void CWallet::SyncTransaction (const CTransactionRef& ptx, const SyncTxState& state, bool update_tx, bool rescanning_old_block)
1386
+ bool CWallet::SyncTransaction (const CTransactionRef& ptx, const SyncTxState& state, bool update_tx, bool rescanning_old_block)
1381
1387
{
1382
1388
if (!AddToWalletIfInvolvingMe (ptx, state, update_tx, rescanning_old_block))
1383
- return ; // Not one of ours
1389
+ return false ; // Not one of ours
1384
1390
1385
1391
// If a transaction changes 'conflicted' state, that changes the balance
1386
1392
// available of the outputs it spends. So force those to be
1387
1393
// recomputed, also:
1388
1394
MarkInputsDirty (ptx);
1395
+ return true ;
1389
1396
}
1390
1397
1391
1398
void CWallet::transactionAddedToMempool (const CTransactionRef& tx) {
@@ -1472,18 +1479,25 @@ void CWallet::blockConnected(ChainstateRole role, const interfaces::BlockInfo& b
1472
1479
assert (block.data );
1473
1480
LOCK (cs_wallet);
1474
1481
1475
- m_last_block_processed_height = block.height ;
1476
- m_last_block_processed = block.hash ;
1482
+ // Update the best block in memory first. This will set the best block's height, which is
1483
+ // needed by MarkConflicted.
1484
+ SetLastBlockProcessedInMem (block.height , block.hash );
1477
1485
1478
1486
// No need to scan block if it was created before the wallet birthday.
1479
1487
// Uses chain max time and twice the grace period to adjust time for block time variability.
1480
1488
if (block.chain_time_max < m_birth_time.load () - (TIMESTAMP_WINDOW * 2 )) return ;
1481
1489
1482
1490
// Scan block
1491
+ bool wallet_updated = false ;
1483
1492
for (size_t index = 0 ; index < block.data ->vtx .size (); index++) {
1484
- SyncTransaction (block.data ->vtx [index], TxStateConfirmed{block.hash , block.height , static_cast <int >(index)});
1493
+ wallet_updated |= SyncTransaction (block.data ->vtx [index], TxStateConfirmed{block.hash , block.height , static_cast <int >(index)});
1485
1494
transactionRemovedFromMempool (block.data ->vtx [index], MemPoolRemovalReason::BLOCK);
1486
1495
}
1496
+
1497
+ // Update on disk if this block resulted in us updating a tx, or periodically every 144 blocks (~1 day)
1498
+ if (wallet_updated || block.height % 144 == 0 ) {
1499
+ WriteBestBlock ();
1500
+ }
1487
1501
}
1488
1502
1489
1503
void CWallet::blockDisconnected (const interfaces::BlockInfo& block)
@@ -1495,9 +1509,6 @@ void CWallet::blockDisconnected(const interfaces::BlockInfo& block)
1495
1509
// be unconfirmed, whether or not the transaction is added back to the mempool.
1496
1510
// User may have to call abandontransaction again. It may be addressed in the
1497
1511
// future with a stickier abandoned state or even removing abandontransaction call.
1498
- m_last_block_processed_height = block.height - 1 ;
1499
- m_last_block_processed = *Assert (block.prev_hash );
1500
-
1501
1512
int disconnect_height = block.height ;
1502
1513
1503
1514
for (size_t index = 0 ; index < block.data ->vtx .size (); index++) {
@@ -1531,6 +1542,9 @@ void CWallet::blockDisconnected(const interfaces::BlockInfo& block)
1531
1542
}
1532
1543
}
1533
1544
}
1545
+
1546
+ // Update the best block
1547
+ SetLastBlockProcessed (block.height - 1 , *Assert (block.prev_hash ));
1534
1548
}
1535
1549
1536
1550
void CWallet::updatedBlockTip ()
@@ -2885,6 +2899,8 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
2885
2899
!walletInstance->IsWalletFlagSet (WALLET_FLAG_BLANK_WALLET);
2886
2900
if (fFirstRun )
2887
2901
{
2902
+ LOCK (walletInstance->cs_wallet );
2903
+
2888
2904
// ensure this wallet.dat can only be opened by clients supporting HD with chain split and expects no default key
2889
2905
walletInstance->SetMinVersion (FEATURE_LATEST);
2890
2906
@@ -2894,7 +2910,6 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
2894
2910
assert (walletInstance->IsWalletFlagSet (WALLET_FLAG_DESCRIPTORS));
2895
2911
2896
2912
if ((wallet_creation_flags & WALLET_FLAG_EXTERNAL_SIGNER) || !(wallet_creation_flags & (WALLET_FLAG_DISABLE_PRIVATE_KEYS | WALLET_FLAG_BLANK_WALLET))) {
2897
- LOCK (walletInstance->cs_wallet );
2898
2913
if (walletInstance->IsWalletFlagSet (WALLET_FLAG_DESCRIPTORS)) {
2899
2914
walletInstance->SetupDescriptorScriptPubKeyMans ();
2900
2915
// SetupDescriptorScriptPubKeyMans already calls SetupGeneration for us so we don't need to call SetupGeneration separately
@@ -2910,7 +2925,10 @@ std::shared_ptr<CWallet> CWallet::Create(WalletContext& context, const std::stri
2910
2925
}
2911
2926
2912
2927
if (chain) {
2913
- walletInstance->chainStateFlushed (ChainstateRole::NORMAL, chain->getTipLocator ());
2928
+ std::optional<int > tip_height = chain->getHeight ();
2929
+ if (tip_height) {
2930
+ walletInstance->SetLastBlockProcessed (*tip_height, chain->getBlockHash (*tip_height));
2931
+ }
2914
2932
}
2915
2933
} else if (wallet_creation_flags & WALLET_FLAG_DISABLE_PRIVATE_KEYS) {
2916
2934
// Make it impossible to disable private keys after creation
@@ -3112,11 +3130,6 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
3112
3130
// be pending on the validation-side until lock release. Blocks that are connected while the
3113
3131
// rescan is ongoing will not be processed in the rescan but with the block connected notifications,
3114
3132
// so the wallet will only be completeley synced after the notifications delivery.
3115
- // chainStateFlushed notifications are ignored until the rescan is finished
3116
- // so that in case of a shutdown event, the rescan will be repeated at the next start.
3117
- // This is temporary until rescan and notifications delivery are unified under same
3118
- // interface.
3119
- walletInstance->m_attaching_chain = true ; // ignores chainStateFlushed notifications
3120
3133
walletInstance->m_chain_notifications_handler = walletInstance->chain ().handleNotifications (walletInstance);
3121
3134
3122
3135
// If rescan_required = true, rescan_height remains equal to 0
@@ -3193,15 +3206,21 @@ bool CWallet::AttachChain(const std::shared_ptr<CWallet>& walletInstance, interf
3193
3206
3194
3207
{
3195
3208
WalletRescanReserver reserver (*walletInstance);
3196
- if (!reserver.reserve () || (ScanResult::SUCCESS != walletInstance->ScanForWalletTransactions (chain.getBlockHash (rescan_height), rescan_height, /* max_height=*/ {}, reserver, /* fUpdate=*/ true , /* save_progress=*/ true ).status )) {
3209
+ if (!reserver.reserve ()) {
3210
+ error = _ (" Failed to acquire rescan reserver during wallet initialization" );
3211
+ return false ;
3212
+ }
3213
+ ScanResult scan_res = walletInstance->ScanForWalletTransactions (chain.getBlockHash (rescan_height), rescan_height, /* max_height=*/ {}, reserver, /* fUpdate=*/ true , /* save_progress=*/ true );
3214
+ if (ScanResult::SUCCESS != scan_res.status ) {
3197
3215
error = _ (" Failed to rescan the wallet during initialization" );
3198
3216
return false ;
3199
3217
}
3218
+ // Set and update the best block record
3219
+ // Set last block scanned as the last block processed as it may be different in case the case of a reorg.
3220
+ // Also save the best block locator because rescanning only updates it intermittently.
3221
+ walletInstance->SetLastBlockProcessed (*scan_res.last_scanned_height , scan_res.last_scanned_block );
3200
3222
}
3201
- walletInstance->m_attaching_chain = false ;
3202
- walletInstance->chainStateFlushed (ChainstateRole::NORMAL, chain.getTipLocator ());
3203
3223
}
3204
- walletInstance->m_attaching_chain = false ;
3205
3224
3206
3225
return true ;
3207
3226
}
@@ -3261,14 +3280,7 @@ void CWallet::postInitProcess()
3261
3280
3262
3281
bool CWallet::BackupWallet (const std::string& strDest) const
3263
3282
{
3264
- if (m_chain) {
3265
- CBlockLocator loc;
3266
- WITH_LOCK (cs_wallet, chain ().findBlock (m_last_block_processed, FoundBlock ().locator (loc)));
3267
- if (!loc.IsNull ()) {
3268
- WalletBatch batch (GetDatabase ());
3269
- batch.WriteBestBlock (loc);
3270
- }
3271
- }
3283
+ WITH_LOCK (cs_wallet, WriteBestBlock ());
3272
3284
return GetDatabase ().Backup (strDest);
3273
3285
}
3274
3286
@@ -4197,11 +4209,6 @@ util::Result<MigrationResult> MigrateLegacyToDescriptor(const std::string& walle
4197
4209
return util::Error{_ (" Error: This wallet is already a descriptor wallet" )};
4198
4210
}
4199
4211
4200
- // Flush chain state before unloading wallet
4201
- CBlockLocator locator;
4202
- WITH_LOCK (wallet->cs_wallet , context.chain ->findBlock (wallet->GetLastBlockHash (), FoundBlock ().locator (locator)));
4203
- if (!locator.IsNull ()) wallet->chainStateFlushed (ChainstateRole::NORMAL, locator);
4204
-
4205
4212
if (!RemoveWallet (context, wallet, /* load_on_start=*/ std::nullopt, warnings)) {
4206
4213
return util::Error{_ (" Unable to unload the wallet before migrating" )};
4207
4214
}
@@ -4450,4 +4457,17 @@ std::optional<CKey> CWallet::GetKey(const CKeyID& keyid) const
4450
4457
}
4451
4458
return std::nullopt;
4452
4459
}
4460
+
4461
+ void CWallet::WriteBestBlock () const
4462
+ {
4463
+ AssertLockHeld (cs_wallet);
4464
+
4465
+ if (!m_last_block_processed.IsNull ()) {
4466
+ CBlockLocator loc;
4467
+ chain ().findBlock (m_last_block_processed, FoundBlock ().locator (loc));
4468
+
4469
+ WalletBatch batch (GetDatabase ());
4470
+ batch.WriteBestBlock (loc);
4471
+ }
4472
+ }
4453
4473
} // namespace wallet
0 commit comments