decred.org/dcrwallet/v3@v3.1.0/wallet/udb/stakevalidation_test.go (about) 1 // Copyright (c) 2016 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package udb 6 7 import ( 8 "bytes" 9 "context" 10 "testing" 11 "time" 12 13 _ "decred.org/dcrwallet/v3/wallet/internal/bdb" 14 "decred.org/dcrwallet/v3/wallet/walletdb" 15 "github.com/decred/dcrd/dcrutil/v4" 16 gcs2 "github.com/decred/dcrd/gcs/v4" 17 "github.com/decred/dcrd/wire" 18 ) 19 20 func insertMainChainHeaders(s *Store, dbtx walletdb.ReadWriteTx, 21 headerData []BlockHeaderData, filters []*gcs2.FilterV2) error { 22 23 ns := dbtx.ReadWriteBucket(wtxmgrBucketKey) 24 25 for i := range headerData { 26 h := &headerData[i] 27 f := filters[i] 28 header := new(wire.BlockHeader) 29 err := header.Deserialize(bytes.NewReader(h.SerializedHeader[:])) 30 if err != nil { 31 return err 32 } 33 err = s.ExtendMainChain(ns, header, f) 34 if err != nil { 35 return err 36 } 37 } 38 return nil 39 } 40 41 func TestStakeInvalidationOfTip(t *testing.T) { 42 ctx := context.Background() 43 db, _, s, _, teardown, err := cloneDB(ctx, "stake_inv_of_tip.kv") 44 defer teardown() 45 if err != nil { 46 t.Fatal(err) 47 } 48 49 g := makeBlockGenerator() 50 block1Header := g.generate(dcrutil.BlockValid) 51 block2Header := g.generate(dcrutil.BlockValid) 52 block3Header := g.generate(0) 53 54 block1Tx := wire.MsgTx{ 55 TxOut: []*wire.TxOut{{Value: 2e8}}, 56 } 57 block2Tx := wire.MsgTx{ 58 TxIn: []*wire.TxIn{ 59 { 60 PreviousOutPoint: wire.OutPoint{ 61 Hash: block1Tx.TxHash(), 62 Index: 0, 63 Tree: 0, 64 }, 65 }, 66 }, 67 TxOut: []*wire.TxOut{{Value: 1e8}}, 68 } 69 block1TxRec, err := NewTxRecordFromMsgTx(&block1Tx, time.Time{}) 70 if err != nil { 71 t.Fatal(err) 72 } 73 block2TxRec, err := NewTxRecordFromMsgTx(&block2Tx, time.Time{}) 74 if err != nil { 75 t.Fatal(err) 76 } 77 78 err = walletdb.Update(ctx, db, func(dbtx walletdb.ReadWriteTx) error { 79 err := s.InsertMemPoolTx(dbtx, block1TxRec) 80 if err != nil { 81 return err 82 } 83 err = s.AddCredit(dbtx, block1TxRec, nil, 0, false, 0) 84 if err != nil { 85 return err 86 } 87 err = s.InsertMemPoolTx(dbtx, block2TxRec) 88 if err != nil { 89 return err 90 } 91 err = s.AddCredit(dbtx, block2TxRec, nil, 0, false, 0) 92 if err != nil { 93 return err 94 } 95 96 bal, err := s.AccountBalance(dbtx, 0, 0) 97 if err != nil { 98 return err 99 } 100 if bal.Total != 1e8 { 101 t.Errorf("Wrong balance before mining either transaction: %v", bal) 102 } 103 104 headerData := makeHeaderDataSlice(block1Header, block2Header) 105 filters := emptyFilters(2) 106 err = insertMainChainHeaders(s, dbtx, headerData, filters) 107 if err != nil { 108 return err 109 } 110 111 err = s.InsertMinedTx(dbtx, block1TxRec, &headerData[0].BlockHash) 112 if err != nil { 113 return err 114 } 115 err = s.InsertMinedTx(dbtx, block2TxRec, &headerData[1].BlockHash) 116 if err != nil { 117 return err 118 } 119 120 // At this point there should only be one credit for the tx in block 2. 121 bal, err = s.AccountBalance(dbtx, 1, 0) 122 if err != nil { 123 return err 124 } 125 if bal.Total != dcrutil.Amount(block2Tx.TxOut[0].Value) { 126 t.Errorf("Wrong balance: expected %v got %v", 127 dcrutil.Amount(block2Tx.TxOut[0].Value), bal) 128 } 129 credits, err := s.UnspentOutputs(dbtx) 130 if err != nil { 131 return err 132 } 133 if len(credits) != 1 { 134 t.Errorf("Expected only 1 credit, got %v", len(credits)) 135 return nil 136 } 137 if credits[0].Hash != block2Tx.TxHash() { 138 t.Errorf("Credit hash does match tx from block 2") 139 return nil 140 } 141 if credits[0].Amount != dcrutil.Amount(block2Tx.TxOut[0].Value) { 142 t.Errorf("Credit value does not match tx output 0 from block 2") 143 return nil 144 } 145 146 // Add the next block header which invalidates the regular tx tree of 147 // block 2. 148 t.Log("Invalidating block 2") 149 headerData = makeHeaderDataSlice(block3Header) 150 filters = emptyFilters(1) 151 err = insertMainChainHeaders(s, dbtx, headerData, filters) 152 if err != nil { 153 return err 154 } 155 156 // Now the transaction in block 2 is invalidated. There should only be 157 // one unspent output, from block 1. 158 bal, err = s.AccountBalance(dbtx, 1, 0) 159 if err != nil { 160 return err 161 } 162 if bal.Total != dcrutil.Amount(block1Tx.TxOut[0].Value) { 163 t.Errorf("Wrong balance: expected %v got %v", dcrutil.Amount(block1Tx.TxOut[0].Value), bal) 164 } 165 credits, err = s.UnspentOutputs(dbtx) 166 if err != nil { 167 return err 168 } 169 if len(credits) != 1 { 170 t.Errorf("Expected only 1 credit, got %v", len(credits)) 171 return nil 172 } 173 if credits[0].Hash != block1Tx.TxHash() { 174 t.Errorf("Credit hash does not match tx from block 1") 175 return nil 176 } 177 if credits[0].Amount != dcrutil.Amount(block1Tx.TxOut[0].Value) { 178 t.Errorf("Credit value does not match tx output 0 from block 1") 179 return nil 180 } 181 182 return nil 183 }) 184 if err != nil { 185 t.Error(err) 186 } 187 }