lnd/sweep/backend_mock_test.go
Joost Jager e01600fdb8
sweep: add wallet inputs to reach dust limit
This commit allows sweeper to sweep inputs that on its own are not able
to form a sweep transaction that meets the dust limit.

This functionality is useful for sweeping small outputs. In the future,
this will be particularly important to sweep anchors. Anchors will
typically be spent with a relatively large fee to pay for the parent tx.
It will then be necessary to attach an additional wallet utxo.
2019-12-17 22:00:39 +01:00

142 lines
3.5 KiB
Go

package sweep
import (
"sync"
"testing"
"time"
"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 {
t *testing.T
lock sync.Mutex
notifier *MockNotifier
confirmedSpendInputs map[wire.OutPoint]struct{}
unconfirmedTxes map[chainhash.Hash]*wire.MsgTx
unconfirmedSpendInputs map[wire.OutPoint]struct{}
publishChan chan wire.MsgTx
walletUtxos []*lnwallet.Utxo
}
func newMockBackend(t *testing.T, notifier *MockNotifier) *mockBackend {
return &mockBackend{
t: t,
notifier: notifier,
unconfirmedTxes: make(map[chainhash.Hash]*wire.MsgTx),
confirmedSpendInputs: make(map[wire.OutPoint]struct{}),
unconfirmedSpendInputs: make(map[wire.OutPoint]struct{}),
publishChan: make(chan wire.MsgTx, 2),
}
}
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) PublishTransaction(tx *wire.MsgTx) error {
log.Tracef("Publishing tx %v", tx.TxHash())
err := b.publishTransaction(tx)
select {
case b.publishChan <- *tx:
case <-time.After(defaultTestTimeout):
b.t.Fatalf("unexpected tx published")
}
return err
}
func (b *mockBackend) ListUnspentWitness(minconfirms, maxconfirms int32) (
[]*lnwallet.Utxo, error) {
return b.walletUtxos, nil
}
func (b *mockBackend) WithCoinSelectLock(f func() error) error {
return f()
}
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
}