// Copyright 2025 The go-ethereum Authors // This file is part of the go-ethereum library. // // The go-ethereum library is free software: you can redistribute it and/or modify // it under the terms of the GNU Lesser General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // The go-ethereum library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public License // along with the go-ethereum library. If not, see 0 { filter = &filters[rand.Intn(len(filters))] } else { filter = nil } var input uint64 if rand.Intn(2) == 0 { input = elements[rand.Intn(len(elements))] } else { input = uint64(rand.Uint32()) } index := sort.Search(len(elements), func(i int) bool { return elements[i] > input }) for index < len(elements) { if checkExt(filter, ext[index]) { break } index++ } var ( exp bool expVal uint64 remains []uint64 ) if index == len(elements) { exp = false } else { exp = true expVal = elements[index] index++ for index < len(elements) { if checkExt(filter, ext[index]) { remains = append(remains, elements[index]) } index++ } } it := newIter(filter) if err := checkSeekGT(it, input, exp, expVal); err != nil { t.Fatal(err) } if exp { if err := checkNext(it, remains); err != nil { t.Fatal(err) } } } } func verifyTraversal(t *testing.T, elements []uint64, ext [][]uint16, newIter func(filter *extFilter) HistoryIndexIterator) { set := make(map[extFilter]bool) for _, extList := range ext { for _, f := range extList { set[extFilter(f)] = true } } filters := slices.Collect(maps.Keys(set)) for i := 0; i < 16; i++ { var filter *extFilter if len(filters) > 0 { filter = &filters[rand.Intn(len(filters))] } else { filter = nil } it := newIter(filter) var ( pos int exp []uint64 ) for pos < len(elements) { if checkExt(filter, ext[pos]) { exp = append(exp, elements[pos]) } pos++ } if err := checkNext(it, exp); err != nil { t.Fatal(err) } } } func TestBlockIteratorSeekGT(t *testing.T) { for _, size := range []int{0, 2, 34} { for _, n := range []int{1, indexBlockRestartLen, 3 * indexBlockRestartLen} { data, elements, ext := makeTestIndexBlock(n, size) verifySeekGT(t, elements, ext, func(filter *extFilter) HistoryIndexIterator { br, err := newBlockReader(data, size != 0) if err != nil { t.Fatalf("Failed to open the block for reading, %v", err) } return br.newIterator(filter) }) } } } func TestIndexIteratorSeekGT(t *testing.T) { ident := newAccountIdent(common.Hash{0x1}) for _, size := range []int{0, 2, 34} { for _, n := range []int{1, 4096, 3 * 4096} { db := rawdb.NewMemoryDatabase() elements, ext := makeTestIndexBlocks(db, ident, n, size) verifySeekGT(t, elements, ext, func(filter *extFilter) HistoryIndexIterator { ir, err := newIndexReader(db, ident, size) if err != nil { t.Fatalf("Failed to open the index reader, %v", err) } return ir.newIterator(filter) }) } } } func TestBlockIteratorTraversal(t *testing.T) { /* 0-size index block is not allowed data, elements := makeTestIndexBlock(0) testBlockIterator(t, data, elements) */ for _, size := range []int{0, 2, 34} { for _, n := range []int{1, indexBlockRestartLen, 3 * indexBlockRestartLen} { data, elements, ext := makeTestIndexBlock(n, size) verifyTraversal(t, elements, ext, func(filter *extFilter) HistoryIndexIterator { br, err := newBlockReader(data, size != 0) if err != nil { t.Fatalf("Failed to open the block for reading, %v", err) } return br.newIterator(filter) }) } } } func TestIndexIteratorTraversal(t *testing.T) { ident := newAccountIdent(common.Hash{0x1}) for _, size := range []int{0, 2, 34} { for _, n := range []int{1, 4096, 3 * 4096} { db := rawdb.NewMemoryDatabase() elements, ext := makeTestIndexBlocks(db, ident, n, size) verifyTraversal(t, elements, ext, func(filter *extFilter) HistoryIndexIterator { ir, err := newIndexReader(db, ident, size) if err != nil { t.Fatalf("Failed to open the index reader, %v", err) } return ir.newIterator(filter) }) } } } func TestSeqIterBasicIteration(t *testing.T) { it := newSeqIter(5) // iterates over [1..5] it.SeekGT(0) var ( got []uint64 expected = []uint64{1, 2, 3, 4, 5} ) got = append(got, it.ID()) for it.Next() { got = append(got, it.ID()) } if len(got) != len(expected) { t.Fatalf("iteration length mismatch: got %v, expected %v", got, expected) } for i := range expected { if got[i] != expected[i] { t.Fatalf("element mismatch at %d: got %d, expected %d", i, got[i], expected[i]) } } } func TestSeqIterSeekGT(t *testing.T) { it := newSeqIter(5) tests := []struct { input uint64 ok bool expected uint64 }{ {0, true, 1}, {1, true, 2}, {4, true, 5}, {5, false, 0}, // 6 is out of range } for _, tt := range tests { ok := it.SeekGT(tt.input) if ok != tt.ok { t.Fatalf("SeekGT(%d) ok mismatch: got %v, expected %v", tt.input, ok, tt.ok) } if ok && it.ID() != tt.expected { t.Fatalf("SeekGT(%d) positioned at %d, expected %d", tt.input, it.ID(), tt.expected) } } }