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  }