plugeth/internal/era/e2store/e2store_test.go
lightclient 1f50aa7631
cmd,internal/era: implement export-history subcommand (#26621)
* all: implement era format, add history importer/export

* internal/era/e2store: refactor e2store to provide ReadAt interface

* internal/era/e2store: export HeaderSize

* internal/era: refactor era to use ReadAt interface

* internal/era: elevate anonymous func to named

* cmd/utils: don't store entire era file in-memory during import / export

* internal/era: better abstraction between era and e2store

* cmd/era: properly close era files

* cmd/era: don't let defers stack

* cmd/geth: add description for import-history

* cmd/utils: better bytes buffer

* internal/era: error if accumulator has more records than max allowed

* internal/era: better doc comment

* internal/era/e2store: rm superfluous reader, rm superfluous testcases, add fuzzer

* internal/era: avoid some repetition

* internal/era: simplify clauses

* internal/era: unexport things

* internal/era,cmd/utils,cmd/era: change to iterator interface for reading era entries

* cmd/utils: better defer handling in history test

* internal/era,cmd: add number method to era iterator to get the current block number

* internal/era/e2store: avoid double allocation during write

* internal/era,cmd/utils: fix lint issues

* internal/era: add ReaderAt func so entry value can be read lazily

Co-authored-by: lightclient <lightclient@protonmail.com>
Co-authored-by: Martin Holst Swende <martin@swende.se>

* internal/era: improve iterator interface

* internal/era: fix rlp decode of header and correctly read total difficulty

* cmd/era: fix rebase errors

* cmd/era: clearer comments

* cmd,internal: fix comment typos

---------

Co-authored-by: Martin Holst Swende <martin@swende.se>
2024-02-07 09:18:27 -07:00

151 lines
3.7 KiB
Go

// Copyright 2023 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
package e2store
import (
"bytes"
"fmt"
"io"
"testing"
"github.com/ethereum/go-ethereum/common"
)
func TestEncode(t *testing.T) {
for _, test := range []struct {
entries []Entry
want string
name string
}{
{
name: "emptyEntry",
entries: []Entry{{0xffff, nil}},
want: "ffff000000000000",
},
{
name: "beef",
entries: []Entry{{42, common.Hex2Bytes("beef")}},
want: "2a00020000000000beef",
},
{
name: "twoEntries",
entries: []Entry{
{42, common.Hex2Bytes("beef")},
{9, common.Hex2Bytes("abcdabcd")},
},
want: "2a00020000000000beef0900040000000000abcdabcd",
},
} {
tt := test
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var (
b = bytes.NewBuffer(nil)
w = NewWriter(b)
)
for _, e := range tt.entries {
if _, err := w.Write(e.Type, e.Value); err != nil {
t.Fatalf("encoding error: %v", err)
}
}
if want, have := common.FromHex(tt.want), b.Bytes(); !bytes.Equal(want, have) {
t.Fatalf("encoding mismatch (want %x, have %x", want, have)
}
r := NewReader(bytes.NewReader(b.Bytes()))
for _, want := range tt.entries {
have, err := r.Read()
if err != nil {
t.Fatalf("decoding error: %v", err)
}
if have.Type != want.Type {
t.Fatalf("decoded entry does type mismatch (want %v, got %v)", want.Type, have.Type)
}
if !bytes.Equal(have.Value, want.Value) {
t.Fatalf("decoded entry does not match (want %#x, got %#x)", want.Value, have.Value)
}
}
})
}
}
func TestDecode(t *testing.T) {
for i, tt := range []struct {
have string
err error
}{
{ // basic valid decoding
have: "ffff000000000000",
},
{ // basic invalid decoding
have: "ffff000000000001",
err: fmt.Errorf("reserved bytes are non-zero"),
},
{ // no more entries to read, returns EOF
have: "",
err: io.EOF,
},
{ // malformed type
have: "bad",
err: io.ErrUnexpectedEOF,
},
{ // malformed length
have: "badbeef",
err: io.ErrUnexpectedEOF,
},
{ // specified length longer than actual value
have: "beef010000000000",
err: io.ErrUnexpectedEOF,
},
} {
r := NewReader(bytes.NewReader(common.FromHex(tt.have)))
if tt.err != nil {
_, err := r.Read()
if err == nil && tt.err != nil {
t.Fatalf("test %d, expected error, got none", i)
}
if err != nil && tt.err == nil {
t.Fatalf("test %d, expected no error, got %v", i, err)
}
if err != nil && tt.err != nil && err.Error() != tt.err.Error() {
t.Fatalf("expected error %v, got %v", tt.err, err)
}
continue
}
}
}
func FuzzCodec(f *testing.F) {
f.Fuzz(func(t *testing.T, input []byte) {
r := NewReader(bytes.NewReader(input))
entry, err := r.Read()
if err != nil {
return
}
var (
b = bytes.NewBuffer(nil)
w = NewWriter(b)
)
w.Write(entry.Type, entry.Value)
output := b.Bytes()
// Only care about the input that was actually consumed
input = input[:r.offset]
if !bytes.Equal(input, output) {
t.Fatalf("decode-encode mismatch, input %#x output %#x", input, output)
}
})
}