github.com/ethereum/go-ethereum@v1.16.1/core/rawdb/chain_iterator_test.go (about) 1 // Copyright 2020 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package rawdb 18 19 import ( 20 "math/big" 21 "reflect" 22 "sort" 23 "sync" 24 "testing" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/core/types" 28 "github.com/ethereum/go-ethereum/ethdb" 29 ) 30 31 func TestChainIterator(t *testing.T) { 32 // Construct test chain db 33 chainDb := NewMemoryDatabase() 34 35 var block *types.Block 36 var txs []*types.Transaction 37 to := common.BytesToAddress([]byte{0x11}) 38 block = types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, newTestHasher()) // Empty genesis block 39 WriteBlock(chainDb, block) 40 WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) 41 for i := uint64(1); i <= 10; i++ { 42 var tx *types.Transaction 43 if i%2 == 0 { 44 tx = types.NewTx(&types.LegacyTx{ 45 Nonce: i, 46 GasPrice: big.NewInt(11111), 47 Gas: 1111, 48 To: &to, 49 Value: big.NewInt(111), 50 Data: []byte{0x11, 0x11, 0x11}, 51 }) 52 } else { 53 tx = types.NewTx(&types.AccessListTx{ 54 ChainID: big.NewInt(1337), 55 Nonce: i, 56 GasPrice: big.NewInt(11111), 57 Gas: 1111, 58 To: &to, 59 Value: big.NewInt(111), 60 Data: []byte{0x11, 0x11, 0x11}, 61 }) 62 } 63 txs = append(txs, tx) 64 block = types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, &types.Body{Transactions: types.Transactions{tx}}, nil, newTestHasher()) 65 WriteBlock(chainDb, block) 66 WriteCanonicalHash(chainDb, block.Hash(), block.NumberU64()) 67 } 68 69 var cases = []struct { 70 from, to uint64 71 reverse bool 72 expect []int 73 }{ 74 {0, 11, true, []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0}}, 75 {0, 0, true, nil}, 76 {0, 5, true, []int{4, 3, 2, 1, 0}}, 77 {10, 11, true, []int{10}}, 78 {0, 11, false, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}}, 79 {0, 0, false, nil}, 80 {10, 11, false, []int{10}}, 81 } 82 for i, c := range cases { 83 var numbers []int 84 hashCh := iterateTransactions(chainDb, c.from, c.to, c.reverse, nil) 85 if hashCh != nil { 86 for h := range hashCh { 87 numbers = append(numbers, int(h.number)) 88 if len(h.hashes) > 0 { 89 if got, exp := h.hashes[0], txs[h.number-1].Hash(); got != exp { 90 t.Fatalf("block %d: hash wrong, got %x exp %x", h.number, got, exp) 91 } 92 } 93 } 94 } 95 if !c.reverse { 96 sort.Ints(numbers) 97 } else { 98 sort.Sort(sort.Reverse(sort.IntSlice(numbers))) 99 } 100 if !reflect.DeepEqual(numbers, c.expect) { 101 t.Fatalf("Case %d failed, visit element mismatch, want %v, got %v", i, c.expect, numbers) 102 } 103 } 104 } 105 106 func initDatabaseWithTransactions(db ethdb.Database) ([]*types.Block, []*types.Transaction) { 107 var blocks []*types.Block 108 var txs []*types.Transaction 109 to := common.BytesToAddress([]byte{0x11}) 110 111 // Write empty genesis block 112 block := types.NewBlock(&types.Header{Number: big.NewInt(int64(0))}, nil, nil, newTestHasher()) 113 WriteBlock(db, block) 114 WriteCanonicalHash(db, block.Hash(), block.NumberU64()) 115 blocks = append(blocks, block) 116 117 // Create transactions. 118 for i := uint64(1); i <= 10; i++ { 119 var tx *types.Transaction 120 if i%2 == 0 { 121 tx = types.NewTx(&types.LegacyTx{ 122 Nonce: i, 123 GasPrice: big.NewInt(11111), 124 Gas: 1111, 125 To: &to, 126 Value: big.NewInt(111), 127 Data: []byte{0x11, 0x11, 0x11}, 128 }) 129 } else { 130 tx = types.NewTx(&types.AccessListTx{ 131 ChainID: big.NewInt(1337), 132 Nonce: i, 133 GasPrice: big.NewInt(11111), 134 Gas: 1111, 135 To: &to, 136 Value: big.NewInt(111), 137 Data: []byte{0x11, 0x11, 0x11}, 138 }) 139 } 140 txs = append(txs, tx) 141 block := types.NewBlock(&types.Header{Number: big.NewInt(int64(i))}, &types.Body{Transactions: types.Transactions{tx}}, nil, newTestHasher()) 142 WriteBlock(db, block) 143 WriteCanonicalHash(db, block.Hash(), block.NumberU64()) 144 blocks = append(blocks, block) 145 } 146 147 return blocks, txs 148 } 149 150 func TestIndexTransactions(t *testing.T) { 151 // Construct test chain db 152 chainDB := NewMemoryDatabase() 153 154 _, txs := initDatabaseWithTransactions(chainDB) 155 156 // verify checks whether the tx indices in the range [from, to) 157 // is expected. 158 verify := func(from, to int, exist bool, tail uint64) { 159 for i := from; i < to; i++ { 160 if i == 0 { 161 continue 162 } 163 number := ReadTxLookupEntry(chainDB, txs[i-1].Hash()) 164 if exist && number == nil { 165 t.Fatalf("Transaction index %d missing", i) 166 } 167 if !exist && number != nil { 168 t.Fatalf("Transaction index %d is not deleted", i) 169 } 170 } 171 number := ReadTxIndexTail(chainDB) 172 if number == nil || *number != tail { 173 t.Fatalf("Transaction tail mismatch") 174 } 175 } 176 IndexTransactions(chainDB, 5, 11, nil, false) 177 verify(5, 11, true, 5) 178 verify(0, 5, false, 5) 179 180 IndexTransactions(chainDB, 0, 5, nil, false) 181 verify(0, 11, true, 0) 182 183 UnindexTransactions(chainDB, 0, 5, nil, false) 184 verify(5, 11, true, 5) 185 verify(0, 5, false, 5) 186 187 UnindexTransactions(chainDB, 5, 11, nil, false) 188 verify(0, 11, false, 11) 189 190 // Testing corner cases 191 signal := make(chan struct{}) 192 var once sync.Once 193 indexTransactionsForTesting(chainDB, 5, 11, signal, func(n uint64) bool { 194 if n <= 8 { 195 once.Do(func() { 196 close(signal) 197 }) 198 return false 199 } 200 return true 201 }) 202 verify(9, 11, true, 9) 203 verify(0, 9, false, 9) 204 IndexTransactions(chainDB, 0, 9, nil, false) 205 206 signal = make(chan struct{}) 207 var once2 sync.Once 208 unindexTransactionsForTesting(chainDB, 0, 11, signal, func(n uint64) bool { 209 if n >= 8 { 210 once2.Do(func() { 211 close(signal) 212 }) 213 return false 214 } 215 return true 216 }) 217 verify(8, 11, true, 8) 218 verify(0, 8, false, 8) 219 } 220 221 func TestPruneTransactionIndex(t *testing.T) { 222 chainDB := NewMemoryDatabase() 223 blocks, _ := initDatabaseWithTransactions(chainDB) 224 lastBlock := blocks[len(blocks)-1].NumberU64() 225 pruneBlock := lastBlock - 3 226 227 IndexTransactions(chainDB, 0, lastBlock+1, nil, false) 228 229 // Check all transactions are in index. 230 for _, block := range blocks { 231 for _, tx := range block.Transactions() { 232 num := ReadTxLookupEntry(chainDB, tx.Hash()) 233 if num == nil || *num != block.NumberU64() { 234 t.Fatalf("wrong TxLookup entry: %x -> %v", tx.Hash(), num) 235 } 236 } 237 } 238 239 PruneTransactionIndex(chainDB, pruneBlock) 240 241 // Check transactions from old blocks not included. 242 for _, block := range blocks { 243 for _, tx := range block.Transactions() { 244 num := ReadTxLookupEntry(chainDB, tx.Hash()) 245 if block.NumberU64() < pruneBlock && num != nil { 246 t.Fatalf("TxLookup entry not removed: %x -> %v", tx.Hash(), num) 247 } 248 if block.NumberU64() >= pruneBlock && (num == nil || *num != block.NumberU64()) { 249 t.Fatalf("wrong TxLookup entry after pruning: %x -> %v", tx.Hash(), num) 250 } 251 } 252 } 253 }