mirror of https://github.com/ethereum/go-ethereum
tests/fuzzers: move fuzzers into native packages (#28467)
This PR moves our fuzzers from tests/fuzzers into whatever their respective 'native' package is. The historical reason why they were placed in an external location, is that when they were based on go-fuzz, they could not be "hidden" via the _test.go prefix. So in order to shove them away from the go-ethereum "production code", they were put aside. But now we've rewritten them to be based on golang testing, and thus can be brought back. I've left (in tests/) the ones that are not production (bls128381), require non-standard imports (secp requires btcec, bn256 requires gnark/google/cloudflare deps). This PR also adds a fuzzer for precompiled contracts, because why not. This PR utilizes a newly rewritten replacement for go-118-fuzz-build, namely gofuzz-shim, which utilises the inputs from the fuzzing engine better.pull/28523/head
parent
24d46224c1
commit
2391fbc676
@ -0,0 +1,147 @@ |
|||||||
|
// Copyright 2019 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package types |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"fmt" |
||||||
|
"math/big" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/rlp" |
||||||
|
"github.com/holiman/uint256" |
||||||
|
) |
||||||
|
|
||||||
|
func decodeEncode(input []byte, val interface{}) error { |
||||||
|
if err := rlp.DecodeBytes(input, val); err != nil { |
||||||
|
// not valid rlp, nothing to do
|
||||||
|
return nil |
||||||
|
} |
||||||
|
// If it _were_ valid rlp, we can encode it again
|
||||||
|
output, err := rlp.EncodeToBytes(val) |
||||||
|
if err != nil { |
||||||
|
return err |
||||||
|
} |
||||||
|
if !bytes.Equal(input, output) { |
||||||
|
return fmt.Errorf("encode-decode is not equal, \ninput : %x\noutput: %x", input, output) |
||||||
|
} |
||||||
|
return nil |
||||||
|
} |
||||||
|
|
||||||
|
func FuzzRLP(f *testing.F) { |
||||||
|
f.Fuzz(fuzzRlp) |
||||||
|
} |
||||||
|
|
||||||
|
func fuzzRlp(t *testing.T, input []byte) { |
||||||
|
if len(input) == 0 || len(input) > 500*1024 { |
||||||
|
return |
||||||
|
} |
||||||
|
rlp.Split(input) |
||||||
|
if elems, _, err := rlp.SplitList(input); err == nil { |
||||||
|
rlp.CountValues(elems) |
||||||
|
} |
||||||
|
rlp.NewStream(bytes.NewReader(input), 0).Decode(new(interface{})) |
||||||
|
if err := decodeEncode(input, new(interface{})); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
{ |
||||||
|
var v struct { |
||||||
|
Int uint |
||||||
|
String string |
||||||
|
Bytes []byte |
||||||
|
} |
||||||
|
if err := decodeEncode(input, &v); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
} |
||||||
|
{ |
||||||
|
type Types struct { |
||||||
|
Bool bool |
||||||
|
Raw rlp.RawValue |
||||||
|
Slice []*Types |
||||||
|
Iface []interface{} |
||||||
|
} |
||||||
|
var v Types |
||||||
|
if err := decodeEncode(input, &v); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
} |
||||||
|
{ |
||||||
|
type AllTypes struct { |
||||||
|
Int uint |
||||||
|
String string |
||||||
|
Bytes []byte |
||||||
|
Bool bool |
||||||
|
Raw rlp.RawValue |
||||||
|
Slice []*AllTypes |
||||||
|
Array [3]*AllTypes |
||||||
|
Iface []interface{} |
||||||
|
} |
||||||
|
var v AllTypes |
||||||
|
if err := decodeEncode(input, &v); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
} |
||||||
|
{ |
||||||
|
if err := decodeEncode(input, [10]byte{}); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
} |
||||||
|
{ |
||||||
|
var v struct { |
||||||
|
Byte [10]byte |
||||||
|
Rool [10]bool |
||||||
|
} |
||||||
|
if err := decodeEncode(input, &v); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
} |
||||||
|
{ |
||||||
|
var h Header |
||||||
|
if err := decodeEncode(input, &h); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
var b Block |
||||||
|
if err := decodeEncode(input, &b); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
var tx Transaction |
||||||
|
if err := decodeEncode(input, &tx); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
var txs Transactions |
||||||
|
if err := decodeEncode(input, &txs); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
var rs Receipts |
||||||
|
if err := decodeEncode(input, &rs); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
} |
||||||
|
{ |
||||||
|
var v struct { |
||||||
|
AnIntPtr *big.Int |
||||||
|
AnInt big.Int |
||||||
|
AnU256Ptr *uint256.Int |
||||||
|
AnU256 uint256.Int |
||||||
|
NotAnU256 [4]uint64 |
||||||
|
} |
||||||
|
if err := decodeEncode(input, &v); err != nil { |
||||||
|
t.Fatal(err) |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -1,68 +0,0 @@ |
|||||||
// Copyright 2023 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package bitutil |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"testing" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/bitutil" |
|
||||||
) |
|
||||||
|
|
||||||
func FuzzEncoder(f *testing.F) { |
|
||||||
f.Fuzz(func(t *testing.T, data []byte) { |
|
||||||
fuzzEncode(data) |
|
||||||
}) |
|
||||||
} |
|
||||||
func FuzzDecoder(f *testing.F) { |
|
||||||
f.Fuzz(func(t *testing.T, data []byte) { |
|
||||||
fuzzDecode(data) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
// fuzzEncode implements a go-fuzz fuzzer method to test the bitset encoding and
|
|
||||||
// decoding algorithm.
|
|
||||||
func fuzzEncode(data []byte) { |
|
||||||
proc, _ := bitutil.DecompressBytes(bitutil.CompressBytes(data), len(data)) |
|
||||||
if !bytes.Equal(data, proc) { |
|
||||||
panic("content mismatch") |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and
|
|
||||||
// reencoding algorithm.
|
|
||||||
func fuzzDecode(data []byte) { |
|
||||||
blob, err := bitutil.DecompressBytes(data, 1024) |
|
||||||
if err != nil { |
|
||||||
return |
|
||||||
} |
|
||||||
// re-compress it (it's OK if the re-compressed differs from the
|
|
||||||
// original - the first input may not have been compressed at all)
|
|
||||||
comp := bitutil.CompressBytes(blob) |
|
||||||
if len(comp) > len(blob) { |
|
||||||
// After compression, it must be smaller or equal
|
|
||||||
panic("bad compression") |
|
||||||
} |
|
||||||
// But decompressing it once again should work
|
|
||||||
decomp, err := bitutil.DecompressBytes(data, 1024) |
|
||||||
if err != nil { |
|
||||||
panic(err) |
|
||||||
} |
|
||||||
if !bytes.Equal(decomp, blob) { |
|
||||||
panic("content mismatch") |
|
||||||
} |
|
||||||
} |
|
@ -1,37 +0,0 @@ |
|||||||
// Copyright 2019 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package keystore |
|
||||||
|
|
||||||
import ( |
|
||||||
"os" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/keystore" |
|
||||||
) |
|
||||||
|
|
||||||
func fuzz(input []byte) int { |
|
||||||
ks := keystore.NewKeyStore("/tmp/ks", keystore.LightScryptN, keystore.LightScryptP) |
|
||||||
|
|
||||||
a, err := ks.NewAccount(string(input)) |
|
||||||
if err != nil { |
|
||||||
panic(err) |
|
||||||
} |
|
||||||
if err := ks.Unlock(a, string(input)); err != nil { |
|
||||||
panic(err) |
|
||||||
} |
|
||||||
os.Remove(a.URL.Path) |
|
||||||
return 1 |
|
||||||
} |
|
Binary file not shown.
@ -1 +0,0 @@ |
|||||||
Ë€€€À€ÀÃÀÀÀÀ |
|
@ -1,143 +0,0 @@ |
|||||||
// Copyright 2019 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package rlp |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"fmt" |
|
||||||
"math/big" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/types" |
|
||||||
"github.com/ethereum/go-ethereum/rlp" |
|
||||||
"github.com/holiman/uint256" |
|
||||||
) |
|
||||||
|
|
||||||
func decodeEncode(input []byte, val interface{}, i int) { |
|
||||||
if err := rlp.DecodeBytes(input, val); err == nil { |
|
||||||
output, err := rlp.EncodeToBytes(val) |
|
||||||
if err != nil { |
|
||||||
panic(err) |
|
||||||
} |
|
||||||
if !bytes.Equal(input, output) { |
|
||||||
panic(fmt.Sprintf("case %d: encode-decode is not equal, \ninput : %x\noutput: %x", i, input, output)) |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
func fuzz(input []byte) int { |
|
||||||
if len(input) == 0 { |
|
||||||
return 0 |
|
||||||
} |
|
||||||
if len(input) > 500*1024 { |
|
||||||
return 0 |
|
||||||
} |
|
||||||
|
|
||||||
var i int |
|
||||||
{ |
|
||||||
rlp.Split(input) |
|
||||||
} |
|
||||||
{ |
|
||||||
if elems, _, err := rlp.SplitList(input); err == nil { |
|
||||||
rlp.CountValues(elems) |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
{ |
|
||||||
rlp.NewStream(bytes.NewReader(input), 0).Decode(new(interface{})) |
|
||||||
} |
|
||||||
|
|
||||||
{ |
|
||||||
decodeEncode(input, new(interface{}), i) |
|
||||||
i++ |
|
||||||
} |
|
||||||
{ |
|
||||||
var v struct { |
|
||||||
Int uint |
|
||||||
String string |
|
||||||
Bytes []byte |
|
||||||
} |
|
||||||
decodeEncode(input, &v, i) |
|
||||||
i++ |
|
||||||
} |
|
||||||
|
|
||||||
{ |
|
||||||
type Types struct { |
|
||||||
Bool bool |
|
||||||
Raw rlp.RawValue |
|
||||||
Slice []*Types |
|
||||||
Iface []interface{} |
|
||||||
} |
|
||||||
var v Types |
|
||||||
decodeEncode(input, &v, i) |
|
||||||
i++ |
|
||||||
} |
|
||||||
{ |
|
||||||
type AllTypes struct { |
|
||||||
Int uint |
|
||||||
String string |
|
||||||
Bytes []byte |
|
||||||
Bool bool |
|
||||||
Raw rlp.RawValue |
|
||||||
Slice []*AllTypes |
|
||||||
Array [3]*AllTypes |
|
||||||
Iface []interface{} |
|
||||||
} |
|
||||||
var v AllTypes |
|
||||||
decodeEncode(input, &v, i) |
|
||||||
i++ |
|
||||||
} |
|
||||||
{ |
|
||||||
decodeEncode(input, [10]byte{}, i) |
|
||||||
i++ |
|
||||||
} |
|
||||||
{ |
|
||||||
var v struct { |
|
||||||
Byte [10]byte |
|
||||||
Rool [10]bool |
|
||||||
} |
|
||||||
decodeEncode(input, &v, i) |
|
||||||
i++ |
|
||||||
} |
|
||||||
{ |
|
||||||
var h types.Header |
|
||||||
decodeEncode(input, &h, i) |
|
||||||
i++ |
|
||||||
var b types.Block |
|
||||||
decodeEncode(input, &b, i) |
|
||||||
i++ |
|
||||||
var t types.Transaction |
|
||||||
decodeEncode(input, &t, i) |
|
||||||
i++ |
|
||||||
var txs types.Transactions |
|
||||||
decodeEncode(input, &txs, i) |
|
||||||
i++ |
|
||||||
var rs types.Receipts |
|
||||||
decodeEncode(input, &rs, i) |
|
||||||
} |
|
||||||
{ |
|
||||||
i++ |
|
||||||
var v struct { |
|
||||||
AnIntPtr *big.Int |
|
||||||
AnInt big.Int |
|
||||||
AnU256Ptr *uint256.Int |
|
||||||
AnU256 uint256.Int |
|
||||||
NotAnU256 [4]uint64 |
|
||||||
} |
|
||||||
decodeEncode(input, &v, i) |
|
||||||
} |
|
||||||
return 1 |
|
||||||
} |
|
@ -1,47 +0,0 @@ |
|||||||
// Copyright 2023 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package snap |
|
||||||
|
|
||||||
import ( |
|
||||||
"testing" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/eth/protocols/snap" |
|
||||||
) |
|
||||||
|
|
||||||
func FuzzARange(f *testing.F) { |
|
||||||
f.Fuzz(func(t *testing.T, data []byte) { |
|
||||||
doFuzz(data, &snap.GetAccountRangePacket{}, snap.GetAccountRangeMsg) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func FuzzSRange(f *testing.F) { |
|
||||||
f.Fuzz(func(t *testing.T, data []byte) { |
|
||||||
doFuzz(data, &snap.GetStorageRangesPacket{}, snap.GetStorageRangesMsg) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func FuzzByteCodes(f *testing.F) { |
|
||||||
f.Fuzz(func(t *testing.T, data []byte) { |
|
||||||
doFuzz(data, &snap.GetByteCodesPacket{}, snap.GetByteCodesMsg) |
|
||||||
}) |
|
||||||
} |
|
||||||
|
|
||||||
func FuzzTrieNodes(f *testing.F) { |
|
||||||
f.Fuzz(func(t *testing.T, data []byte) { |
|
||||||
doFuzz(data, &snap.GetTrieNodesPacket{}, snap.GetTrieNodesMsg) |
|
||||||
}) |
|
||||||
} |
|
@ -1,248 +0,0 @@ |
|||||||
// Copyright 2020 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package stacktrie |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"encoding/binary" |
|
||||||
"errors" |
|
||||||
"fmt" |
|
||||||
"hash" |
|
||||||
"io" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common" |
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb" |
|
||||||
"github.com/ethereum/go-ethereum/core/types" |
|
||||||
"github.com/ethereum/go-ethereum/crypto" |
|
||||||
"github.com/ethereum/go-ethereum/ethdb" |
|
||||||
"github.com/ethereum/go-ethereum/trie" |
|
||||||
"github.com/ethereum/go-ethereum/trie/trienode" |
|
||||||
"golang.org/x/crypto/sha3" |
|
||||||
"golang.org/x/exp/slices" |
|
||||||
) |
|
||||||
|
|
||||||
type fuzzer struct { |
|
||||||
input io.Reader |
|
||||||
exhausted bool |
|
||||||
debugging bool |
|
||||||
} |
|
||||||
|
|
||||||
func (f *fuzzer) read(size int) []byte { |
|
||||||
out := make([]byte, size) |
|
||||||
if _, err := f.input.Read(out); err != nil { |
|
||||||
f.exhausted = true |
|
||||||
} |
|
||||||
return out |
|
||||||
} |
|
||||||
|
|
||||||
func (f *fuzzer) readSlice(min, max int) []byte { |
|
||||||
var a uint16 |
|
||||||
binary.Read(f.input, binary.LittleEndian, &a) |
|
||||||
size := min + int(a)%(max-min) |
|
||||||
out := make([]byte, size) |
|
||||||
if _, err := f.input.Read(out); err != nil { |
|
||||||
f.exhausted = true |
|
||||||
} |
|
||||||
return out |
|
||||||
} |
|
||||||
|
|
||||||
// spongeDb is a dummy db backend which accumulates writes in a sponge
|
|
||||||
type spongeDb struct { |
|
||||||
sponge hash.Hash |
|
||||||
debug bool |
|
||||||
} |
|
||||||
|
|
||||||
func (s *spongeDb) Has(key []byte) (bool, error) { panic("implement me") } |
|
||||||
func (s *spongeDb) Get(key []byte) ([]byte, error) { return nil, errors.New("no such elem") } |
|
||||||
func (s *spongeDb) Delete(key []byte) error { panic("implement me") } |
|
||||||
func (s *spongeDb) NewBatch() ethdb.Batch { return &spongeBatch{s} } |
|
||||||
func (s *spongeDb) NewBatchWithSize(size int) ethdb.Batch { return &spongeBatch{s} } |
|
||||||
func (s *spongeDb) NewSnapshot() (ethdb.Snapshot, error) { panic("implement me") } |
|
||||||
func (s *spongeDb) Stat(property string) (string, error) { panic("implement me") } |
|
||||||
func (s *spongeDb) Compact(start []byte, limit []byte) error { panic("implement me") } |
|
||||||
func (s *spongeDb) Close() error { return nil } |
|
||||||
|
|
||||||
func (s *spongeDb) Put(key []byte, value []byte) error { |
|
||||||
if s.debug { |
|
||||||
fmt.Printf("db.Put %x : %x\n", key, value) |
|
||||||
} |
|
||||||
s.sponge.Write(key) |
|
||||||
s.sponge.Write(value) |
|
||||||
return nil |
|
||||||
} |
|
||||||
func (s *spongeDb) NewIterator(prefix []byte, start []byte) ethdb.Iterator { panic("implement me") } |
|
||||||
|
|
||||||
// spongeBatch is a dummy batch which immediately writes to the underlying spongedb
|
|
||||||
type spongeBatch struct { |
|
||||||
db *spongeDb |
|
||||||
} |
|
||||||
|
|
||||||
func (b *spongeBatch) Put(key, value []byte) error { |
|
||||||
b.db.Put(key, value) |
|
||||||
return nil |
|
||||||
} |
|
||||||
func (b *spongeBatch) Delete(key []byte) error { panic("implement me") } |
|
||||||
func (b *spongeBatch) ValueSize() int { return 100 } |
|
||||||
func (b *spongeBatch) Write() error { return nil } |
|
||||||
func (b *spongeBatch) Reset() {} |
|
||||||
func (b *spongeBatch) Replay(w ethdb.KeyValueWriter) error { return nil } |
|
||||||
|
|
||||||
type kv struct { |
|
||||||
k, v []byte |
|
||||||
} |
|
||||||
|
|
||||||
// Fuzz is the fuzzing entry-point.
|
|
||||||
// The function must return
|
|
||||||
//
|
|
||||||
// - 1 if the fuzzer should increase priority of the
|
|
||||||
// given input during subsequent fuzzing (for example, the input is lexically
|
|
||||||
// correct and was parsed successfully);
|
|
||||||
// - -1 if the input must not be added to corpus even if gives new coverage; and
|
|
||||||
// - 0 otherwise
|
|
||||||
//
|
|
||||||
// other values are reserved for future use.
|
|
||||||
func fuzz(data []byte) int { |
|
||||||
f := fuzzer{ |
|
||||||
input: bytes.NewReader(data), |
|
||||||
exhausted: false, |
|
||||||
} |
|
||||||
return f.fuzz() |
|
||||||
} |
|
||||||
|
|
||||||
func Debug(data []byte) int { |
|
||||||
f := fuzzer{ |
|
||||||
input: bytes.NewReader(data), |
|
||||||
exhausted: false, |
|
||||||
debugging: true, |
|
||||||
} |
|
||||||
return f.fuzz() |
|
||||||
} |
|
||||||
|
|
||||||
func (f *fuzzer) fuzz() int { |
|
||||||
// This spongeDb is used to check the sequence of disk-db-writes
|
|
||||||
var ( |
|
||||||
spongeA = &spongeDb{sponge: sha3.NewLegacyKeccak256()} |
|
||||||
dbA = trie.NewDatabase(rawdb.NewDatabase(spongeA), nil) |
|
||||||
trieA = trie.NewEmpty(dbA) |
|
||||||
spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()} |
|
||||||
dbB = trie.NewDatabase(rawdb.NewDatabase(spongeB), nil) |
|
||||||
|
|
||||||
options = trie.NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) { |
|
||||||
rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme()) |
|
||||||
}) |
|
||||||
trieB = trie.NewStackTrie(options) |
|
||||||
vals []kv |
|
||||||
useful bool |
|
||||||
maxElements = 10000 |
|
||||||
// operate on unique keys only
|
|
||||||
keys = make(map[string]struct{}) |
|
||||||
) |
|
||||||
// Fill the trie with elements
|
|
||||||
for i := 0; !f.exhausted && i < maxElements; i++ { |
|
||||||
k := f.read(32) |
|
||||||
v := f.readSlice(1, 500) |
|
||||||
if f.exhausted { |
|
||||||
// If it was exhausted while reading, the value may be all zeroes,
|
|
||||||
// thus 'deletion' which is not supported on stacktrie
|
|
||||||
break |
|
||||||
} |
|
||||||
if _, present := keys[string(k)]; present { |
|
||||||
// This key is a duplicate, ignore it
|
|
||||||
continue |
|
||||||
} |
|
||||||
keys[string(k)] = struct{}{} |
|
||||||
vals = append(vals, kv{k: k, v: v}) |
|
||||||
trieA.MustUpdate(k, v) |
|
||||||
useful = true |
|
||||||
} |
|
||||||
if !useful { |
|
||||||
return 0 |
|
||||||
} |
|
||||||
// Flush trie -> database
|
|
||||||
rootA, nodes, err := trieA.Commit(false) |
|
||||||
if err != nil { |
|
||||||
panic(err) |
|
||||||
} |
|
||||||
if nodes != nil { |
|
||||||
dbA.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) |
|
||||||
} |
|
||||||
// Flush memdb -> disk (sponge)
|
|
||||||
dbA.Commit(rootA, false) |
|
||||||
|
|
||||||
// Stacktrie requires sorted insertion
|
|
||||||
slices.SortFunc(vals, func(a, b kv) int { |
|
||||||
return bytes.Compare(a.k, b.k) |
|
||||||
}) |
|
||||||
for _, kv := range vals { |
|
||||||
if f.debugging { |
|
||||||
fmt.Printf("{\"%#x\" , \"%#x\"} // stacktrie.Update\n", kv.k, kv.v) |
|
||||||
} |
|
||||||
trieB.MustUpdate(kv.k, kv.v) |
|
||||||
} |
|
||||||
rootB := trieB.Hash() |
|
||||||
trieB.Commit() |
|
||||||
if rootA != rootB { |
|
||||||
panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootB)) |
|
||||||
} |
|
||||||
sumA := spongeA.sponge.Sum(nil) |
|
||||||
sumB := spongeB.sponge.Sum(nil) |
|
||||||
if !bytes.Equal(sumA, sumB) { |
|
||||||
panic(fmt.Sprintf("sequence differ: (trie) %x != %x (stacktrie)", sumA, sumB)) |
|
||||||
} |
|
||||||
|
|
||||||
// Ensure all the nodes are persisted correctly
|
|
||||||
var ( |
|
||||||
nodeset = make(map[string][]byte) // path -> blob
|
|
||||||
optionsC = trie.NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) { |
|
||||||
if crypto.Keccak256Hash(blob) != hash { |
|
||||||
panic("invalid node blob") |
|
||||||
} |
|
||||||
nodeset[string(path)] = common.CopyBytes(blob) |
|
||||||
}) |
|
||||||
trieC = trie.NewStackTrie(optionsC) |
|
||||||
checked int |
|
||||||
) |
|
||||||
for _, kv := range vals { |
|
||||||
trieC.MustUpdate(kv.k, kv.v) |
|
||||||
} |
|
||||||
rootC := trieC.Commit() |
|
||||||
if rootA != rootC { |
|
||||||
panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootC)) |
|
||||||
} |
|
||||||
trieA, _ = trie.New(trie.TrieID(rootA), dbA) |
|
||||||
iterA := trieA.MustNodeIterator(nil) |
|
||||||
for iterA.Next(true) { |
|
||||||
if iterA.Hash() == (common.Hash{}) { |
|
||||||
if _, present := nodeset[string(iterA.Path())]; present { |
|
||||||
panic("unexpected tiny node") |
|
||||||
} |
|
||||||
continue |
|
||||||
} |
|
||||||
nodeBlob, present := nodeset[string(iterA.Path())] |
|
||||||
if !present { |
|
||||||
panic("missing node") |
|
||||||
} |
|
||||||
if !bytes.Equal(nodeBlob, iterA.NodeBlob()) { |
|
||||||
panic("node blob is not matched") |
|
||||||
} |
|
||||||
checked += 1 |
|
||||||
} |
|
||||||
if checked != len(nodeset) { |
|
||||||
panic("node number is not matched") |
|
||||||
} |
|
||||||
return 1 |
|
||||||
} |
|
@ -1,25 +0,0 @@ |
|||||||
// Copyright 2023 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package stacktrie |
|
||||||
|
|
||||||
import "testing" |
|
||||||
|
|
||||||
func Fuzz(f *testing.F) { |
|
||||||
f.Fuzz(func(t *testing.T, data []byte) { |
|
||||||
fuzz(data) |
|
||||||
}) |
|
||||||
} |
|
@ -1,201 +0,0 @@ |
|||||||
// Copyright 2019 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package trie |
|
||||||
|
|
||||||
import ( |
|
||||||
"bytes" |
|
||||||
"encoding/binary" |
|
||||||
"errors" |
|
||||||
"fmt" |
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb" |
|
||||||
"github.com/ethereum/go-ethereum/core/types" |
|
||||||
"github.com/ethereum/go-ethereum/trie" |
|
||||||
"github.com/ethereum/go-ethereum/trie/trienode" |
|
||||||
) |
|
||||||
|
|
||||||
// randTest performs random trie operations.
|
|
||||||
// Instances of this test are created by Generate.
|
|
||||||
type randTest []randTestStep |
|
||||||
|
|
||||||
type randTestStep struct { |
|
||||||
op int |
|
||||||
key []byte // for opUpdate, opDelete, opGet
|
|
||||||
value []byte // for opUpdate
|
|
||||||
err error // for debugging
|
|
||||||
} |
|
||||||
|
|
||||||
type proofDb struct{} |
|
||||||
|
|
||||||
func (proofDb) Put(key []byte, value []byte) error { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
func (proofDb) Delete(key []byte) error { |
|
||||||
return nil |
|
||||||
} |
|
||||||
|
|
||||||
const ( |
|
||||||
opUpdate = iota |
|
||||||
opDelete |
|
||||||
opGet |
|
||||||
opHash |
|
||||||
opCommit |
|
||||||
opItercheckhash |
|
||||||
opProve |
|
||||||
opMax // boundary value, not an actual op
|
|
||||||
) |
|
||||||
|
|
||||||
type dataSource struct { |
|
||||||
input []byte |
|
||||||
reader *bytes.Reader |
|
||||||
} |
|
||||||
|
|
||||||
func newDataSource(input []byte) *dataSource { |
|
||||||
return &dataSource{ |
|
||||||
input, bytes.NewReader(input), |
|
||||||
} |
|
||||||
} |
|
||||||
func (ds *dataSource) readByte() byte { |
|
||||||
if b, err := ds.reader.ReadByte(); err != nil { |
|
||||||
return 0 |
|
||||||
} else { |
|
||||||
return b |
|
||||||
} |
|
||||||
} |
|
||||||
func (ds *dataSource) Read(buf []byte) (int, error) { |
|
||||||
return ds.reader.Read(buf) |
|
||||||
} |
|
||||||
func (ds *dataSource) Ended() bool { |
|
||||||
return ds.reader.Len() == 0 |
|
||||||
} |
|
||||||
|
|
||||||
func Generate(input []byte) randTest { |
|
||||||
var allKeys [][]byte |
|
||||||
r := newDataSource(input) |
|
||||||
genKey := func() []byte { |
|
||||||
if len(allKeys) < 2 || r.readByte() < 0x0f { |
|
||||||
// new key
|
|
||||||
key := make([]byte, r.readByte()%50) |
|
||||||
r.Read(key) |
|
||||||
allKeys = append(allKeys, key) |
|
||||||
return key |
|
||||||
} |
|
||||||
// use existing key
|
|
||||||
return allKeys[int(r.readByte())%len(allKeys)] |
|
||||||
} |
|
||||||
|
|
||||||
var steps randTest |
|
||||||
|
|
||||||
for i := 0; !r.Ended(); i++ { |
|
||||||
step := randTestStep{op: int(r.readByte()) % opMax} |
|
||||||
switch step.op { |
|
||||||
case opUpdate: |
|
||||||
step.key = genKey() |
|
||||||
step.value = make([]byte, 8) |
|
||||||
binary.BigEndian.PutUint64(step.value, uint64(i)) |
|
||||||
case opGet, opDelete, opProve: |
|
||||||
step.key = genKey() |
|
||||||
} |
|
||||||
steps = append(steps, step) |
|
||||||
if len(steps) > 500 { |
|
||||||
break |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
return steps |
|
||||||
} |
|
||||||
|
|
||||||
// Fuzz is the fuzzing entry-point.
|
|
||||||
// The function must return
|
|
||||||
//
|
|
||||||
// - 1 if the fuzzer should increase priority of the
|
|
||||||
// given input during subsequent fuzzing (for example, the input is lexically
|
|
||||||
// correct and was parsed successfully);
|
|
||||||
// - -1 if the input must not be added to corpus even if gives new coverage; and
|
|
||||||
// - 0 otherwise
|
|
||||||
//
|
|
||||||
// other values are reserved for future use.
|
|
||||||
func fuzz(input []byte) int { |
|
||||||
program := Generate(input) |
|
||||||
if len(program) == 0 { |
|
||||||
return 0 |
|
||||||
} |
|
||||||
if err := runRandTest(program); err != nil { |
|
||||||
panic(err) |
|
||||||
} |
|
||||||
return 1 |
|
||||||
} |
|
||||||
|
|
||||||
func runRandTest(rt randTest) error { |
|
||||||
var ( |
|
||||||
triedb = trie.NewDatabase(rawdb.NewMemoryDatabase(), nil) |
|
||||||
tr = trie.NewEmpty(triedb) |
|
||||||
origin = types.EmptyRootHash |
|
||||||
values = make(map[string]string) // tracks content of the trie
|
|
||||||
) |
|
||||||
for i, step := range rt { |
|
||||||
switch step.op { |
|
||||||
case opUpdate: |
|
||||||
tr.MustUpdate(step.key, step.value) |
|
||||||
values[string(step.key)] = string(step.value) |
|
||||||
case opDelete: |
|
||||||
tr.MustDelete(step.key) |
|
||||||
delete(values, string(step.key)) |
|
||||||
case opGet: |
|
||||||
v := tr.MustGet(step.key) |
|
||||||
want := values[string(step.key)] |
|
||||||
if string(v) != want { |
|
||||||
rt[i].err = fmt.Errorf("mismatch for key %#x, got %#x want %#x", step.key, v, want) |
|
||||||
} |
|
||||||
case opHash: |
|
||||||
tr.Hash() |
|
||||||
case opCommit: |
|
||||||
hash, nodes, err := tr.Commit(false) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
if nodes != nil { |
|
||||||
if err := triedb.Update(hash, origin, 0, trienode.NewWithNodeSet(nodes), nil); err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
} |
|
||||||
newtr, err := trie.New(trie.TrieID(hash), triedb) |
|
||||||
if err != nil { |
|
||||||
return err |
|
||||||
} |
|
||||||
tr = newtr |
|
||||||
origin = hash |
|
||||||
case opItercheckhash: |
|
||||||
checktr := trie.NewEmpty(triedb) |
|
||||||
it := trie.NewIterator(tr.MustNodeIterator(nil)) |
|
||||||
for it.Next() { |
|
||||||
checktr.MustUpdate(it.Key, it.Value) |
|
||||||
} |
|
||||||
if tr.Hash() != checktr.Hash() { |
|
||||||
return errors.New("hash mismatch in opItercheckhash") |
|
||||||
} |
|
||||||
case opProve: |
|
||||||
rt[i].err = tr.Prove(step.key, proofDb{}) |
|
||||||
} |
|
||||||
// Abort the test on error.
|
|
||||||
if rt[i].err != nil { |
|
||||||
return rt[i].err |
|
||||||
} |
|
||||||
} |
|
||||||
return nil |
|
||||||
} |
|
@ -1,25 +0,0 @@ |
|||||||
// Copyright 2023 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 <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
package trie |
|
||||||
|
|
||||||
import "testing" |
|
||||||
|
|
||||||
func Fuzz(f *testing.F) { |
|
||||||
f.Fuzz(func(t *testing.T, data []byte) { |
|
||||||
fuzz(data) |
|
||||||
}) |
|
||||||
} |
|
@ -0,0 +1,155 @@ |
|||||||
|
// Copyright 2020 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 <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package trie |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"encoding/binary" |
||||||
|
"fmt" |
||||||
|
"testing" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common" |
||||||
|
"github.com/ethereum/go-ethereum/core/rawdb" |
||||||
|
"github.com/ethereum/go-ethereum/core/types" |
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"github.com/ethereum/go-ethereum/trie/trienode" |
||||||
|
"golang.org/x/crypto/sha3" |
||||||
|
"golang.org/x/exp/slices" |
||||||
|
) |
||||||
|
|
||||||
|
func FuzzStackTrie(f *testing.F) { |
||||||
|
f.Fuzz(func(t *testing.T, data []byte) { |
||||||
|
fuzz(data, false) |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
func fuzz(data []byte, debugging bool) { |
||||||
|
// This spongeDb is used to check the sequence of disk-db-writes
|
||||||
|
var ( |
||||||
|
input = bytes.NewReader(data) |
||||||
|
spongeA = &spongeDb{sponge: sha3.NewLegacyKeccak256()} |
||||||
|
dbA = NewDatabase(rawdb.NewDatabase(spongeA), nil) |
||||||
|
trieA = NewEmpty(dbA) |
||||||
|
spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()} |
||||||
|
dbB = NewDatabase(rawdb.NewDatabase(spongeB), nil) |
||||||
|
|
||||||
|
options = NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) { |
||||||
|
rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme()) |
||||||
|
}) |
||||||
|
trieB = NewStackTrie(options) |
||||||
|
vals []*kv |
||||||
|
maxElements = 10000 |
||||||
|
// operate on unique keys only
|
||||||
|
keys = make(map[string]struct{}) |
||||||
|
) |
||||||
|
// Fill the trie with elements
|
||||||
|
for i := 0; input.Len() > 0 && i < maxElements; i++ { |
||||||
|
k := make([]byte, 32) |
||||||
|
input.Read(k) |
||||||
|
var a uint16 |
||||||
|
binary.Read(input, binary.LittleEndian, &a) |
||||||
|
a = 1 + a%100 |
||||||
|
v := make([]byte, a) |
||||||
|
input.Read(v) |
||||||
|
if input.Len() == 0 { |
||||||
|
// If it was exhausted while reading, the value may be all zeroes,
|
||||||
|
// thus 'deletion' which is not supported on stacktrie
|
||||||
|
break |
||||||
|
} |
||||||
|
if _, present := keys[string(k)]; present { |
||||||
|
// This key is a duplicate, ignore it
|
||||||
|
continue |
||||||
|
} |
||||||
|
keys[string(k)] = struct{}{} |
||||||
|
vals = append(vals, &kv{k: k, v: v}) |
||||||
|
trieA.MustUpdate(k, v) |
||||||
|
} |
||||||
|
if len(vals) == 0 { |
||||||
|
return |
||||||
|
} |
||||||
|
// Flush trie -> database
|
||||||
|
rootA, nodes, err := trieA.Commit(false) |
||||||
|
if err != nil { |
||||||
|
panic(err) |
||||||
|
} |
||||||
|
if nodes != nil { |
||||||
|
dbA.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil) |
||||||
|
} |
||||||
|
// Flush memdb -> disk (sponge)
|
||||||
|
dbA.Commit(rootA, false) |
||||||
|
|
||||||
|
// Stacktrie requires sorted insertion
|
||||||
|
slices.SortFunc(vals, (*kv).cmp) |
||||||
|
|
||||||
|
for _, kv := range vals { |
||||||
|
if debugging { |
||||||
|
fmt.Printf("{\"%#x\" , \"%#x\"} // stacktrie.Update\n", kv.k, kv.v) |
||||||
|
} |
||||||
|
trieB.MustUpdate(kv.k, kv.v) |
||||||
|
} |
||||||
|
rootB := trieB.Hash() |
||||||
|
trieB.Commit() |
||||||
|
if rootA != rootB { |
||||||
|
panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootB)) |
||||||
|
} |
||||||
|
sumA := spongeA.sponge.Sum(nil) |
||||||
|
sumB := spongeB.sponge.Sum(nil) |
||||||
|
if !bytes.Equal(sumA, sumB) { |
||||||
|
panic(fmt.Sprintf("sequence differ: (trie) %x != %x (stacktrie)", sumA, sumB)) |
||||||
|
} |
||||||
|
|
||||||
|
// Ensure all the nodes are persisted correctly
|
||||||
|
var ( |
||||||
|
nodeset = make(map[string][]byte) // path -> blob
|
||||||
|
optionsC = NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) { |
||||||
|
if crypto.Keccak256Hash(blob) != hash { |
||||||
|
panic("invalid node blob") |
||||||
|
} |
||||||
|
nodeset[string(path)] = common.CopyBytes(blob) |
||||||
|
}) |
||||||
|
trieC = NewStackTrie(optionsC) |
||||||
|
checked int |
||||||
|
) |
||||||
|
for _, kv := range vals { |
||||||
|
trieC.MustUpdate(kv.k, kv.v) |
||||||
|
} |
||||||
|
rootC := trieC.Commit() |
||||||
|
if rootA != rootC { |
||||||
|
panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootC)) |
||||||
|
} |
||||||
|
trieA, _ = New(TrieID(rootA), dbA) |
||||||
|
iterA := trieA.MustNodeIterator(nil) |
||||||
|
for iterA.Next(true) { |
||||||
|
if iterA.Hash() == (common.Hash{}) { |
||||||
|
if _, present := nodeset[string(iterA.Path())]; present { |
||||||
|
panic("unexpected tiny node") |
||||||
|
} |
||||||
|
continue |
||||||
|
} |
||||||
|
nodeBlob, present := nodeset[string(iterA.Path())] |
||||||
|
if !present { |
||||||
|
panic("missing node") |
||||||
|
} |
||||||
|
if !bytes.Equal(nodeBlob, iterA.NodeBlob()) { |
||||||
|
panic("node blob is not matched") |
||||||
|
} |
||||||
|
checked += 1 |
||||||
|
} |
||||||
|
if checked != len(nodeset) { |
||||||
|
panic("node number is not matched") |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue