github.com/decred/dcrlnd@v0.7.6/chainscan/util_test.go (about) 1 package chainscan 2 3 import ( 4 "context" 5 "encoding/binary" 6 "errors" 7 "fmt" 8 "math/rand" 9 "sync" 10 "sync/atomic" 11 "time" 12 13 "github.com/decred/dcrd/chaincfg/chainhash" 14 "github.com/decred/dcrd/gcs/v4" 15 "github.com/decred/dcrd/gcs/v4/blockcf2" 16 "github.com/decred/dcrd/wire" 17 ) 18 19 var ( 20 testBlockCounter uint32 21 22 testPkScript = []byte{ 23 0x76, 0xa9, 0x14, 0x2b, 0xf4, 0x0a, 0x13, 0xea, 24 0x4f, 0xf1, 0xf9, 0xd3, 0x5a, 0x54, 0x15, 0xb0, 25 0xf7, 0x7d, 0x9e, 0x5d, 0xd9, 0x3b, 0x23, 0x88, 26 0xac, 27 } 28 testSigScript = []byte{ 29 0x47, 0x30, 0x44, 0x2, 0x20, 0x7b, 0xe8, 0x26, 30 0xed, 0x10, 0x5f, 0xaf, 0x88, 0xb1, 0x7d, 0x1, 31 0x72, 0x43, 0xd, 0x76, 0x3a, 0x9a, 0x14, 0x99, 32 0x8f, 0x2a, 0x96, 0x12, 0x4b, 0x7c, 0x70, 0x4d, 33 0xa1, 0x4d, 0x96, 0x20, 0xc2, 0x2, 0x20, 0x45, 34 0x8e, 0xf, 0x98, 0x11, 0x3b, 0x35, 0xa2, 0x2d, 35 0x43, 0xfc, 0x2, 0xdf, 0xa4, 0xe, 0x17, 0x8, 36 0xf7, 0xd8, 0xc7, 0x5f, 0xfe, 0xcd, 0x8e, 0x50, 37 0x2c, 0xfd, 0x58, 0xda, 0x4a, 0x2b, 0x10, 0x1, 38 0x21, 0x2, 0x6e, 0xa2, 0xca, 0x26, 0x16, 0x56, 39 0xe6, 0xbc, 0xae, 0xd9, 0xb2, 0x32, 0xdc, 0xc4, 40 0x7b, 0x30, 0x33, 0x20, 0x41, 0xbc, 0x31, 0xd5, 41 0x3c, 0x40, 0xa8, 0xa1, 0x4a, 0xb9, 0x60, 0x4f, 42 0x25, 0x33, 43 } 44 testOutPoint = wire.OutPoint{ 45 Hash: chainhash.Hash{ 46 0x72, 0x43, 0x0d, 0x76, 0x3a, 0x9a, 0x14, 0x99, 47 0x8f, 0x2a, 0x96, 0x12, 0x4b, 0x7c, 0x70, 0x4d, 48 0x43, 0xfc, 0x02, 0xdf, 0xa4, 0x0e, 0x17, 0x08, 49 0xf7, 0xd8, 0xc7, 0x5f, 0xfe, 0xcd, 0x8e, 0x50, 50 }, 51 Index: 1701, 52 } 53 54 emptyEvent Event 55 ) 56 57 // testingIntf matches both *testing.T and *testing.B 58 type testingIntf interface { 59 Fatal(...interface{}) 60 Fatalf(string, ...interface{}) 61 Helper() 62 } 63 64 func assertFoundChanRcv(t testingIntf, c chan Event) Event { 65 t.Helper() 66 67 var e Event 68 select { 69 case e = <-c: 70 return e 71 case <-time.After(5 * time.Second): 72 t.Fatal("Timeout waiting for receive in foundChan") 73 } 74 return e 75 } 76 77 func assertFoundChanRcvHeight(t testingIntf, c chan Event, wantHeight int32) Event { 78 t.Helper() 79 80 e := assertFoundChanRcv(t, c) 81 if e.BlockHeight != wantHeight { 82 t.Fatalf("Unexpected BlockHeight in foundChan event. want=%d got=%d", 83 wantHeight, e.BlockHeight) 84 } 85 return e 86 } 87 88 func assertFoundChanEmpty(t testingIntf, c chan Event) { 89 t.Helper() 90 select { 91 case <-c: 92 t.Fatal("Unexpected signal in foundChan") 93 case <-time.After(10 * time.Millisecond): 94 } 95 } 96 97 func assertStartWatchHeightSignalled(t testingIntf, c chan int32) int32 { 98 t.Helper() 99 100 var endHeight int32 101 select { 102 case endHeight = <-c: 103 case <-time.After(5 * time.Second): 104 t.Fatal("Timeout waiting for start watch height chan signal") 105 } 106 return endHeight 107 } 108 109 func assertNoError(t testingIntf, err error) { 110 t.Helper() 111 112 if err == nil { 113 return 114 } 115 116 t.Fatalf("Unexpected error: %v", err) 117 } 118 119 // assertCompleted asserts that the 'c' chan (which should be a completeChan 120 // passed to a scanner) is closed in a reasonable time. 121 func assertCompleted(t testingIntf, c chan struct{}) { 122 t.Helper() 123 select { 124 case <-c: 125 case <-time.After(5 * time.Second): 126 t.Fatal("Timeout waiting for completeChan to close") 127 } 128 } 129 130 type testBlock struct { 131 block *wire.MsgBlock 132 blockHash chainhash.Hash 133 cfilter *gcs.FilterV2 134 cfKey [16]byte 135 } 136 137 type blockMangler struct { 138 txMangler func(tx *wire.MsgTx) 139 blockMangler func(b *wire.MsgBlock) 140 cfilterData []byte 141 } 142 143 func spendOutPoint(outp wire.OutPoint) blockMangler { 144 return blockMangler{ 145 txMangler: func(tx *wire.MsgTx) { 146 tx.AddTxIn(wire.NewTxIn(&outp, 0, nil)) 147 }, 148 } 149 } 150 151 func spendScript(sigScript []byte) blockMangler { 152 var outp wire.OutPoint 153 rand.Read(outp.Hash[:]) 154 return blockMangler{ 155 txMangler: func(tx *wire.MsgTx) { 156 tx.AddTxIn(wire.NewTxIn(&outp, 0, sigScript)) 157 }, 158 } 159 } 160 161 func confirmScript(pkScript []byte) blockMangler { 162 return blockMangler{ 163 txMangler: func(tx *wire.MsgTx) { 164 tx.AddTxOut(wire.NewTxOut(0, pkScript)) 165 }, 166 } 167 } 168 169 func moveRegularToStakeTree() blockMangler { 170 return blockMangler{ 171 blockMangler: func(b *wire.MsgBlock) { 172 b.STransactions = b.Transactions 173 b.Transactions = make([]*wire.MsgTx, 0) 174 }, 175 } 176 } 177 178 func cfilterData(pkScript []byte) blockMangler { 179 return blockMangler{ 180 cfilterData: pkScript, 181 } 182 } 183 184 func newTestBlock(height int32, prevBlock chainhash.Hash, blockManglers ...blockMangler) *testBlock { 185 bc := atomic.AddUint32(&testBlockCounter, 1) 186 tx := &wire.MsgTx{ 187 TxIn: []*wire.TxIn{ 188 { 189 PreviousOutPoint: wire.OutPoint{ 190 Index: bc, // Ensure unique tx hash 191 }, 192 }, 193 }, 194 TxOut: []*wire.TxOut{ 195 { 196 PkScript: []byte{0x6a}, 197 }, 198 }, 199 } 200 var cfData [][]byte 201 for _, m := range blockManglers { 202 if m.txMangler != nil { 203 m.txMangler(tx) 204 } 205 if m.cfilterData != nil { 206 cfData = append(cfData, m.cfilterData) 207 } 208 } 209 210 block := &wire.MsgBlock{ 211 Header: wire.BlockHeader{ 212 PrevBlock: prevBlock, 213 Height: uint32(height), 214 Nonce: testBlockCounter, 215 }, 216 Transactions: []*wire.MsgTx{tx}, 217 } 218 binary.LittleEndian.PutUint32(block.Header.MerkleRoot[:], bc) 219 220 for _, m := range blockManglers { 221 if m.blockMangler != nil { 222 m.blockMangler(block) 223 } 224 } 225 226 var cfKey [16]byte 227 copy(cfKey[:], block.Header.MerkleRoot[:]) 228 cf, err := gcs.NewFilterV2(blockcf2.B, blockcf2.M, cfKey, cfData) 229 if err != nil { 230 panic(err) 231 } 232 233 return &testBlock{ 234 block: block, 235 blockHash: block.BlockHash(), 236 cfilter: cf, 237 cfKey: cfKey, 238 } 239 } 240 241 // dupeTestTx duplicates the first transaction of the test block (in either the 242 // regular or stake transaction trees). 243 func dupeTestTx(b *testBlock) { 244 if len(b.block.Transactions) > 0 { 245 b.block.Transactions = append(b.block.Transactions, b.block.Transactions[0]) 246 } else { 247 b.block.STransactions = append(b.block.STransactions, b.block.STransactions[0]) 248 } 249 } 250 251 type mockChain struct { 252 // blocks and byHeight are sync.Maps to avoid triggering the race 253 // detector for the chain. 254 blocks *sync.Map // map[chainhash.Hash]*testBlock 255 byHeight *sync.Map // map[int32]*testBlock 256 257 tipMtx sync.Mutex 258 tip *testBlock 259 260 mtx sync.Mutex 261 eventReaders []*eventReader 262 263 newTipChan chan struct{} 264 getBlockCount uint32 265 getCfilterCount uint32 266 sendNextCfilterChan chan struct{} 267 } 268 269 func newMockChain() *mockChain { 270 return &mockChain{ 271 blocks: &sync.Map{}, 272 byHeight: &sync.Map{}, 273 newTipChan: make(chan struct{}), 274 } 275 } 276 277 func (mc *mockChain) newFromTip(manglers ...blockMangler) *testBlock { 278 var height int32 279 var prevBlock chainhash.Hash 280 mc.tipMtx.Lock() 281 if mc.tip != nil { 282 height = int32(mc.tip.block.Header.Height + 1) 283 prevBlock = mc.tip.block.BlockHash() 284 } 285 mc.tipMtx.Unlock() 286 287 b := newTestBlock(height, prevBlock, manglers...) 288 bh := b.block.BlockHash() 289 mc.blocks.Store(bh, b) 290 mc.byHeight.Store(height, b) 291 return b 292 } 293 294 func (mc *mockChain) extend(b *testBlock) { 295 mc.tipMtx.Lock() 296 mc.tip = b 297 mc.tipMtx.Unlock() 298 } 299 300 func (mc *mockChain) signalNewTip() { 301 mc.mtx.Lock() 302 readers := mc.eventReaders 303 mc.mtx.Unlock() 304 305 mc.tipMtx.Lock() 306 tip := mc.tip 307 mc.tipMtx.Unlock() 308 309 e := BlockConnectedEvent{ 310 Hash: tip.blockHash, 311 Height: int32(tip.block.Header.Height), 312 PrevHash: tip.block.Header.PrevBlock, 313 CFKey: tip.cfKey, 314 Filter: tip.cfilter, 315 } 316 for _, r := range readers { 317 select { 318 case <-r.ctx.Done(): 319 case r.c <- e: 320 } 321 } 322 } 323 324 func (mc *mockChain) genBlocks(n int, manglers ...blockMangler) { 325 for i := 0; i < n; i++ { 326 mc.extend(mc.newFromTip(manglers...)) 327 } 328 } 329 330 func (mc *mockChain) ChainEvents(ctx context.Context) <-chan ChainEvent { 331 r := &eventReader{ 332 ctx: ctx, 333 c: make(chan ChainEvent), 334 } 335 mc.mtx.Lock() 336 mc.eventReaders = append(mc.eventReaders, r) 337 mc.mtx.Unlock() 338 return r.c 339 } 340 341 func (mc *mockChain) NextTip(ctx context.Context) (*chainhash.Hash, int32, [16]byte, *gcs.FilterV2, error) { 342 select { 343 case <-ctx.Done(): 344 return nil, 0, [16]byte{}, nil, ctx.Err() 345 case <-mc.newTipChan: 346 mc.tipMtx.Lock() 347 tip := mc.tip 348 mc.tipMtx.Unlock() 349 return &tip.blockHash, int32(tip.block.Header.Height), tip.cfKey, tip.cfilter, nil 350 } 351 } 352 353 func (mc *mockChain) GetBlock(ctx context.Context, bh *chainhash.Hash) (*wire.MsgBlock, error) { 354 if b, ok := mc.blocks.Load(*bh); ok { 355 atomic.AddUint32(&mc.getBlockCount, 1) 356 return b.(*testBlock).block, nil 357 } 358 return nil, errors.New("block not found") 359 } 360 361 func (mc *mockChain) CurrentTip(ctx context.Context) (*chainhash.Hash, int32, error) { 362 mc.tipMtx.Lock() 363 tip := mc.tip 364 mc.tipMtx.Unlock() 365 366 if tip == nil { 367 return nil, 0, errors.New("chain uninitialized") 368 } 369 370 return &tip.blockHash, int32(tip.block.Header.Height), nil 371 } 372 373 func (mc *mockChain) GetCFilter(ctx context.Context, height int32) (*chainhash.Hash, [16]byte, *gcs.FilterV2, error) { 374 // If sendNextCfilter is specified then wait until it's signalled by 375 // the test routine to send the cfilter. 376 if mc.sendNextCfilterChan != nil { 377 <-mc.sendNextCfilterChan 378 } 379 380 mc.tipMtx.Lock() 381 tip := mc.tip 382 mc.tipMtx.Unlock() 383 if tip == nil { 384 return nil, [16]byte{}, nil, errors.New("chain unitialized") 385 } 386 387 if height > int32(tip.block.Header.Height) { 388 return nil, [16]byte{}, nil, ErrBlockAfterTip{Height: height} 389 } 390 391 bl, ok := mc.byHeight.Load(height) 392 if !ok { 393 return nil, [16]byte{}, nil, fmt.Errorf("unknown block by height %d", height) 394 } 395 block := bl.(*testBlock) 396 atomic.AddUint32(&mc.getCfilterCount, 1) 397 return &block.blockHash, block.cfKey, block.cfilter, nil 398 }