github.com/decred/dcrlnd@v0.7.6/sweep/backend_mock_test.go (about) 1 package sweep 2 3 import ( 4 "sync" 5 "testing" 6 "time" 7 8 "github.com/decred/dcrd/chaincfg/chainhash" 9 "github.com/decred/dcrd/wire" 10 "github.com/decred/dcrlnd/lnwallet" 11 ) 12 13 // mockBackend simulates a chain backend for realistic behaviour in unit tests 14 // around double spends. 15 type mockBackend struct { 16 t *testing.T 17 18 lock sync.Mutex 19 20 notifier *MockNotifier 21 22 confirmedSpendInputs map[wire.OutPoint]struct{} 23 24 unconfirmedTxes map[chainhash.Hash]*wire.MsgTx 25 unconfirmedSpendInputs map[wire.OutPoint]struct{} 26 27 publishChan chan wire.MsgTx 28 29 walletUtxos []*lnwallet.Utxo 30 utxoCnt int 31 } 32 33 func newMockBackend(t *testing.T, notifier *MockNotifier) *mockBackend { 34 return &mockBackend{ 35 t: t, 36 notifier: notifier, 37 unconfirmedTxes: make(map[chainhash.Hash]*wire.MsgTx), 38 confirmedSpendInputs: make(map[wire.OutPoint]struct{}), 39 unconfirmedSpendInputs: make(map[wire.OutPoint]struct{}), 40 publishChan: make(chan wire.MsgTx, 2), 41 } 42 } 43 44 func (b *mockBackend) publishTransaction(tx *wire.MsgTx) error { 45 b.lock.Lock() 46 defer b.lock.Unlock() 47 48 txHash := tx.TxHash() 49 if _, ok := b.unconfirmedTxes[txHash]; ok { 50 // Tx already exists 51 testLog.Tracef("mockBackend duplicate tx %v", tx.TxHash()) 52 return lnwallet.ErrDoubleSpend 53 } 54 55 for _, in := range tx.TxIn { 56 if _, ok := b.unconfirmedSpendInputs[in.PreviousOutPoint]; ok { 57 // Double spend 58 testLog.Tracef("mockBackend double spend tx %v", tx.TxHash()) 59 return lnwallet.ErrDoubleSpend 60 } 61 62 if _, ok := b.confirmedSpendInputs[in.PreviousOutPoint]; ok { 63 // Already included in block 64 testLog.Tracef("mockBackend already in block tx %v", tx.TxHash()) 65 return lnwallet.ErrDoubleSpend 66 } 67 } 68 69 b.unconfirmedTxes[txHash] = tx 70 for _, in := range tx.TxIn { 71 b.unconfirmedSpendInputs[in.PreviousOutPoint] = struct{}{} 72 } 73 74 testLog.Tracef("mockBackend publish tx %v", tx.TxHash()) 75 76 return nil 77 } 78 79 func (b *mockBackend) PublishTransaction(tx *wire.MsgTx, _ string) error { 80 log.Tracef("Publishing tx %v", tx.TxHash()) 81 err := b.publishTransaction(tx) 82 select { 83 case b.publishChan <- *tx: 84 case <-time.After(defaultTestTimeout): 85 b.t.Fatalf("unexpected tx published") 86 } 87 return err 88 } 89 90 func (b *mockBackend) AbandonDoubleSpends(spentOutpoints ...*wire.OutPoint) error { 91 return nil 92 } 93 94 func (b *mockBackend) ListUnspentWitnessFromDefaultAccount(minConfs, maxConfs int32) ( 95 []*lnwallet.Utxo, error) { 96 b.lock.Lock() 97 defer b.lock.Unlock() 98 99 // Each time we list output, we increment the utxo counter, to 100 // ensure we don't return the same outpoint every time. 101 b.utxoCnt++ 102 103 for i := range b.walletUtxos { 104 b.walletUtxos[i].OutPoint.Hash[0] = byte(b.utxoCnt) 105 } 106 107 return b.walletUtxos, nil 108 } 109 110 func (b *mockBackend) WithCoinSelectLock(f func() error) error { 111 return f() 112 } 113 114 func (b *mockBackend) deleteUnconfirmed(txHash chainhash.Hash) { 115 b.lock.Lock() 116 defer b.lock.Unlock() 117 118 tx, ok := b.unconfirmedTxes[txHash] 119 if !ok { 120 // Tx already exists 121 testLog.Errorf("mockBackend delete tx not existing %v", txHash) 122 return 123 } 124 125 testLog.Tracef("mockBackend delete tx %v", tx.TxHash()) 126 delete(b.unconfirmedTxes, txHash) 127 for _, in := range tx.TxIn { 128 delete(b.unconfirmedSpendInputs, in.PreviousOutPoint) 129 } 130 } 131 132 func (b *mockBackend) mine() { 133 b.lock.Lock() 134 defer b.lock.Unlock() 135 136 notifications := make(map[wire.OutPoint]*wire.MsgTx) 137 for _, tx := range b.unconfirmedTxes { 138 testLog.Tracef("mockBackend mining tx %v", tx.TxHash()) 139 for _, in := range tx.TxIn { 140 b.confirmedSpendInputs[in.PreviousOutPoint] = struct{}{} 141 notifications[in.PreviousOutPoint] = tx 142 } 143 } 144 b.unconfirmedSpendInputs = make(map[wire.OutPoint]struct{}) 145 b.unconfirmedTxes = make(map[chainhash.Hash]*wire.MsgTx) 146 147 for outpoint, tx := range notifications { 148 testLog.Tracef("mockBackend delivering spend ntfn for %v", 149 outpoint) 150 b.notifier.SpendOutpoint(outpoint, *tx) 151 } 152 } 153 154 func (b *mockBackend) isDone() bool { 155 return len(b.unconfirmedTxes) == 0 156 } 157 158 func (b *mockBackend) RemoveDescendants(*wire.MsgTx) error { 159 return nil 160 } 161 162 func (b *mockBackend) FetchTx(chainhash.Hash) (*wire.MsgTx, error) { 163 return nil, nil 164 }