github.com/ethereum/go-ethereum@v1.16.1/triedb/pathdb/history_index_test.go (about) 1 // Copyright 2025 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 pathdb 18 19 import ( 20 "math" 21 "math/rand" 22 "slices" 23 "sort" 24 "testing" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/core/rawdb" 28 "github.com/ethereum/go-ethereum/crypto" 29 ) 30 31 func TestIndexReaderBasic(t *testing.T) { 32 elements := []uint64{ 33 1, 5, 10, 11, 20, 34 } 35 db := rawdb.NewMemoryDatabase() 36 bw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa})) 37 for i := 0; i < len(elements); i++ { 38 bw.append(elements[i]) 39 } 40 batch := db.NewBatch() 41 bw.finish(batch) 42 batch.Write() 43 44 br, err := newIndexReader(db, newAccountIdent(common.Hash{0xa})) 45 if err != nil { 46 t.Fatalf("Failed to construct the index reader, %v", err) 47 } 48 cases := []struct { 49 value uint64 50 result uint64 51 }{ 52 {0, 1}, 53 {1, 5}, 54 {10, 11}, 55 {19, 20}, 56 {20, math.MaxUint64}, 57 {21, math.MaxUint64}, 58 } 59 for _, c := range cases { 60 got, err := br.readGreaterThan(c.value) 61 if err != nil { 62 t.Fatalf("Unexpected error, got %v", err) 63 } 64 if got != c.result { 65 t.Fatalf("Unexpected result, got %v, wanted %v", got, c.result) 66 } 67 } 68 } 69 70 func TestIndexReaderLarge(t *testing.T) { 71 var elements []uint64 72 for i := 0; i < 10*indexBlockEntriesCap; i++ { 73 elements = append(elements, rand.Uint64()) 74 } 75 slices.Sort(elements) 76 77 db := rawdb.NewMemoryDatabase() 78 bw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa})) 79 for i := 0; i < len(elements); i++ { 80 bw.append(elements[i]) 81 } 82 batch := db.NewBatch() 83 bw.finish(batch) 84 batch.Write() 85 86 br, err := newIndexReader(db, newAccountIdent(common.Hash{0xa})) 87 if err != nil { 88 t.Fatalf("Failed to construct the index reader, %v", err) 89 } 90 for i := 0; i < 100; i++ { 91 value := rand.Uint64() 92 pos := sort.Search(len(elements), func(i int) bool { 93 return elements[i] > value 94 }) 95 got, err := br.readGreaterThan(value) 96 if err != nil { 97 t.Fatalf("Unexpected error, got %v", err) 98 } 99 if pos == len(elements) { 100 if got != math.MaxUint64 { 101 t.Fatalf("Unexpected result, got %d, wanted math.MaxUint64", got) 102 } 103 } else if got != elements[pos] { 104 t.Fatalf("Unexpected result, got %d, wanted %d", got, elements[pos]) 105 } 106 } 107 } 108 109 func TestEmptyIndexReader(t *testing.T) { 110 br, err := newIndexReader(rawdb.NewMemoryDatabase(), newAccountIdent(common.Hash{0xa})) 111 if err != nil { 112 t.Fatalf("Failed to construct the index reader, %v", err) 113 } 114 res, err := br.readGreaterThan(100) 115 if err != nil { 116 t.Fatalf("Failed to query, %v", err) 117 } 118 if res != math.MaxUint64 { 119 t.Fatalf("Unexpected result, got %d, wanted math.MaxUint64", res) 120 } 121 } 122 123 func TestIndexWriterBasic(t *testing.T) { 124 db := rawdb.NewMemoryDatabase() 125 iw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa})) 126 iw.append(2) 127 if err := iw.append(1); err == nil { 128 t.Fatal("out-of-order insertion is not expected") 129 } 130 for i := 0; i < 10; i++ { 131 iw.append(uint64(i + 3)) 132 } 133 batch := db.NewBatch() 134 iw.finish(batch) 135 batch.Write() 136 137 iw, err := newIndexWriter(db, newAccountIdent(common.Hash{0xa})) 138 if err != nil { 139 t.Fatalf("Failed to construct the block writer, %v", err) 140 } 141 for i := 0; i < 10; i++ { 142 if err := iw.append(uint64(i + 100)); err != nil { 143 t.Fatalf("Failed to append item, %v", err) 144 } 145 } 146 iw.finish(db.NewBatch()) 147 } 148 149 func TestIndexWriterDelete(t *testing.T) { 150 db := rawdb.NewMemoryDatabase() 151 iw, _ := newIndexWriter(db, newAccountIdent(common.Hash{0xa})) 152 for i := 0; i < indexBlockEntriesCap*4; i++ { 153 iw.append(uint64(i + 1)) 154 } 155 batch := db.NewBatch() 156 iw.finish(batch) 157 batch.Write() 158 159 // Delete unknown id, the request should be rejected 160 id, _ := newIndexDeleter(db, newAccountIdent(common.Hash{0xa})) 161 if err := id.pop(indexBlockEntriesCap * 5); err == nil { 162 t.Fatal("Expect error to occur for unknown id") 163 } 164 for i := indexBlockEntriesCap * 4; i >= 1; i-- { 165 if err := id.pop(uint64(i)); err != nil { 166 t.Fatalf("Unexpected error for element popping, %v", err) 167 } 168 if id.lastID != uint64(i-1) { 169 t.Fatalf("Unexpected lastID, want: %d, got: %d", uint64(i-1), iw.lastID) 170 } 171 if rand.Intn(10) == 0 { 172 batch := db.NewBatch() 173 id.finish(batch) 174 batch.Write() 175 } 176 } 177 } 178 179 func TestBatchIndexerWrite(t *testing.T) { 180 var ( 181 db = rawdb.NewMemoryDatabase() 182 batch = newBatchIndexer(db, false) 183 histories = makeHistories(10) 184 ) 185 for i, h := range histories { 186 if err := batch.process(h, uint64(i+1)); err != nil { 187 t.Fatalf("Failed to process history, %v", err) 188 } 189 } 190 if err := batch.finish(true); err != nil { 191 t.Fatalf("Failed to finish batch indexer, %v", err) 192 } 193 metadata := loadIndexMetadata(db) 194 if metadata == nil || metadata.Last != uint64(10) { 195 t.Fatal("Unexpected index position") 196 } 197 var ( 198 accounts = make(map[common.Hash][]uint64) 199 storages = make(map[common.Hash]map[common.Hash][]uint64) 200 ) 201 for i, h := range histories { 202 for _, addr := range h.accountList { 203 addrHash := crypto.Keccak256Hash(addr.Bytes()) 204 accounts[addrHash] = append(accounts[addrHash], uint64(i+1)) 205 206 if _, ok := storages[addrHash]; !ok { 207 storages[addrHash] = make(map[common.Hash][]uint64) 208 } 209 for _, slot := range h.storageList[addr] { 210 storages[addrHash][slot] = append(storages[addrHash][slot], uint64(i+1)) 211 } 212 } 213 } 214 for addrHash, indexes := range accounts { 215 ir, _ := newIndexReader(db, newAccountIdent(addrHash)) 216 for i := 0; i < len(indexes)-1; i++ { 217 n, err := ir.readGreaterThan(indexes[i]) 218 if err != nil { 219 t.Fatalf("Failed to read index, %v", err) 220 } 221 if n != indexes[i+1] { 222 t.Fatalf("Unexpected result, want %d, got %d", indexes[i+1], n) 223 } 224 } 225 n, err := ir.readGreaterThan(indexes[len(indexes)-1]) 226 if err != nil { 227 t.Fatalf("Failed to read index, %v", err) 228 } 229 if n != math.MaxUint64 { 230 t.Fatalf("Unexpected result, want math.MaxUint64, got %d", n) 231 } 232 } 233 for addrHash, slots := range storages { 234 for slotHash, indexes := range slots { 235 ir, _ := newIndexReader(db, newStorageIdent(addrHash, slotHash)) 236 for i := 0; i < len(indexes)-1; i++ { 237 n, err := ir.readGreaterThan(indexes[i]) 238 if err != nil { 239 t.Fatalf("Failed to read index, %v", err) 240 } 241 if n != indexes[i+1] { 242 t.Fatalf("Unexpected result, want %d, got %d", indexes[i+1], n) 243 } 244 } 245 n, err := ir.readGreaterThan(indexes[len(indexes)-1]) 246 if err != nil { 247 t.Fatalf("Failed to read index, %v", err) 248 } 249 if n != math.MaxUint64 { 250 t.Fatalf("Unexpected result, want math.MaxUint64, got %d", n) 251 } 252 } 253 } 254 } 255 256 func TestBatchIndexerDelete(t *testing.T) { 257 var ( 258 db = rawdb.NewMemoryDatabase() 259 bw = newBatchIndexer(db, false) 260 histories = makeHistories(10) 261 ) 262 // Index histories 263 for i, h := range histories { 264 if err := bw.process(h, uint64(i+1)); err != nil { 265 t.Fatalf("Failed to process history, %v", err) 266 } 267 } 268 if err := bw.finish(true); err != nil { 269 t.Fatalf("Failed to finish batch indexer, %v", err) 270 } 271 272 // Unindex histories 273 bd := newBatchIndexer(db, true) 274 for i := len(histories) - 1; i >= 0; i-- { 275 if err := bd.process(histories[i], uint64(i+1)); err != nil { 276 t.Fatalf("Failed to process history, %v", err) 277 } 278 } 279 if err := bd.finish(true); err != nil { 280 t.Fatalf("Failed to finish batch indexer, %v", err) 281 } 282 283 metadata := loadIndexMetadata(db) 284 if metadata != nil { 285 t.Fatal("Unexpected index position") 286 } 287 it := db.NewIterator(rawdb.StateHistoryIndexPrefix, nil) 288 for it.Next() { 289 t.Fatal("Leftover history index data") 290 } 291 it.Release() 292 }