Skip to content

Commit

Permalink
dcr: Make tip an atomic.
Browse files Browse the repository at this point in the history
  • Loading branch information
JoeGruffins committed Nov 20, 2024
1 parent 364e181 commit cbeaa2f
Show file tree
Hide file tree
Showing 5 changed files with 28 additions and 50 deletions.
48 changes: 15 additions & 33 deletions client/asset/dcr/dcr.go
Original file line number Diff line number Diff line change
Expand Up @@ -643,8 +643,8 @@ type ExchangeWallet struct {
oracleFees map[uint64]feeStamped // conf target => fee rate
oracleFailing bool

tipMtx sync.RWMutex
currentTip *block
handleTipMtx sync.Mutex
currentTip atomic.Value // *block

// Coins returned by Fund are cached for quick reference.
fundingMtx sync.RWMutex
Expand Down Expand Up @@ -1064,13 +1064,11 @@ func (dcr *ExchangeWallet) Connect(ctx context.Context) (*sync.WaitGroup, error)
}

// Initialize the best block.
dcr.tipMtx.Lock()
dcr.currentTip, err = dcr.getBestBlock(ctx)
tip := dcr.currentTip
dcr.tipMtx.Unlock()
tip, err := dcr.getBestBlock(ctx)
if err != nil {
return nil, fmt.Errorf("error initializing best block for DCR: %w", err)
}
dcr.currentTip.Store(tip)
dcr.startingBlocks.Store(uint64(tip.height))

