Skip to content

Commit

Permalink
Log transaction changes for non-initial rescans
Browse files Browse the repository at this point in the history
For all rescans except for the initial one when a wallet is first synced
(during reseed, or after process restart), log those transactions that are
changed status from unmined to mined in a block, and any newly-observed
transaction that is added to the wallet.

These later rescans are typically done to fix a problem syncing blocks and/or
transactions, and these transaction hashes should be useful information to
discover what previously went wrong.
  • Loading branch information
jrick committed Aug 4, 2023
1 parent a38abe2 commit d200acb
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 15 deletions.
32 changes: 30 additions & 2 deletions wallet/rescan.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,30 @@ func (f *RescanFilter) RemoveUnspentOutPoint(op *wire.OutPoint) {
delete(f.unspent, *op)
}

// logRescannedTransactions logs newly-observed transactions that were added
// by the rescan, and transactions that were changed from unmined to mined.
// It should only be called if the wallet field logRescannedTransactions is
// false (just after wallet creation) and will be set true for all later
// rescans.
func (w *Wallet) logRescannedTxs(txmgrNs walletdb.ReadBucket, height int32, tx *wire.MsgTx) {
txHash := tx.TxHash()
haveMined, haveUnmined := w.txStore.ExistsTxMinedOrUnmined(txmgrNs, &txHash)
if haveUnmined {
log.Infof("Rescan of block %d discovered previously unmined "+
"transaction %v", height, &txHash)
} else if !haveMined {
log.Infof("Rescan of block %d discovered new transaction %v",
height, &txHash)
}
}

// saveRescanned records transactions from a rescanned block. This
// does not update the network backend with data to watch for future
// relevant transactions as the rescanner is assumed to handle this
// task.
func (w *Wallet) saveRescanned(ctx context.Context, hash *chainhash.Hash, txs []*wire.MsgTx) error {
func (w *Wallet) saveRescanned(ctx context.Context, hash *chainhash.Hash,
txs []*wire.MsgTx, logTxs bool) error {

const op errors.Op = "wallet.saveRescanned"

defer w.lockedOutpointMu.Unlock()
Expand All @@ -170,6 +189,10 @@ func (w *Wallet) saveRescanned(ctx context.Context, hash *chainhash.Hash, txs []
}

for _, tx := range txs {
if logTxs {
w.logRescannedTxs(txmgrNs, blockMeta.Height, tx)
}

rec, err := udb.NewTxRecordFromMsgTx(tx, time.Now())
if err != nil {
return err
Expand All @@ -194,6 +217,11 @@ func (w *Wallet) saveRescanned(ctx context.Context, hash *chainhash.Hash, txs []
func (w *Wallet) rescan(ctx context.Context, n NetworkBackend,
startHash *chainhash.Hash, height int32, p chan<- RescanProgress) error {

w.logRescannedTransactionsMu.Lock()
logTxs := w.logRescannedTransactions
w.logRescannedTransactions = true
w.logRescannedTransactionsMu.Unlock()

blockHashStorage := make([]chainhash.Hash, maxBlocksPerRescan)
rescanFrom := *startHash
inclusive := true
Expand Down Expand Up @@ -230,7 +258,7 @@ func (w *Wallet) rescan(ctx context.Context, n NetworkBackend,
}
log.Infof("Rescanning block range [%v, %v]...", height, through)
saveRescanned := func(block *chainhash.Hash, txs []*wire.MsgTx) error {
return w.saveRescanned(ctx, block, txs)
return w.saveRescanned(ctx, block, txs, logTxs)
}
err = n.Rescan(ctx, rescanBlocks, saveRescanned)
if err != nil {
Expand Down
16 changes: 9 additions & 7 deletions wallet/udb/txquery.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,17 +276,19 @@ func (s *Store) Tx(ns walletdb.ReadBucket, txHash *chainhash.Hash) (*wire.MsgTx,

// ExistsTx checks to see if a transaction exists in the database.
func (s *Store) ExistsTx(ns walletdb.ReadBucket, txHash *chainhash.Hash) bool {
// First, check whether there exists an unmined transaction with this
// hash. Use it if found.
mined, unmined := s.ExistsTxMinedOrUnmined(ns, txHash)
return mined || unmined
}

// ExistsTxMinedOrUnmined checks if a transaction is recorded as a mined or
// unmined transaction.
func (s *Store) ExistsTxMinedOrUnmined(ns walletdb.ReadBucket, txHash *chainhash.Hash) (mined, unmined bool) {
v := existsRawUnmined(ns, txHash[:])
if v != nil {
return true
return false, true
}

// Otherwise, if there exists a mined transaction with this matching
// hash, skip over to the newest and begin fetching the msgTx.
_, v = latestTxRecord(ns, txHash[:])
return v != nil
return v != nil, false
}

// ExistsUTXO checks to see if op refers to an unspent transaction output or a
Expand Down
14 changes: 8 additions & 6 deletions wallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,14 @@ type Wallet struct {
lockedOutpoints map[outpoint]struct{}
lockedOutpointMu sync.Mutex

relayFee dcrutil.Amount
relayFeeMu sync.Mutex
allowHighFees bool
disableCoinTypeUpgrades bool
recentlyPublished map[chainhash.Hash]struct{}
recentlyPublishedMu sync.Mutex
relayFee dcrutil.Amount
relayFeeMu sync.Mutex
allowHighFees bool
disableCoinTypeUpgrades bool
recentlyPublished map[chainhash.Hash]struct{}
recentlyPublishedMu sync.Mutex
logRescannedTransactions bool
logRescannedTransactionsMu sync.Mutex

// Internal address handling.
ticketAddress stdaddr.StakeAddress
Expand Down

0 comments on commit d200acb

Please sign in to comment.