lnd/lntest/mock/spendnotifier.go
2022-02-10 11:02:01 +01:00

104 lines
2.9 KiB
Go

package mock
import (
"sync"
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/chainntnfs"
)
// SpendNotifier extends the mock.ChainNotifier so that spend
// notifications can be triggered and delivered to subscribers.
type SpendNotifier struct {
*ChainNotifier
spendMap map[wire.OutPoint][]chan *chainntnfs.SpendDetail
spends map[wire.OutPoint]*chainntnfs.SpendDetail
mtx sync.Mutex
}
// MakeMockSpendNotifier creates a SpendNotifier.
func MakeMockSpendNotifier() *SpendNotifier {
return &SpendNotifier{
ChainNotifier: &ChainNotifier{
SpendChan: make(chan *chainntnfs.SpendDetail),
EpochChan: make(chan *chainntnfs.BlockEpoch),
ConfChan: make(chan *chainntnfs.TxConfirmation),
},
spendMap: make(map[wire.OutPoint][]chan *chainntnfs.SpendDetail),
spends: make(map[wire.OutPoint]*chainntnfs.SpendDetail),
}
}
// RegisterSpendNtfn registers a spend notification for a specified outpoint.
func (s *SpendNotifier) RegisterSpendNtfn(outpoint *wire.OutPoint,
_ []byte, heightHint uint32) (*chainntnfs.SpendEvent, error) {
s.mtx.Lock()
defer s.mtx.Unlock()
spendChan := make(chan *chainntnfs.SpendDetail, 1)
if detail, ok := s.spends[*outpoint]; ok {
// Deliver spend immediately if details are already known.
spendChan <- &chainntnfs.SpendDetail{
SpentOutPoint: detail.SpentOutPoint,
SpendingHeight: detail.SpendingHeight,
SpendingTx: detail.SpendingTx,
SpenderTxHash: detail.SpenderTxHash,
SpenderInputIndex: detail.SpenderInputIndex,
}
} else {
// Otherwise, queue the notification for delivery if the spend
// is ever received.
s.spendMap[*outpoint] = append(s.spendMap[*outpoint], spendChan)
}
return &chainntnfs.SpendEvent{
Spend: spendChan,
Cancel: func() {},
}, nil
}
// Spend dispatches SpendDetails to all subscribers of the outpoint. The details
// will includethe transaction and height provided by the caller.
func (s *SpendNotifier) Spend(outpoint *wire.OutPoint, height int32,
txn *wire.MsgTx) {
s.mtx.Lock()
defer s.mtx.Unlock()
var inputIndex uint32
for i, in := range txn.TxIn {
if in.PreviousOutPoint == *outpoint {
inputIndex = uint32(i)
}
}
txnHash := txn.TxHash()
details := &chainntnfs.SpendDetail{
SpentOutPoint: outpoint,
SpendingHeight: height,
SpendingTx: txn,
SpenderTxHash: &txnHash,
SpenderInputIndex: inputIndex,
}
// Cache details in case of late registration.
if _, ok := s.spends[*outpoint]; !ok {
s.spends[*outpoint] = details
}
// Deliver any backlogged spend notifications.
if spendChans, ok := s.spendMap[*outpoint]; ok {
delete(s.spendMap, *outpoint)
for _, spendChan := range spendChans {
spendChan <- &chainntnfs.SpendDetail{
SpentOutPoint: details.SpentOutPoint,
SpendingHeight: details.SpendingHeight,
SpendingTx: details.SpendingTx,
SpenderTxHash: details.SpenderTxHash,
SpenderInputIndex: details.SpenderInputIndex,
}
}
}
}