github.com/decred/dcrlnd@v0.7.6/lntest/mock/spendnotifier.go (about) 1 package mock 2 3 import ( 4 "sync" 5 6 "github.com/decred/dcrd/wire" 7 8 "github.com/decred/dcrlnd/chainntnfs" 9 ) 10 11 // SpendNotifier extends the mock.ChainNotifier so that spend 12 // notifications can be triggered and delivered to subscribers. 13 type SpendNotifier struct { 14 *ChainNotifier 15 spendMap map[wire.OutPoint][]chan *chainntnfs.SpendDetail 16 spends map[wire.OutPoint]*chainntnfs.SpendDetail 17 mtx sync.Mutex 18 } 19 20 // MakeMockSpendNotifier creates a SpendNotifier. 21 func MakeMockSpendNotifier() *SpendNotifier { 22 return &SpendNotifier{ 23 ChainNotifier: &ChainNotifier{ 24 SpendChan: make(chan *chainntnfs.SpendDetail), 25 EpochChan: make(chan *chainntnfs.BlockEpoch), 26 ConfChan: make(chan *chainntnfs.TxConfirmation), 27 }, 28 spendMap: make(map[wire.OutPoint][]chan *chainntnfs.SpendDetail), 29 spends: make(map[wire.OutPoint]*chainntnfs.SpendDetail), 30 } 31 } 32 33 // RegisterSpendNtfn registers a spend notification for a specified outpoint. 34 func (s *SpendNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint, 35 _ []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) { 36 37 s.mtx.Lock() 38 defer s.mtx.Unlock() 39 40 spendChan := make(chan *chainntnfs.SpendDetail, 1) 41 if detail, ok := s.spends[*outpoint]; ok { 42 // Deliver spend immediately if details are already known. 43 spendChan <- &chainntnfs.SpendDetail{ 44 SpentOutPoint: detail.SpentOutPoint, 45 SpendingHeight: detail.SpendingHeight, 46 SpendingTx: detail.SpendingTx, 47 SpenderTxHash: detail.SpenderTxHash, 48 SpenderInputIndex: detail.SpenderInputIndex, 49 } 50 } else { 51 // Otherwise, queue the notification for delivery if the spend 52 // is ever received. 53 s.spendMap[*outpoint] = append(s.spendMap[*outpoint], spendChan) 54 } 55 56 return &chainntnfs.SpendEvent{ 57 Spend: spendChan, 58 Cancel: func() {}, 59 }, nil 60 } 61 62 // Spend dispatches SpendDetails to all subscribers of the outpoint. The details 63 // will includethe transaction and height provided by the caller. 64 func (s *SpendNotifier) Spend(outpoint *wire.OutPoint, height int32, 65 txn *wire.MsgTx) { 66 67 s.mtx.Lock() 68 defer s.mtx.Unlock() 69 70 var inputIndex uint32 71 for i, in := range txn.TxIn { 72 if in.PreviousOutPoint == *outpoint { 73 inputIndex = uint32(i) 74 } 75 } 76 77 txnHash := txn.TxHash() 78 details := &chainntnfs.SpendDetail{ 79 SpentOutPoint: outpoint, 80 SpendingHeight: height, 81 SpendingTx: txn, 82 SpenderTxHash: &txnHash, 83 SpenderInputIndex: inputIndex, 84 } 85 86 // Cache details in case of late registration. 87 if _, ok := s.spends[*outpoint]; !ok { 88 s.spends[*outpoint] = details 89 } 90 91 // Deliver any backlogged spend notifications. 92 if spendChans, ok := s.spendMap[*outpoint]; ok { 93 delete(s.spendMap, *outpoint) 94 for _, spendChan := range spendChans { 95 spendChan <- &chainntnfs.SpendDetail{ 96 SpentOutPoint: details.SpentOutPoint, 97 SpendingHeight: details.SpendingHeight, 98 SpendingTx: details.SpendingTx, 99 SpenderTxHash: details.SpenderTxHash, 100 SpenderInputIndex: details.SpenderInputIndex, 101 } 102 } 103 } 104 } 105 106 // HasPenderNotification checks whether the given outpoint has at least one 107 // client registered to receive spend notifications for the given outpoint. 108 func (m *SpendNotifier) HasSpenderNotification(outpoint *wire.OutPoint) bool { 109 m.mtx.Lock() 110 _, ok := m.spendMap[*outpoint] 111 m.mtx.Unlock() 112 return ok 113 }