dbCM, err := dcr.startTxHistoryDB(ctx)
Expand Down Expand Up @@ -3592,9 +3590,7 @@ func (dcr *ExchangeWallet) lookupTxOutput(ctx context.Context, txHash *chainhash
// LockTimeExpired returns true if the specified locktime has expired, making it
// possible to redeem the locked coins.
func (dcr *ExchangeWallet) LockTimeExpired(ctx context.Context, lockTime time.Time) (bool, error) {
dcr.tipMtx.RLock()
blockHash := dcr.currentTip.hash
dcr.tipMtx.RUnlock()
blockHash := dcr.cachedBestBlock().hash
hdr, err := dcr.wallet.GetBlockHeader(ctx, blockHash)
if err != nil {
return false, fmt.Errorf("unable to retrieve the block header: %w", err)
Expand Down Expand Up @@ -3657,9 +3653,7 @@ func (dcr *ExchangeWallet) FindRedemption(ctx context.Context, coinID, _ dex.Byt
if contractBlock == nil {
dcr.findRedemptionsInMempool([]outPoint{contractOutpoint})
} else {
dcr.tipMtx.RLock()
bestBlock := dcr.currentTip
dcr.tipMtx.RUnlock()
bestBlock := dcr.cachedBestBlock()
dcr.findRedemptionsInBlockRange(contractBlock.height, bestBlock.height, []outPoint{contractOutpoint})
}

Expand Down Expand Up @@ -4833,11 +4827,8 @@ func (dcr *ExchangeWallet) SyncStatus() (ss *asset.SyncStatus, err error) {
}

if wasSynced := dcr.previouslySynced.Swap(synced); synced && !wasSynced {
dcr.tipMtx.RLock()
tip := dcr.currentTip
dcr.tipMtx.RUnlock()

dcr.syncTxHistory(dcr.ctx, uint64(tip.height))
tip := dcr.cachedBestBlock()
go dcr.syncTxHistory(dcr.ctx, uint64(tip.height))
}
}()

Expand Down Expand Up @@ -6630,11 +6621,7 @@ func (dcr *ExchangeWallet) monitorBlocks(ctx context.Context) {
return
}

dcr.tipMtx.RLock()
sameTip := dcr.currentTip.hash.IsEqual(newTip.hash)
dcr.tipMtx.RUnlock()

if sameTip {
if dcr.cachedBestBlock().hash.IsEqual(newTip.hash) {
return
}

Expand Down Expand Up @@ -6682,9 +6669,7 @@ func (dcr *ExchangeWallet) monitorBlocks(ctx context.Context) {
// Mempool tx seen.
dcr.emitBalance()

dcr.tipMtx.RLock()
tip := dcr.currentTip
dcr.tipMtx.RUnlock()
tip := dcr.cachedBestBlock()
dcr.syncTxHistory(ctx, uint64(tip.height))
continue
}
Expand Down Expand Up @@ -6712,11 +6697,10 @@ func (dcr *ExchangeWallet) handleTipChange(ctx context.Context, newTipHash *chai
}

// Lock to avoid concurrent handleTipChange execution for simplicity.
dcr.tipMtx.Lock()
defer dcr.tipMtx.Unlock()
dcr.handleTipMtx.Lock()
defer dcr.handleTipMtx.Unlock()

prevTip := dcr.currentTip
dcr.currentTip = &block{newTipHeight, newTipHash}
prevTip := dcr.currentTip.Swap(&block{newTipHeight, newTipHash}).(*block)

dcr.log.Tracef("tip change: %d (%s) => %d (%s)", prevTip.height, prevTip.hash, newTipHeight, newTipHash)

Expand Down Expand Up @@ -6849,10 +6833,8 @@ func (dcr *ExchangeWallet) blockHeader(ctx context.Context, blockHash *chainhash
return blockHeader, true, validMainchain, nil
}

func (dcr *ExchangeWallet) cachedBestBlock() block {
dcr.tipMtx.RLock()
defer dcr.tipMtx.RUnlock()
return *dcr.currentTip
func (dcr *ExchangeWallet) cachedBestBlock() *block {
return dcr.currentTip.Load().(*block)
}

// wireBytes dumps the serialized transaction bytes.
Expand Down
12 changes: 5 additions & 7 deletions client/asset/dcr/dcr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -162,9 +162,8 @@ func tNewWalletMonitorBlocks(monitorBlocks bool) (*ExchangeWallet, *tRPCClient,
wallet.ctx = walletCtx

// Initialize the best block.
wallet.tipMtx.Lock()
wallet.currentTip, _ = wallet.getBestBlock(walletCtx)
wallet.tipMtx.Unlock()
tip, _ := wallet.getBestBlock(walletCtx)
wallet.currentTip.Store(tip)

if monitorBlocks {
go wallet.monitorBlocks(walletCtx)
Expand Down Expand Up @@ -4232,9 +4231,8 @@ func TestConfirmRedemption(t *testing.T) {
spenderTx := makeRawTx(inputs, nil)
node.blockchain.addRawTx(2, spenderTx)

wallet.tipMtx.Lock()
wallet.currentTip, _ = wallet.getBestBlock(wallet.ctx)
wallet.tipMtx.Unlock()
tip, _ := wallet.getBestBlock(wallet.ctx)
wallet.currentTip.Store(tip)

txFn := func(doErr []bool) func() (*walletjson.GetTransactionResult, error) {
var i int
Expand Down Expand Up @@ -5007,7 +5005,7 @@ func TestRescanSync(t *testing.T) {
defer shutdown()

const tip = 1000
wallet.currentTip = &block{height: tip}
wallet.currentTip.Store(&block{height: tip})

node.rawRes[methodSyncStatus], node.rawErr[methodSyncStatus] = json.Marshal(&walletjson.SyncStatusResult{
Synced: true,
Expand Down
2 changes: 1 addition & 1 deletion client/asset/dcr/externaltx.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ func (dcr *ExchangeWallet) lookupTxOutWithBlockFilters(ctx context.Context, op o
tip, err := dcr.getBestBlock(ctx)
if err != nil {
dcr.log.Errorf("getbestblock error %v", err)
*tip = dcr.cachedBestBlock()
tip = dcr.cachedBestBlock()
}
var confs uint32
if tip.height >= outputBlock.height { // slight possibility that the cached tip height is behind the output's block height
Expand Down
4 changes: 1 addition & 3 deletions client/asset/dcr/native_wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,7 @@ func (w *NativeWallet) transferAccount(ctx context.Context, toAcct string, fromA
// birthdayBlockHeight performs a binary search for the last block with a
// timestamp lower than the provided birthday.
func (w *NativeWallet) birthdayBlockHeight(ctx context.Context, bday uint64) int32 {
w.tipMtx.RLock()
tipHeight := w.currentTip.height
w.tipMtx.RUnlock()
tipHeight := w.cachedBestBlock().height
var err error
firstBlockAfterBday := sort.Search(int(tipHeight), func(blockHeightI int) bool {
if err != nil { // if we see any errors, just give up.
Expand Down
12 changes: 6 additions & 6 deletions client/asset/dcr/spv_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -956,12 +956,12 @@ func TestBirthdayBlockHeight(t *testing.T) {
dcrw.makeBlocks(0, tipHeight)
w := &NativeWallet{
ExchangeWallet: &ExchangeWallet{
wallet: spvw,
log: dex.StdOutLogger("T", dex.LevelInfo),
currentTip: &block{height: tipHeight},
wallet: spvw,
log: dex.StdOutLogger("T", dex.LevelInfo),
},
spvw: spvw,
}
w.currentTip.Store(&block{height: tipHeight})

if h := w.birthdayBlockHeight(tCtx, 5); h != 4 {
t.Fatalf("expected block 4, got %d", h)
Expand All @@ -982,12 +982,12 @@ func TestRescan(t *testing.T) {
spvw, dcrw := tNewSpvWallet()
w := &NativeWallet{
ExchangeWallet: &ExchangeWallet{
wallet: spvw,
log: dex.StdOutLogger("T", dex.LevelInfo),
currentTip: &block{height: tipHeight},
wallet: spvw,
log: dex.StdOutLogger("T", dex.LevelInfo),
},
spvw: spvw,
}
w.currentTip.Store(&block{height: tipHeight})

dcrw.makeBlocks(0, tipHeight)

Expand Down

0 comments on commit cbeaa2f

Please sign in to comment.