github.com/decred/dcrlnd@v0.7.6/chanbackup/pubsub_test.go (about) 1 package chanbackup 2 3 import ( 4 "fmt" 5 "testing" 6 "time" 7 8 "github.com/decred/dcrd/wire" 9 "github.com/decred/dcrlnd/keychain" 10 ) 11 12 type mockSwapper struct { 13 fail bool 14 15 swaps chan PackedMulti 16 17 swapState *Multi 18 19 keyChain keychain.KeyRing 20 } 21 22 func newMockSwapper(keychain keychain.KeyRing) *mockSwapper { 23 return &mockSwapper{ 24 swaps: make(chan PackedMulti, 1), 25 keyChain: keychain, 26 swapState: &Multi{}, 27 } 28 } 29 30 func (m *mockSwapper) UpdateAndSwap(newBackup PackedMulti) error { 31 if m.fail { 32 return fmt.Errorf("fail") 33 } 34 35 swapState, err := newBackup.Unpack(m.keyChain) 36 if err != nil { 37 return fmt.Errorf("unable to decode on disk swaps: %v", err) 38 } 39 40 m.swapState = swapState 41 42 m.swaps <- newBackup 43 44 return nil 45 } 46 47 func (m *mockSwapper) ExtractMulti(keychain keychain.KeyRing) (*Multi, error) { 48 return m.swapState, nil 49 } 50 51 type mockChannelNotifier struct { 52 fail bool 53 54 chanEvents chan ChannelEvent 55 } 56 57 func newMockChannelNotifier() *mockChannelNotifier { 58 return &mockChannelNotifier{ 59 chanEvents: make(chan ChannelEvent), 60 } 61 } 62 63 func (m *mockChannelNotifier) SubscribeChans(chans map[wire.OutPoint]struct{}) ( 64 *ChannelSubscription, error) { 65 66 if m.fail { 67 return nil, fmt.Errorf("fail") 68 } 69 70 return &ChannelSubscription{ 71 ChanUpdates: m.chanEvents, 72 Cancel: func() { 73 }, 74 }, nil 75 } 76 77 // TestNewSubSwapperSubscribeFail tests that if we're unable to obtain a 78 // channel subscription, then the entire sub-swapper will fail to start. 79 func TestNewSubSwapperSubscribeFail(t *testing.T) { 80 t.Parallel() 81 82 keyRing := &mockKeyRing{} 83 84 var swapper mockSwapper 85 chanNotifier := mockChannelNotifier{ 86 fail: true, 87 } 88 89 _, err := NewSubSwapper(nil, &chanNotifier, keyRing, &swapper) 90 if err == nil { 91 t.Fatalf("expected fail due to lack of subscription") 92 } 93 } 94 95 func assertExpectedBackupSwap(t *testing.T, swapper *mockSwapper, 96 subSwapper *SubSwapper, keyRing keychain.KeyRing, 97 expectedChanSet map[wire.OutPoint]Single) { 98 99 t.Helper() 100 101 select { 102 case newPackedMulti := <-swapper.swaps: 103 // If we unpack the new multi, then we should find all the old 104 // channels, and also the new channel included and any deleted 105 // channel omitted. 106 newMulti, err := newPackedMulti.Unpack(keyRing) 107 if err != nil { 108 t.Fatalf("unable to unpack multi: %v", err) 109 } 110 111 // Ensure that once unpacked, the current backup has the 112 // expected number of Singles. 113 if len(newMulti.StaticBackups) != len(expectedChanSet) { 114 t.Fatalf("new backup wasn't included: expected %v "+ 115 "backups have %v", len(expectedChanSet), 116 len(newMulti.StaticBackups)) 117 } 118 119 // We should also find all the old and new channels in this new 120 // backup. 121 for _, backup := range newMulti.StaticBackups { 122 _, ok := expectedChanSet[backup.FundingOutpoint] 123 if !ok { 124 t.Fatalf("didn't find backup in original set: %v", 125 backup.FundingOutpoint) 126 } 127 } 128 129 // The same applies for our in-memory state, but it's also 130 // possible for there to be items in the on-disk state that we 131 // don't know of explicit. 132 newChans := make(map[wire.OutPoint]Single) 133 for _, newChan := range newMulti.StaticBackups { 134 newChans[newChan.FundingOutpoint] = newChan 135 } 136 for _, backup := range subSwapper.backupState { 137 _, ok := newChans[backup.FundingOutpoint] 138 if !ok { 139 t.Fatalf("didn't find backup in original set: %v", 140 backup.FundingOutpoint) 141 } 142 } 143 144 case <-time.After(time.Second * 5): 145 t.Fatalf("update swapper didn't swap out multi") 146 } 147 } 148 149 // TestSubSwapperIdempotentStartStop tests that calling the Start/Stop methods 150 // multiple time is permitted. 151 func TestSubSwapperIdempotentStartStop(t *testing.T) { 152 t.Parallel() 153 154 keyRing := &mockKeyRing{} 155 156 var chanNotifier mockChannelNotifier 157 158 swapper := newMockSwapper(keyRing) 159 subSwapper, err := NewSubSwapper(nil, &chanNotifier, keyRing, swapper) 160 if err != nil { 161 t.Fatalf("unable to init subSwapper: %v", err) 162 } 163 164 if err := subSwapper.Start(); err != nil { 165 t.Fatalf("unable to start swapper: %v", err) 166 } 167 168 // The swapper should write the initial channel state as soon as it's 169 // active. 170 backupSet := make(map[wire.OutPoint]Single) 171 assertExpectedBackupSwap(t, swapper, subSwapper, keyRing, backupSet) 172 173 subSwapper.Start() 174 175 subSwapper.Stop() 176 subSwapper.Stop() 177 } 178 179 // TestSubSwapperUpdater tests that the SubSwapper will properly swap out 180 // new/old channels within the channel set, and notify the swapper to update 181 // the master multi file backup. 182 func TestSubSwapperUpdater(t *testing.T) { 183 t.Parallel() 184 185 keyRing := &mockKeyRing{} 186 chanNotifier := newMockChannelNotifier() 187 swapper := newMockSwapper(keyRing) 188 189 // First, we'll start out by creating a channels set for the initial 190 // set of channels known to the sub-swapper. 191 const numStartingChans = 3 192 initialChanSet := make([]Single, 0, numStartingChans) 193 backupSet := make(map[wire.OutPoint]Single) 194 for i := 0; i < numStartingChans; i++ { 195 channel, err := genRandomOpenChannelShell() 196 if err != nil { 197 t.Fatalf("unable to make test chan: %v", err) 198 } 199 200 single := NewSingle(channel, nil) 201 202 backupSet[channel.FundingOutpoint] = single 203 initialChanSet = append(initialChanSet, single) 204 } 205 206 // We'll also generate two additional channels which will already be 207 // present on disk. However, these will at first only be known by the 208 // on disk backup (the backup set). 209 const numDiskChans = 2 210 for i := 0; i < numDiskChans; i++ { 211 channel, err := genRandomOpenChannelShell() 212 if err != nil { 213 t.Fatalf("unable to make test chan: %v", err) 214 } 215 216 single := NewSingle(channel, nil) 217 218 backupSet[channel.FundingOutpoint] = single 219 swapper.swapState.StaticBackups = append( 220 swapper.swapState.StaticBackups, single, 221 ) 222 } 223 224 // With our channel set created, we'll make a fresh sub swapper 225 // instance to begin our test. 226 subSwapper, err := NewSubSwapper( 227 initialChanSet, chanNotifier, keyRing, swapper, 228 ) 229 if err != nil { 230 t.Fatalf("unable to make swapper: %v", err) 231 } 232 if err := subSwapper.Start(); err != nil { 233 t.Fatalf("unable to start sub swapper: %v", err) 234 } 235 defer subSwapper.Stop() 236 237 // The swapper should write the initial channel state as soon as it's 238 // active. 239 assertExpectedBackupSwap(t, swapper, subSwapper, keyRing, backupSet) 240 241 // Now that the sub-swapper is active, we'll notify to add a brand new 242 // channel to the channel state. 243 newChannel, err := genRandomOpenChannelShell() 244 if err != nil { 245 t.Fatalf("unable to create new chan: %v", err) 246 } 247 248 // With the new channel created, we'll send a new update to the main 249 // goroutine telling it about this new channel. 250 select { 251 case chanNotifier.chanEvents <- ChannelEvent{ 252 NewChans: []ChannelWithAddrs{ 253 { 254 OpenChannel: newChannel, 255 }, 256 }, 257 }: 258 case <-time.After(time.Second * 5): 259 t.Fatalf("update swapper didn't read new channel: %v", err) 260 } 261 262 backupSet[newChannel.FundingOutpoint] = NewSingle(newChannel, nil) 263 264 // At this point, the sub-swapper should now have packed a new multi, 265 // and then sent it to the swapper so the back up can be updated. 266 assertExpectedBackupSwap(t, swapper, subSwapper, keyRing, backupSet) 267 268 // We'll now trigger an update to remove an existing channel. 269 chanToDelete := initialChanSet[0].FundingOutpoint 270 select { 271 case chanNotifier.chanEvents <- ChannelEvent{ 272 ClosedChans: []wire.OutPoint{chanToDelete}, 273 }: 274 275 case <-time.After(time.Second * 5): 276 t.Fatalf("update swapper didn't read new channel: %v", err) 277 } 278 279 delete(backupSet, chanToDelete) 280 281 // Verify that the new set of backups, now has one less after the 282 // sub-swapper switches the new set with the old. 283 assertExpectedBackupSwap(t, swapper, subSwapper, keyRing, backupSet) 284 }