decred.org/dcrwallet/v3@v3.1.0/wallet/udb/txquery_test.go (about) 1 // Copyright (c) 2015 The btcsuite developers 2 // Copyright (c) 2015-2016 The Decred developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 //go:build ignore 7 8 package udb 9 10 import ( 11 "bytes" 12 "encoding/binary" 13 "fmt" 14 "testing" 15 "time" 16 17 "github.com/decred/dcrd/chaincfg/chainhash" 18 "github.com/decred/dcrd/dcrutil/v4" 19 "github.com/decred/dcrd/wire" 20 ) 21 22 type queryState struct { 23 // slice items are ordered by height, mempool comes last. 24 blocks [][]TxDetails 25 txDetails map[chainhash.Hash][]TxDetails 26 } 27 28 func newQueryState() *queryState { 29 return &queryState{ 30 txDetails: make(map[chainhash.Hash][]TxDetails), 31 } 32 } 33 34 func (q *queryState) deepCopy() *queryState { 35 cpy := newQueryState() 36 for _, blockDetails := range q.blocks { 37 var cpyDetails []TxDetails 38 for _, detail := range blockDetails { 39 cpyDetails = append(cpyDetails, *deepCopyTxDetails(&detail)) 40 } 41 cpy.blocks = append(cpy.blocks, cpyDetails) 42 } 43 cpy.txDetails = make(map[chainhash.Hash][]TxDetails) 44 for txHash, details := range q.txDetails { 45 detailsSlice := make([]TxDetails, len(details)) 46 for i, detail := range details { 47 detailsSlice[i] = *deepCopyTxDetails(&detail) 48 } 49 cpy.txDetails[txHash] = detailsSlice 50 } 51 return cpy 52 } 53 54 func deepCopyTxDetails(d *TxDetails) *TxDetails { 55 cpy := *d 56 cpy.MsgTx = *d.MsgTx.Copy() 57 if cpy.SerializedTx != nil { 58 cpy.SerializedTx = make([]byte, len(cpy.SerializedTx)) 59 copy(cpy.SerializedTx, d.SerializedTx) 60 } 61 cpy.Credits = make([]CreditRecord, len(d.Credits)) 62 copy(cpy.Credits, d.Credits) 63 cpy.Debits = make([]DebitRecord, len(d.Debits)) 64 copy(cpy.Debits, d.Debits) 65 return &cpy 66 } 67 68 func (q *queryState) compare(t *testing.T, s *Store, changeDesc string) { 69 defer func() { 70 if t.Failed() { 71 t.Fatalf("Store state queries failed after '%s'", changeDesc) 72 } 73 }() 74 75 fwdBlocks := q.blocks 76 revBlocks := make([][]TxDetails, len(q.blocks)) 77 copy(revBlocks, q.blocks) 78 for i := 0; i < len(revBlocks)/2; i++ { 79 revBlocks[i], revBlocks[len(revBlocks)-1-i] = revBlocks[len(revBlocks)-1-i], revBlocks[i] 80 } 81 checkBlock := func(blocks [][]TxDetails) func([]TxDetails) (bool, error) { 82 return func(got []TxDetails) (bool, error) { 83 if len(fwdBlocks) == 0 { 84 return false, fmt.Errorf("entered range when no more details expected") 85 } 86 exp := blocks[0] 87 if len(got) != len(exp) { 88 return false, fmt.Errorf("got len(details)=%d in transaction range, expected %d", len(got), len(exp)) 89 } 90 for i := range got { 91 equalTxDetails(t, &got[i], &exp[i]) 92 } 93 if t.Failed() { 94 return false, fmt.Errorf("Failed comparing range of transaction details") 95 } 96 blocks = blocks[1:] 97 return false, nil 98 } 99 } 100 err := s.RangeTransactions(0, -1, checkBlock(fwdBlocks)) 101 if err != nil { 102 t.Fatalf("Failed in RangeTransactions (forwards iteration): %v", err) 103 } 104 err = s.RangeTransactions(-1, 0, checkBlock(revBlocks)) 105 if err != nil { 106 t.Fatalf("Failed in RangeTransactions (reverse iteration): %v", err) 107 } 108 109 for txHash, details := range q.txDetails { 110 for _, detail := range details { 111 blk := &detail.Block.Block 112 if blk.Height == -1 { 113 blk = nil 114 } 115 d, err := s.UniqueTxDetails(&txHash, blk) 116 if err != nil { 117 t.Fatal(err) 118 } 119 if d == nil { 120 t.Errorf("Found no matching transaction at height %d", detail.Block.Height) 121 continue 122 } 123 equalTxDetails(t, d, &detail) 124 } 125 if t.Failed() { 126 t.Fatalf("Failed querying unique details regarding transaction %v", txHash) 127 } 128 129 // For the most recent tx with this hash, check that 130 // TxDetails (not looking up a tx at any particular 131 // height) matches the last. 132 detail := &details[len(details)-1] 133 d, err := s.TxDetails(&txHash) 134 if err != nil { 135 t.Fatal(err) 136 } 137 equalTxDetails(t, d, detail) 138 if t.Failed() { 139 t.Fatalf("Failed querying latest details regarding transaction %v", txHash) 140 } 141 } 142 } 143 144 func equalTxDetails(t *testing.T, got, exp *TxDetails) { 145 // Need to avoid using reflect.DeepEqual against slices, since it 146 // returns false for nil vs non-nil zero length slices. 147 equalTxs(t, &got.MsgTx, &exp.MsgTx) 148 if got.Hash != exp.Hash { 149 t.Errorf("Found mismatched hashes") 150 t.Errorf("Got: %v", got.Hash) 151 t.Errorf("Expected: %v", exp.Hash) 152 } 153 if got.Received != exp.Received { 154 t.Errorf("Found mismatched receive time") 155 t.Errorf("Got: %v", got.Received) 156 t.Errorf("Expected: %v", exp.Received) 157 } 158 if !bytes.Equal(got.SerializedTx, exp.SerializedTx) { 159 t.Errorf("Found mismatched serialized txs") 160 t.Errorf("Got: %x", got.SerializedTx) 161 t.Errorf("Expected: %x", exp.SerializedTx) 162 } 163 if got.Block != exp.Block { 164 t.Errorf("Found mismatched block meta") 165 t.Errorf("Got: %v", got.Block) 166 t.Errorf("Expected: %v", exp.Block) 167 } 168 if len(got.Credits) != len(exp.Credits) { 169 t.Errorf("Credit slice lengths differ: Got %d Expected %d", len(got.Credits), len(exp.Credits)) 170 } else { 171 for i := range got.Credits { 172 if got.Credits[i] != exp.Credits[i] { 173 t.Errorf("Found mismatched Credit[%d]", i) 174 t.Errorf("Got: %v", got.Credits[i]) 175 t.Errorf("Expected: %v", exp.Credits[i]) 176 } 177 } 178 } 179 if len(got.Debits) != len(exp.Debits) { 180 t.Errorf("Debit slice lengths differ: Got %d Expected %d", len(got.Debits), len(exp.Debits)) 181 } else { 182 for i := range got.Debits { 183 if got.Debits[i] != exp.Debits[i] { 184 t.Errorf("Found mismatched Debit[%d]", i) 185 t.Errorf("Got: %v", got.Debits[i]) 186 t.Errorf("Expected: %v", exp.Debits[i]) 187 } 188 } 189 } 190 } 191 192 func equalTxs(t *testing.T, got, exp *wire.MsgTx) { 193 var bufGot, bufExp bytes.Buffer 194 195 bufGot.Grow(got.SerializeSize()) 196 err := got.Serialize(&bufGot) 197 if err != nil { 198 t.Fatal(err) 199 } 200 bufExp.Grow(exp.SerializeSize()) 201 err = exp.Serialize(&bufExp) 202 if err != nil { 203 t.Fatal(err) 204 } 205 if !bytes.Equal(bufGot.Bytes(), bufExp.Bytes()) { 206 t.Errorf("Found unexpected wire.MsgTx:") 207 t.Errorf("Got: %x", bufGot.Bytes()) 208 t.Errorf("Expected: %x", bufExp.Bytes()) 209 } 210 } 211 212 // Returns time.Now() with seconds resolution, this is what Store saves. 213 func timeNow() time.Time { 214 return time.Unix(time.Now().Unix(), 0) 215 } 216 217 // Returns a copy of a TxRecord without the serialized tx. 218 func stripSerializedTx(rec *TxRecord) *TxRecord { 219 ret := *rec 220 ret.SerializedTx = nil 221 return &ret 222 } 223 224 func makeBlockMeta(height int32) BlockMeta { 225 if height == -1 { 226 return BlockMeta{Block: Block{Height: -1}} 227 } 228 229 b := BlockMeta{ 230 Block: Block{Height: height}, 231 Time: timeNow(), 232 } 233 // Give it a fake block hash created from the height and time. 234 binary.LittleEndian.PutUint32(b.Hash[0:4], uint32(height)) 235 binary.LittleEndian.PutUint64(b.Hash[4:12], uint64(b.Time.Unix())) 236 return b 237 } 238 239 func TestStoreQueries(t *testing.T) { 240 t.Parallel() 241 242 type queryTest struct { 243 desc string 244 updates func() // Unwinds from t.Fatal if the update errors. 245 state *queryState 246 } 247 var tests []queryTest 248 249 // Create the store and test initial state. 250 s, teardown, err := testStore() 251 defer teardown() 252 if err != nil { 253 t.Fatal(err) 254 } 255 lastState := newQueryState() 256 tests = append(tests, queryTest{ 257 desc: "initial store", 258 updates: func() {}, 259 state: lastState, 260 }) 261 262 // simplify error handling 263 insertTx := func(rec *TxRecord, block *BlockMeta) { 264 err := s.InsertTx(rec, block) 265 if err != nil { 266 t.Fatal(err) 267 } 268 } 269 defaultAccount := uint32(0) 270 addCredit := func(s *Store, rec *TxRecord, block *BlockMeta, index uint32, change bool) { 271 err := s.AddCredit(rec, block, index, change, defaultAccount) 272 if err != nil { 273 t.Fatal(err) 274 } 275 } 276 newTxRecordFromMsgTx := func(tx *wire.MsgTx, received time.Time) *TxRecord { 277 rec, err := NewTxRecordFromMsgTx(tx, received) 278 if err != nil { 279 t.Fatal(err) 280 } 281 return rec 282 } 283 rollback := func(height int32) { 284 err := s.Rollback(height) 285 if err != nil { 286 t.Fatal(err) 287 } 288 } 289 290 // Insert an unmined transaction. Mark no credits yet. 291 txA := spendOutput(&chainhash.Hash{}, 0, 0, 100e8) 292 recA := newTxRecordFromMsgTx(txA, timeNow()) 293 newState := lastState.deepCopy() 294 newState.blocks = [][]TxDetails{ 295 { 296 { 297 TxRecord: *stripSerializedTx(recA), 298 Block: BlockMeta{Block: Block{Height: -1}}, 299 }, 300 }, 301 } 302 newState.txDetails[recA.Hash] = []TxDetails{ 303 newState.blocks[0][0], 304 } 305 lastState = newState 306 tests = append(tests, queryTest{ 307 desc: "insert tx A unmined", 308 updates: func() { insertTx(recA, nil) }, 309 state: newState, 310 }) 311 312 // Add txA:0 as a change credit. 313 newState = lastState.deepCopy() 314 newState.blocks[0][0].Credits = []CreditRecord{ 315 { 316 Index: 0, 317 Amount: dcrutil.Amount(recA.MsgTx.TxOut[0].Value), 318 Spent: false, 319 Change: true, 320 }, 321 } 322 newState.txDetails[recA.Hash][0].Credits = newState.blocks[0][0].Credits 323 lastState = newState 324 tests = append(tests, queryTest{ 325 desc: "mark unconfirmed txA:0 as credit", 326 updates: func() { addCredit(s, recA, nil, 0, true) }, 327 state: newState, 328 }) 329 330 // Insert another unmined transaction which spends txA:0, splitting the 331 // amount into outputs of 40 and 60 DCR. 332 txB := spendOutput(&recA.Hash, 0, 0, 40e8, 60e8) 333 recB := newTxRecordFromMsgTx(txB, timeNow()) 334 newState = lastState.deepCopy() 335 newState.blocks[0][0].Credits[0].Spent = true 336 newState.blocks[0] = append(newState.blocks[0], TxDetails{ 337 TxRecord: *stripSerializedTx(recB), 338 Block: BlockMeta{Block: Block{Height: -1}}, 339 Debits: []DebitRecord{ 340 { 341 Amount: dcrutil.Amount(recA.MsgTx.TxOut[0].Value), 342 Index: 0, // recB.MsgTx.TxIn index 343 }, 344 }, 345 }) 346 newState.txDetails[recA.Hash][0].Credits[0].Spent = true 347 newState.txDetails[recB.Hash] = []TxDetails{newState.blocks[0][1]} 348 lastState = newState 349 tests = append(tests, queryTest{ 350 desc: "insert tx B unmined", 351 updates: func() { insertTx(recB, nil) }, 352 state: newState, 353 }) 354 newState = lastState.deepCopy() 355 newState.blocks[0][1].Credits = []CreditRecord{ 356 { 357 Index: 0, 358 Amount: dcrutil.Amount(recB.MsgTx.TxOut[0].Value), 359 Spent: false, 360 Change: false, 361 }, 362 } 363 newState.txDetails[recB.Hash][0].Credits = newState.blocks[0][1].Credits 364 lastState = newState 365 tests = append(tests, queryTest{ 366 desc: "mark txB:0 as non-change credit", 367 updates: func() { addCredit(s, recB, nil, 0, false) }, 368 state: newState, 369 }) 370 371 // Mine tx A at block 100. Leave tx B unmined. 372 b100 := makeBlockMeta(100) 373 newState = lastState.deepCopy() 374 newState.blocks[0] = newState.blocks[0][:1] 375 newState.blocks[0][0].Block = b100 376 newState.blocks = append(newState.blocks, lastState.blocks[0][1:]) 377 newState.txDetails[recA.Hash][0].Block = b100 378 lastState = newState 379 tests = append(tests, queryTest{ 380 desc: "mine tx A", 381 updates: func() { insertTx(recA, &b100) }, 382 state: newState, 383 }) 384 385 // Mine tx B at block 101. 386 b101 := makeBlockMeta(101) 387 newState = lastState.deepCopy() 388 newState.blocks[1][0].Block = b101 389 newState.txDetails[recB.Hash][0].Block = b101 390 lastState = newState 391 tests = append(tests, queryTest{ 392 desc: "mine tx B", 393 updates: func() { insertTx(recB, &b101) }, 394 state: newState, 395 }) 396 397 for _, tst := range tests { 398 tst.updates() 399 tst.state.compare(t, s, tst.desc) 400 } 401 402 // Run some additional query tests with the current store's state: 403 // - Verify that querying for a transaction not in the store returns 404 // nil without failure. 405 // - Verify that querying for a unique transaction at the wrong block 406 // returns nil without failure. 407 // - Verify that breaking early on RangeTransactions stops further 408 // iteration. 409 410 missingTx := spendOutput(&recB.Hash, 0, 0, 40e8) 411 missingRec := newTxRecordFromMsgTx(missingTx, timeNow()) 412 missingBlock := makeBlockMeta(102) 413 missingDetails, err := s.TxDetails(&missingRec.Hash) 414 if err != nil { 415 t.Fatal(err) 416 } 417 if missingDetails != nil { 418 t.Errorf("Expected no details, found details for tx %v", missingDetails.Hash) 419 } 420 missingUniqueTests := []struct { 421 hash *chainhash.Hash 422 block *Block 423 }{ 424 {&missingRec.Hash, &b100.Block}, 425 {&missingRec.Hash, &missingBlock.Block}, 426 {&missingRec.Hash, nil}, 427 {&recB.Hash, &b100.Block}, 428 {&recB.Hash, &missingBlock.Block}, 429 {&recB.Hash, nil}, 430 } 431 for _, tst := range missingUniqueTests { 432 missingDetails, err = s.UniqueTxDetails(tst.hash, tst.block) 433 if err != nil { 434 t.Fatal(err) 435 } 436 if missingDetails != nil { 437 t.Errorf("Expected no details, found details for tx %v", missingDetails.Hash) 438 } 439 } 440 441 iterations := 0 442 err = s.RangeTransactions(0, -1, func([]TxDetails) (bool, error) { 443 iterations++ 444 return true, nil 445 }) 446 if err != nil { 447 t.Error("RangeTransactions (forwards) failed:", err) 448 } 449 if iterations != 1 { 450 t.Errorf("RangeTransactions (forwards) ran func %d times", iterations) 451 } 452 iterations = 0 453 err = s.RangeTransactions(-1, 0, func([]TxDetails) (bool, error) { 454 iterations++ 455 return true, nil 456 }) 457 if err != nil { 458 t.Error("RangeTransactions (reverse) failed:", err) 459 } 460 461 if iterations != 1 { 462 t.Errorf("RangeTransactions (reverse) ran func %d times", iterations) 463 } 464 // Make sure it also breaks early after one iteration through unmined transactions. 465 rollback(b101.Height) 466 iterations = 0 467 err = s.RangeTransactions(-1, 0, func([]TxDetails) (bool, error) { 468 iterations++ 469 return true, nil 470 }) 471 if err != nil { 472 t.Error("RangeTransactions (reverse) failed:", err) 473 } 474 if iterations != 1 { 475 t.Errorf("RangeTransactions (reverse) ran func %d times", iterations) 476 } 477 478 // None of the above tests have tested RangeTransactions with multiple 479 // txs per block, so do that now. Start by moving tx B to block 100 480 // (same block as tx A), and then rollback from block 100 onwards so 481 // both are unmined. 482 newState = lastState.deepCopy() 483 newState.blocks[0] = append(newState.blocks[0], newState.blocks[1]...) 484 newState.blocks[0][1].Block = b100 485 newState.blocks = newState.blocks[:1] 486 newState.txDetails[recB.Hash][0].Block = b100 487 lastState = newState 488 tests = append(tests[:0:0], queryTest{ 489 desc: "move tx B to block 100", 490 updates: func() { insertTx(recB, &b100) }, 491 state: newState, 492 }) 493 newState = lastState.deepCopy() 494 newState.blocks[0][0].Block = makeBlockMeta(-1) 495 newState.blocks[0][1].Block = makeBlockMeta(-1) 496 newState.txDetails[recA.Hash][0].Block = makeBlockMeta(-1) 497 newState.txDetails[recB.Hash][0].Block = makeBlockMeta(-1) 498 lastState = newState 499 tests = append(tests, queryTest{ 500 desc: "rollback block 100", 501 updates: func() { rollback(b100.Height) }, 502 state: newState, 503 }) 504 505 // None of the above tests have tested transactions with colliding 506 // hashes, so mine tx A in block 100, and then insert tx A again 507 // unmined. Also mine tx A in block 101 (this moves it from unmined). 508 // This is a valid test because the store does not perform signature 509 // validation or keep a full utxo set, and duplicated transaction hashes 510 // from different blocks are allowed so long as all previous outputs are 511 // spent. 512 newState = lastState.deepCopy() 513 newState.blocks = append(newState.blocks, newState.blocks[0][1:]) 514 newState.blocks[0] = newState.blocks[0][:1:1] 515 newState.blocks[0][0].Block = b100 516 newState.blocks[1] = []TxDetails{ 517 { 518 TxRecord: *stripSerializedTx(recA), 519 Block: makeBlockMeta(-1), 520 }, 521 newState.blocks[1][0], 522 } 523 newState.txDetails[recA.Hash][0].Block = b100 524 newState.txDetails[recA.Hash] = append(newState.txDetails[recA.Hash], newState.blocks[1][0]) 525 lastState = newState 526 tests = append(tests, queryTest{ 527 desc: "insert duplicate tx A", 528 updates: func() { insertTx(recA, &b100); insertTx(recA, nil) }, 529 state: newState, 530 }) 531 newState = lastState.deepCopy() 532 newState.blocks = [][]TxDetails{ 533 newState.blocks[0], 534 {newState.blocks[1][0]}, 535 {newState.blocks[1][1]}, 536 } 537 newState.blocks[1][0].Block = b101 538 newState.txDetails[recA.Hash][1].Block = b101 539 lastState = newState 540 tests = append(tests, queryTest{ 541 desc: "mine duplicate tx A", 542 updates: func() { insertTx(recA, &b101) }, 543 state: newState, 544 }) 545 546 for _, tst := range tests { 547 tst.updates() 548 tst.state.compare(t, s, tst.desc) 549 } 550 } 551 552 func TestPreviousPkScripts(t *testing.T) { 553 t.Parallel() 554 555 s, teardown, err := testStore() 556 defer teardown() 557 if err != nil { 558 t.Fatal(err) 559 } 560 561 // Invalid scripts but sufficient for testing. 562 var ( 563 scriptA0 = []byte("tx A output 0") 564 scriptA1 = []byte("tx A output 1") 565 scriptB0 = []byte("tx B output 0") 566 scriptB1 = []byte("tx B output 1") 567 scriptC0 = []byte("tx C output 0") 568 scriptC1 = []byte("tx C output 1") 569 ) 570 571 // Create a transaction spending two prevous outputs and generating two 572 // new outputs the passed pkScipts. Spends outputs 0 and 1 from prevHash. 573 buildTx := func(prevHash *chainhash.Hash, script0, script1 []byte) *wire.MsgTx { 574 return &wire.MsgTx{ 575 TxIn: []*wire.TxIn{ 576 {PreviousOutPoint: wire.OutPoint{ 577 Hash: *prevHash, 578 Index: 0, 579 Tree: dcrutil.TxTreeRegular, 580 }}, 581 {PreviousOutPoint: wire.OutPoint{ 582 Hash: *prevHash, 583 Index: 1, 584 Tree: dcrutil.TxTreeRegular, 585 }}, 586 }, 587 TxOut: []*wire.TxOut{ 588 {Value: 1e8, PkScript: script0}, 589 {Value: 1e8, PkScript: script1}, 590 }, 591 } 592 } 593 594 newTxRecordFromMsgTx := func(tx *wire.MsgTx) *TxRecord { 595 rec, err := NewTxRecordFromMsgTx(tx, timeNow()) 596 if err != nil { 597 t.Fatal(err) 598 } 599 return rec 600 } 601 602 // Create transactions with the fake output scripts. 603 var ( 604 txA = buildTx(&chainhash.Hash{}, scriptA0, scriptA1) 605 recA = newTxRecordFromMsgTx(txA) 606 txB = buildTx(&recA.Hash, scriptB0, scriptB1) 607 recB = newTxRecordFromMsgTx(txB) 608 txC = buildTx(&recB.Hash, scriptC0, scriptC1) 609 recC = newTxRecordFromMsgTx(txC) 610 txD = buildTx(&recC.Hash, nil, nil) 611 recD = newTxRecordFromMsgTx(txD) 612 ) 613 614 insertTx := func(rec *TxRecord, block *BlockMeta) { 615 err := s.InsertTx(rec, block) 616 if err != nil { 617 t.Fatal(err) 618 } 619 } 620 defaultAccount := uint32(0) 621 addCredit := func(rec *TxRecord, block *BlockMeta, index uint32) { 622 err := s.AddCredit(rec, block, index, false, defaultAccount) 623 if err != nil { 624 t.Fatal(err) 625 } 626 } 627 628 type scriptTest struct { 629 rec *TxRecord 630 block *Block 631 scripts [][]byte 632 } 633 runTest := func(tst *scriptTest) { 634 scripts, err := s.PreviousPkScripts(tst.rec, tst.block) 635 if err != nil { 636 t.Fatal(err) 637 } 638 height := int32(-1) 639 if tst.block != nil { 640 height = tst.block.Height 641 } 642 if len(scripts) != len(tst.scripts) { 643 t.Errorf("Transaction %v height %d: got len(scripts)=%d, expected %d", 644 tst.rec.Hash, height, len(scripts), len(tst.scripts)) 645 return 646 } 647 for i := range scripts { 648 if !bytes.Equal(scripts[i], tst.scripts[i]) { 649 // Format scripts with %s since they are (should be) ascii. 650 t.Errorf("Transaction %v height %d script %d: got '%s' expected '%s'", 651 tst.rec.Hash, height, i, scripts[i], tst.scripts[i]) 652 } 653 } 654 } 655 656 // Insert transactions A-C unmined, but mark no credits yet. Until 657 // these are marked as credits, PreviousPkScripts should not return 658 // them. 659 insertTx(recA, nil) 660 insertTx(recB, nil) 661 insertTx(recC, nil) 662 663 b100 := makeBlockMeta(100) 664 b101 := makeBlockMeta(101) 665 666 tests := []scriptTest{ 667 {recA, nil, nil}, 668 {recA, &b100.Block, nil}, 669 {recB, nil, nil}, 670 {recB, &b100.Block, nil}, 671 {recC, nil, nil}, 672 {recC, &b100.Block, nil}, 673 } 674 for _, tst := range tests { 675 runTest(&tst) 676 } 677 if t.Failed() { 678 t.Fatal("Failed after unmined tx inserts") 679 } 680 681 // Mark credits. Tx C output 1 not marked as a credit: tx D will spend 682 // both later but when C is mined, output 1's script should not be 683 // returned. 684 addCredit(recA, nil, 0) 685 addCredit(recA, nil, 1) 686 addCredit(recB, nil, 0) 687 addCredit(recB, nil, 1) 688 addCredit(recC, nil, 0) 689 tests = []scriptTest{ 690 {recA, nil, nil}, 691 {recA, &b100.Block, nil}, 692 {recB, nil, [][]byte{scriptA0, scriptA1}}, 693 {recB, &b100.Block, nil}, 694 {recC, nil, [][]byte{scriptB0, scriptB1}}, 695 {recC, &b100.Block, nil}, 696 } 697 for _, tst := range tests { 698 runTest(&tst) 699 } 700 if t.Failed() { 701 t.Fatal("Failed after marking unmined credits") 702 } 703 704 // Mine tx A in block 100. Test results should be identical. 705 insertTx(recA, &b100) 706 for _, tst := range tests { 707 runTest(&tst) 708 } 709 if t.Failed() { 710 t.Fatal("Failed after mining tx A") 711 } 712 713 // Mine tx B in block 101. 714 insertTx(recB, &b101) 715 tests = []scriptTest{ 716 {recA, nil, nil}, 717 {recA, &b100.Block, nil}, 718 {recB, nil, nil}, 719 {recB, &b101.Block, [][]byte{scriptA0, scriptA1}}, 720 {recC, nil, [][]byte{scriptB0, scriptB1}}, 721 {recC, &b101.Block, nil}, 722 } 723 for _, tst := range tests { 724 runTest(&tst) 725 } 726 if t.Failed() { 727 t.Fatal("Failed after mining tx B") 728 } 729 730 // Mine tx C in block 101 (same block as tx B) to test debits from the 731 // same block. 732 insertTx(recC, &b101) 733 tests = []scriptTest{ 734 {recA, nil, nil}, 735 {recA, &b100.Block, nil}, 736 {recB, nil, nil}, 737 {recB, &b101.Block, [][]byte{scriptA0, scriptA1}}, 738 {recC, nil, nil}, 739 {recC, &b101.Block, [][]byte{scriptB0, scriptB1}}, 740 } 741 for _, tst := range tests { 742 runTest(&tst) 743 } 744 if t.Failed() { 745 t.Fatal("Failed after mining tx C") 746 } 747 748 // Insert tx D, which spends C:0 and C:1. However, only C:0 is marked 749 // as a credit, and only that output script should be returned. 750 insertTx(recD, nil) 751 tests = append(tests, scriptTest{recD, nil, [][]byte{scriptC0}}) 752 tests = append(tests, scriptTest{recD, &b101.Block, nil}) 753 for _, tst := range tests { 754 runTest(&tst) 755 } 756 if t.Failed() { 757 t.Fatal("Failed after inserting tx D") 758 } 759 }