111 lines
2.8 KiB
Go
111 lines
2.8 KiB
Go
|
package sweep
|
||
|
|
||
|
import (
|
||
|
"sync"
|
||
|
|
||
|
"github.com/btcsuite/btcd/chaincfg/chainhash"
|
||
|
"github.com/btcsuite/btcd/wire"
|
||
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||
|
)
|
||
|
|
||
|
// mockBackend simulates a chain backend for realistic behaviour in unit tests
|
||
|
// around double spends.
|
||
|
type mockBackend struct {
|
||
|
lock sync.Mutex
|
||
|
|
||
|
notifier *MockNotifier
|
||
|
|
||
|
confirmedSpendInputs map[wire.OutPoint]struct{}
|
||
|
|
||
|
unconfirmedTxes map[chainhash.Hash]*wire.MsgTx
|
||
|
unconfirmedSpendInputs map[wire.OutPoint]struct{}
|
||
|
}
|
||
|
|
||
|
func newMockBackend(notifier *MockNotifier) *mockBackend {
|
||
|
return &mockBackend{
|
||
|
notifier: notifier,
|
||
|
unconfirmedTxes: make(map[chainhash.Hash]*wire.MsgTx),
|
||
|
confirmedSpendInputs: make(map[wire.OutPoint]struct{}),
|
||
|
unconfirmedSpendInputs: make(map[wire.OutPoint]struct{}),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (b *mockBackend) publishTransaction(tx *wire.MsgTx) error {
|
||
|
b.lock.Lock()
|
||
|
defer b.lock.Unlock()
|
||
|
|
||
|
txHash := tx.TxHash()
|
||
|
if _, ok := b.unconfirmedTxes[txHash]; ok {
|
||
|
// Tx already exists
|
||
|
testLog.Tracef("mockBackend duplicate tx %v", tx.TxHash())
|
||
|
return lnwallet.ErrDoubleSpend
|
||
|
}
|
||
|
|
||
|
for _, in := range tx.TxIn {
|
||
|
if _, ok := b.unconfirmedSpendInputs[in.PreviousOutPoint]; ok {
|
||
|
// Double spend
|
||
|
testLog.Tracef("mockBackend double spend tx %v", tx.TxHash())
|
||
|
return lnwallet.ErrDoubleSpend
|
||
|
}
|
||
|
|
||
|
if _, ok := b.confirmedSpendInputs[in.PreviousOutPoint]; ok {
|
||
|
// Already included in block
|
||
|
testLog.Tracef("mockBackend already in block tx %v", tx.TxHash())
|
||
|
return lnwallet.ErrDoubleSpend
|
||
|
}
|
||
|
}
|
||
|
|
||
|
b.unconfirmedTxes[txHash] = tx
|
||
|
for _, in := range tx.TxIn {
|
||
|
b.unconfirmedSpendInputs[in.PreviousOutPoint] = struct{}{}
|
||
|
}
|
||
|
|
||
|
testLog.Tracef("mockBackend publish tx %v", tx.TxHash())
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (b *mockBackend) deleteUnconfirmed(txHash chainhash.Hash) {
|
||
|
b.lock.Lock()
|
||
|
defer b.lock.Unlock()
|
||
|
|
||
|
tx, ok := b.unconfirmedTxes[txHash]
|
||
|
if !ok {
|
||
|
// Tx already exists
|
||
|
testLog.Errorf("mockBackend delete tx not existing %v", txHash)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
testLog.Tracef("mockBackend delete tx %v", tx.TxHash())
|
||
|
delete(b.unconfirmedTxes, txHash)
|
||
|
for _, in := range tx.TxIn {
|
||
|
delete(b.unconfirmedSpendInputs, in.PreviousOutPoint)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (b *mockBackend) mine() {
|
||
|
b.lock.Lock()
|
||
|
defer b.lock.Unlock()
|
||
|
|
||
|
notifications := make(map[wire.OutPoint]*wire.MsgTx)
|
||
|
for _, tx := range b.unconfirmedTxes {
|
||
|
testLog.Tracef("mockBackend mining tx %v", tx.TxHash())
|
||
|
for _, in := range tx.TxIn {
|
||
|
b.confirmedSpendInputs[in.PreviousOutPoint] = struct{}{}
|
||
|
notifications[in.PreviousOutPoint] = tx
|
||
|
}
|
||
|
}
|
||
|
b.unconfirmedSpendInputs = make(map[wire.OutPoint]struct{})
|
||
|
b.unconfirmedTxes = make(map[chainhash.Hash]*wire.MsgTx)
|
||
|
|
||
|
for outpoint, tx := range notifications {
|
||
|
testLog.Tracef("mockBackend delivering spend ntfn for %v",
|
||
|
outpoint)
|
||
|
b.notifier.SpendOutpoint(outpoint, *tx)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (b *mockBackend) isDone() bool {
|
||
|
return len(b.unconfirmedTxes) == 0
|
||
|
}
|