diff --git a/tests/fuzzers/abi/abifuzzer_test.go b/accounts/abi/abifuzzer_test.go
similarity index 65%
rename from tests/fuzzers/abi/abifuzzer_test.go
rename to accounts/abi/abifuzzer_test.go
index a206beaf91..4b67947815 100644
--- a/tests/fuzzers/abi/abifuzzer_test.go
+++ b/accounts/abi/abifuzzer_test.go
@@ -22,33 +22,31 @@ import (
"strings"
"testing"
- "github.com/ethereum/go-ethereum/accounts/abi"
fuzz "github.com/google/gofuzz"
)
// TestReplicate can be used to replicate crashers from the fuzzing tests.
// Just replace testString with the data in .quoted
func TestReplicate(t *testing.T) {
- testString := "\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00"
- data := []byte(testString)
- fuzzAbi(data)
+ //t.Skip("Test only useful for reproducing issues")
+ fuzzAbi([]byte("\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00"))
+ //fuzzAbi([]byte("asdfasdfkadsf;lasdf;lasd;lfk"))
}
-func Fuzz(f *testing.F) {
+// FuzzABI is the main entrypoint for fuzzing
+func FuzzABI(f *testing.F) {
f.Fuzz(func(t *testing.T, data []byte) {
fuzzAbi(data)
})
}
var (
- names = []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"}
- stateMut = []string{"", "pure", "view", "payable"}
- stateMutabilites = []*string{&stateMut[0], &stateMut[1], &stateMut[2], &stateMut[3]}
- pays = []string{"", "true", "false"}
- payables = []*string{&pays[0], &pays[1]}
- vNames = []string{"a", "b", "c", "d", "e", "f", "g"}
- varNames = append(vNames, names...)
- varTypes = []string{"bool", "address", "bytes", "string",
+ names = []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"}
+ stateMut = []string{"pure", "view", "payable"}
+ pays = []string{"true", "false"}
+ vNames = []string{"a", "b", "c", "d", "e", "f", "g"}
+ varNames = append(vNames, names...)
+ varTypes = []string{"bool", "address", "bytes", "string",
"uint8", "int8", "uint8", "int8", "uint16", "int16",
"uint24", "int24", "uint32", "int32", "uint40", "int40", "uint48", "int48", "uint56", "int56",
"uint64", "int64", "uint72", "int72", "uint80", "int80", "uint88", "int88", "uint96", "int96",
@@ -62,7 +60,7 @@ var (
"bytes32", "bytes"}
)
-func unpackPack(abi abi.ABI, method string, input []byte) ([]interface{}, bool) {
+func unpackPack(abi ABI, method string, input []byte) ([]interface{}, bool) {
if out, err := abi.Unpack(method, input); err == nil {
_, err := abi.Pack(method, out...)
if err != nil {
@@ -78,7 +76,7 @@ func unpackPack(abi abi.ABI, method string, input []byte) ([]interface{}, bool)
return nil, false
}
-func packUnpack(abi abi.ABI, method string, input *[]interface{}) bool {
+func packUnpack(abi ABI, method string, input *[]interface{}) bool {
if packed, err := abi.Pack(method, input); err == nil {
outptr := reflect.New(reflect.TypeOf(input))
err := abi.UnpackIntoInterface(outptr.Interface(), method, packed)
@@ -94,12 +92,12 @@ func packUnpack(abi abi.ABI, method string, input *[]interface{}) bool {
return false
}
-type args struct {
+type arg struct {
name string
typ string
}
-func createABI(name string, stateMutability, payable *string, inputs []args) (abi.ABI, error) {
+func createABI(name string, stateMutability, payable *string, inputs []arg) (ABI, error) {
sig := fmt.Sprintf(`[{ "type" : "function", "name" : "%v" `, name)
if stateMutability != nil {
sig += fmt.Sprintf(`, "stateMutability": "%v" `, *stateMutability)
@@ -126,56 +124,55 @@ func createABI(name string, stateMutability, payable *string, inputs []args) (ab
sig += "} ]"
}
sig += `}]`
-
- return abi.JSON(strings.NewReader(sig))
+ //fmt.Printf("sig: %s\n", sig)
+ return JSON(strings.NewReader(sig))
}
-func fuzzAbi(input []byte) int {
- good := false
- fuzzer := fuzz.NewFromGoFuzz(input)
-
- name := names[getUInt(fuzzer)%len(names)]
- stateM := stateMutabilites[getUInt(fuzzer)%len(stateMutabilites)]
- payable := payables[getUInt(fuzzer)%len(payables)]
- maxLen := 5
- for k := 1; k < maxLen; k++ {
- var arg []args
- for i := k; i > 0; i-- {
- argName := varNames[i]
- argTyp := varTypes[getUInt(fuzzer)%len(varTypes)]
- if getUInt(fuzzer)%10 == 0 {
- argTyp += "[]"
- } else if getUInt(fuzzer)%10 == 0 {
- arrayArgs := getUInt(fuzzer)%30 + 1
- argTyp += fmt.Sprintf("[%d]", arrayArgs)
- }
- arg = append(arg, args{
- name: argName,
- typ: argTyp,
- })
+func fuzzAbi(input []byte) {
+ var (
+ fuzzer = fuzz.NewFromGoFuzz(input)
+ name = oneOf(fuzzer, names)
+ stateM = oneOfOrNil(fuzzer, stateMut)
+ payable = oneOfOrNil(fuzzer, pays)
+ arguments []arg
+ )
+ for i := 0; i < upTo(fuzzer, 10); i++ {
+ argName := oneOf(fuzzer, varNames)
+ argTyp := oneOf(fuzzer, varTypes)
+ switch upTo(fuzzer, 10) {
+ case 0: // 10% chance to make it a slice
+ argTyp += "[]"
+ case 1: // 10% chance to make it an array
+ argTyp += fmt.Sprintf("[%d]", 1+upTo(fuzzer, 30))
+ default:
}
- abi, err := createABI(name, stateM, payable, arg)
- if err != nil {
- continue
- }
- structs, b := unpackPack(abi, name, input)
- c := packUnpack(abi, name, &structs)
- good = good || b || c
+ arguments = append(arguments, arg{name: argName, typ: argTyp})
}
- if good {
- return 1
+ abi, err := createABI(name, stateM, payable, arguments)
+ if err != nil {
+ //fmt.Printf("err: %v\n", err)
+ panic(err)
}
- return 0
+ structs, _ := unpackPack(abi, name, input)
+ _ = packUnpack(abi, name, &structs)
}
-func getUInt(fuzzer *fuzz.Fuzzer) int {
+func upTo(fuzzer *fuzz.Fuzzer, max int) int {
var i int
fuzzer.Fuzz(&i)
if i < 0 {
- i = -i
- if i < 0 {
- return 0
- }
+ return (-1 - i) % max
}
- return i
+ return i % max
+}
+
+func oneOf(fuzzer *fuzz.Fuzzer, options []string) string {
+ return options[upTo(fuzzer, len(options))]
+}
+
+func oneOfOrNil(fuzzer *fuzz.Fuzzer, options []string) *string {
+ if i := upTo(fuzzer, len(options)+1); i < len(options) {
+ return &options[i]
+ }
+ return nil
}
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index a26ee12e0a..2faf274dbd 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -846,7 +846,7 @@ func (b *SimulatedBackend) AdjustTime(adjustment time.Duration) error {
defer b.mu.Unlock()
if len(b.pendingBlock.Transactions()) != 0 {
- return errors.New("Could not adjust time on non-empty block")
+ return errors.New("could not adjust time on non-empty block")
}
// Get the last block
block := b.blockchain.GetBlockByHash(b.pendingBlock.ParentHash())
diff --git a/accounts/abi/method.go b/accounts/abi/method.go
index b6e1eef3cf..c5a1a71f47 100644
--- a/accounts/abi/method.go
+++ b/accounts/abi/method.go
@@ -117,15 +117,6 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
sig = fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
id = crypto.Keccak256([]byte(sig))[:4]
}
- // Extract meaningful state mutability of solidity method.
- // If it's default value, never print it.
- state := mutability
- if state == "nonpayable" {
- state = ""
- }
- if state != "" {
- state = state + " "
- }
identity := fmt.Sprintf("function %v", rawName)
switch funType {
case Fallback:
@@ -135,7 +126,14 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
case Constructor:
identity = "constructor"
}
- str := fmt.Sprintf("%v(%v) %sreturns(%v)", identity, strings.Join(inputNames, ", "), state, strings.Join(outputNames, ", "))
+ var str string
+ // Extract meaningful state mutability of solidity method.
+ // If it's empty string or default value "nonpayable", never print it.
+ if mutability == "" || mutability == "nonpayable" {
+ str = fmt.Sprintf("%v(%v) returns(%v)", identity, strings.Join(inputNames, ", "), strings.Join(outputNames, ", "))
+ } else {
+ str = fmt.Sprintf("%v(%v) %s returns(%v)", identity, strings.Join(inputNames, ", "), mutability, strings.Join(outputNames, ", "))
+ }
return Method{
Name: name,
diff --git a/accounts/abi/pack.go b/accounts/abi/pack.go
index 0cd91cb4fa..beef1fa37f 100644
--- a/accounts/abi/pack.go
+++ b/accounts/abi/pack.go
@@ -57,7 +57,7 @@ func packElement(t Type, reflectValue reflect.Value) ([]byte, error) {
reflectValue = mustArrayToByteSlice(reflectValue)
}
if reflectValue.Type() != reflect.TypeOf([]byte{}) {
- return []byte{}, errors.New("Bytes type is neither slice nor array")
+ return []byte{}, errors.New("bytes type is neither slice nor array")
}
return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()), nil
case FixedBytesTy, FunctionTy:
@@ -66,7 +66,7 @@ func packElement(t Type, reflectValue reflect.Value) ([]byte, error) {
}
return common.RightPadBytes(reflectValue.Bytes(), 32), nil
default:
- return []byte{}, fmt.Errorf("Could not pack element, unknown type: %v", t.T)
+ return []byte{}, fmt.Errorf("could not pack element, unknown type: %v", t.T)
}
}
diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go
index 48d2ef41ec..1863e5bb7d 100644
--- a/accounts/abi/reflect.go
+++ b/accounts/abi/reflect.go
@@ -134,7 +134,7 @@ func setSlice(dst, src reflect.Value) error {
dst.Set(slice)
return nil
}
- return errors.New("Cannot set slice, destination not settable")
+ return errors.New("cannot set slice, destination not settable")
}
func setArray(dst, src reflect.Value) error {
@@ -155,7 +155,7 @@ func setArray(dst, src reflect.Value) error {
dst.Set(array)
return nil
}
- return errors.New("Cannot set array, destination not settable")
+ return errors.New("cannot set array, destination not settable")
}
func setStruct(dst, src reflect.Value) error {
@@ -163,7 +163,7 @@ func setStruct(dst, src reflect.Value) error {
srcField := src.Field(i)
dstField := dst.Field(i)
if !dstField.IsValid() || !srcField.IsValid() {
- return fmt.Errorf("Could not find src field: %v value: %v in destination", srcField.Type().Name(), srcField)
+ return fmt.Errorf("could not find src field: %v value: %v in destination", srcField.Type().Name(), srcField)
}
if err := set(dstField, srcField); err != nil {
return err
diff --git a/tests/fuzzers/keystore/keystore_test.go b/accounts/keystore/keystore_fuzzing_test.go
similarity index 72%
rename from tests/fuzzers/keystore/keystore_test.go
rename to accounts/keystore/keystore_fuzzing_test.go
index 167ff6c471..793b46336a 100644
--- a/tests/fuzzers/keystore/keystore_test.go
+++ b/accounts/keystore/keystore_fuzzing_test.go
@@ -16,10 +16,19 @@
package keystore
-import "testing"
+import (
+ "testing"
+)
-func Fuzz(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- fuzz(data)
+func FuzzPassword(f *testing.F) {
+ f.Fuzz(func(t *testing.T, password string) {
+ ks := NewKeyStore(t.TempDir(), LightScryptN, LightScryptP)
+ a, err := ks.NewAccount(password)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := ks.Unlock(a, password); err != nil {
+ t.Fatal(err)
+ }
})
}
diff --git a/accounts/scwallet/wallet.go b/accounts/scwallet/wallet.go
index 067bda83f1..f0ca9085b6 100644
--- a/accounts/scwallet/wallet.go
+++ b/accounts/scwallet/wallet.go
@@ -776,16 +776,16 @@ func (w *Wallet) findAccountPath(account accounts.Account) (accounts.DerivationP
return nil, fmt.Errorf("scheme %s does not match wallet scheme %s", account.URL.Scheme, w.Hub.scheme)
}
- parts := strings.SplitN(account.URL.Path, "/", 2)
- if len(parts) != 2 {
+ url, path, found := strings.Cut(account.URL.Path, "/")
+ if !found {
return nil, fmt.Errorf("invalid URL format: %s", account.URL)
}
- if parts[0] != fmt.Sprintf("%x", w.PublicKey[1:3]) {
+ if url != fmt.Sprintf("%x", w.PublicKey[1:3]) {
return nil, fmt.Errorf("URL %s is not for this wallet", account.URL)
}
- return accounts.ParseDerivationPath(parts[1])
+ return accounts.ParseDerivationPath(path)
}
// Session represents a secured communication session with the wallet.
diff --git a/cmd/clef/main.go b/cmd/clef/main.go
index 06a8cd7ab7..63f34effb7 100644
--- a/cmd/clef/main.go
+++ b/cmd/clef/main.go
@@ -581,6 +581,7 @@ func accountImport(c *cli.Context) error {
return err
}
if first != second {
+ //lint:ignore ST1005 This is a message for the user
return errors.New("Passwords do not match")
}
acc, err := internalApi.ImportRawKey(hex.EncodeToString(crypto.FromECDSA(pKey)), first)
diff --git a/cmd/devp2p/discv4cmd.go b/cmd/devp2p/discv4cmd.go
index 37b139dea2..45bcdcd367 100644
--- a/cmd/devp2p/discv4cmd.go
+++ b/cmd/devp2p/discv4cmd.go
@@ -236,7 +236,7 @@ func discv4Crawl(ctx *cli.Context) error {
func discv4Test(ctx *cli.Context) error {
// Configure test package globals.
if !ctx.IsSet(remoteEnodeFlag.Name) {
- return fmt.Errorf("Missing -%v", remoteEnodeFlag.Name)
+ return fmt.Errorf("missing -%v", remoteEnodeFlag.Name)
}
v4test.Remote = ctx.String(remoteEnodeFlag.Name)
v4test.Listen1 = ctx.String(testListen1Flag.Name)
diff --git a/cmd/devp2p/internal/ethtest/snap.go b/cmd/devp2p/internal/ethtest/snap.go
index 54eb63f3de..f50159a0de 100644
--- a/cmd/devp2p/internal/ethtest/snap.go
+++ b/cmd/devp2p/internal/ethtest/snap.go
@@ -683,7 +683,7 @@ func (s *Suite) snapGetTrieNodes(t *utesting.T, tc *trieNodesTest) error {
hash := make([]byte, 32)
trienodes := res.Nodes
if got, want := len(trienodes), len(tc.expHashes); got != want {
- return fmt.Errorf("wrong trienode count, got %d, want %d\n", got, want)
+ return fmt.Errorf("wrong trienode count, got %d, want %d", got, want)
}
for i, trienode := range trienodes {
hasher.Reset()
diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go
index e4d6ad6977..8f4127216e 100644
--- a/cmd/faucet/faucet.go
+++ b/cmd/faucet/faucet.go
@@ -248,7 +248,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*enode.Node, network ui
lesBackend, err := les.New(stack, &cfg)
if err != nil {
- return nil, fmt.Errorf("Failed to register the Ethereum service: %w", err)
+ return nil, fmt.Errorf("failed to register the Ethereum service: %w", err)
}
// Assemble the ethstats monitoring and reporting service'
diff --git a/cmd/geth/chaincmd.go b/cmd/geth/chaincmd.go
index 5663963e3c..b65827f5bc 100644
--- a/cmd/geth/chaincmd.go
+++ b/cmd/geth/chaincmd.go
@@ -211,7 +211,7 @@ func initGenesis(ctx *cli.Context) error {
}
defer chaindb.Close()
- triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false)
+ triedb := utils.MakeTrieDatabase(ctx, chaindb, ctx.Bool(utils.CachePreimagesFlag.Name), false, genesis.IsVerkle())
defer triedb.Close()
_, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
@@ -485,7 +485,7 @@ func dump(ctx *cli.Context) error {
if err != nil {
return err
}
- triedb := utils.MakeTrieDatabase(ctx, db, true, true) // always enable preimage lookup
+ triedb := utils.MakeTrieDatabase(ctx, db, true, true, false) // always enable preimage lookup
defer triedb.Close()
state, err := state.New(root, state.NewDatabaseWithNodeDB(db, triedb), nil)
diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go
index ab2626c120..c60147b862 100644
--- a/cmd/geth/dbcmd.go
+++ b/cmd/geth/dbcmd.go
@@ -482,7 +482,7 @@ func dbDumpTrie(ctx *cli.Context) error {
db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close()
- triedb := utils.MakeTrieDatabase(ctx, db, false, true)
+ triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
defer triedb.Close()
var (
diff --git a/cmd/geth/logtestcmd_active.go b/cmd/geth/logtestcmd_active.go
index ebcc8de976..0632f9ca4b 100644
--- a/cmd/geth/logtestcmd_active.go
+++ b/cmd/geth/logtestcmd_active.go
@@ -19,6 +19,7 @@
package main
import (
+ "errors"
"fmt"
"math"
"math/big"
@@ -39,6 +40,12 @@ var logTestCommand = &cli.Command{
This command is only meant for testing.
`}
+type customQuotedStringer struct {
+}
+func (c customQuotedStringer) String() string {
+ return "output with 'quotes'"
+}
+
// logTest is an entry point which spits out some logs. This is used by testing
// to verify expected outputs
func logTest(ctx *cli.Context) error {
@@ -70,6 +77,8 @@ func logTest(ctx *cli.Context) error {
log.Info("uint64", "18,446,744,073,709,551,615", uint64(math.MaxUint64))
}
{ // Special characters
+
+
log.Info("Special chars in value", "key", "special \r\n\t chars")
log.Info("Special chars in key", "special \n\t chars", "value")
@@ -83,9 +92,13 @@ func logTest(ctx *cli.Context) error {
colored := fmt.Sprintf("\u001B[%dmColored\u001B[0m[", 35)
log.Info(colored, colored, colored)
+ err := errors.New("this is an 'error'")
+ log.Info("an error message with quotes", "error", err)
}
{ // Custom Stringer() - type
log.Info("Custom Stringer value", "2562047h47m16.854s", common.PrettyDuration(time.Duration(9223372036854775807)))
+ var c customQuotedStringer
+ log.Info("a custom stringer that emits quoted text", "output", c)
}
{ // Lazy eval
log.Info("Lazy evaluation of value", "key", log.Lazy{Fn: func() interface{} { return "lazy value" }})
diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go
index 25c6311c4c..82beb4f2e4 100644
--- a/cmd/geth/snapshot.go
+++ b/cmd/geth/snapshot.go
@@ -205,7 +205,7 @@ func verifyState(ctx *cli.Context) error {
log.Error("Failed to load head block")
return errors.New("no head block")
}
- triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
+ triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
defer triedb.Close()
snapConfig := snapshot.Config{
@@ -260,7 +260,7 @@ func traverseState(ctx *cli.Context) error {
chaindb := utils.MakeChainDatabase(ctx, stack, true)
defer chaindb.Close()
- triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
+ triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
defer triedb.Close()
headBlock := rawdb.ReadHeadBlock(chaindb)
@@ -369,7 +369,7 @@ func traverseRawState(ctx *cli.Context) error {
chaindb := utils.MakeChainDatabase(ctx, stack, true)
defer chaindb.Close()
- triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
+ triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
defer triedb.Close()
headBlock := rawdb.ReadHeadBlock(chaindb)
@@ -533,7 +533,7 @@ func dumpState(ctx *cli.Context) error {
if err != nil {
return err
}
- triedb := utils.MakeTrieDatabase(ctx, db, false, true)
+ triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
defer triedb.Close()
snapConfig := snapshot.Config{
diff --git a/cmd/geth/testdata/logging/logtest-logfmt.txt b/cmd/geth/testdata/logging/logtest-logfmt.txt
index c1e34d1930..f097143a55 100644
--- a/cmd/geth/testdata/logging/logtest-logfmt.txt
+++ b/cmd/geth/testdata/logging/logtest-logfmt.txt
@@ -18,8 +18,10 @@ t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Bash escapes in value" key="\x1b[1G\x1
t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Bash escapes in key" "\x1b[1G\x1b[K\x1b[1A"=value
t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Bash escapes in message \x1b[1G\x1b[K\x1b[1A end" key=value
t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="\x1b[35mColored\x1b[0m[" "\x1b[35mColored\x1b[0m["="\x1b[35mColored\x1b[0m["
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Custom Stringer value" 2562047h47m16.854s=2562047h47m16.854s
-t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Lazy evaluation of value" key="lazy value"
+t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="an error message with quotes" error="this is an 'error'"
+t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Custom Stringer value" 2562047h47m16.854s=2562047h47m16.854s
+t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="a custom stringer that emits quoted text" output="output with 'quotes'"
+t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Lazy evaluation of value" key="lazy value"
t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="A message with wonky 💩 characters"
t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="A multiline message \nINFO [10-18|14:11:31.106] with wonky characters 💩"
t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="A multiline message \nLALA [ZZZZZZZZZZZZZZZZZZ] Actually part of message above"
diff --git a/cmd/geth/testdata/logging/logtest-terminal.txt b/cmd/geth/testdata/logging/logtest-terminal.txt
index af0de7b9ab..051a6267fa 100644
--- a/cmd/geth/testdata/logging/logtest-terminal.txt
+++ b/cmd/geth/testdata/logging/logtest-terminal.txt
@@ -18,22 +18,24 @@ INFO [XX-XX|XX:XX:XX.XXX] Bash escapes in value key="\x1b[1G\
INFO [XX-XX|XX:XX:XX.XXX] Bash escapes in key "\x1b[1G\x1b[K\x1b[1A"=value
INFO [XX-XX|XX:XX:XX.XXX] "Bash escapes in message \x1b[1G\x1b[K\x1b[1A end" key=value
INFO [XX-XX|XX:XX:XX.XXX] "\x1b[35mColored\x1b[0m[" "\x1b[35mColored\x1b[0m["="\x1b[35mColored\x1b[0m["
+INFO [XX-XX|XX:XX:XX.XXX] an error message with quotes error="this is an 'error'"
INFO [XX-XX|XX:XX:XX.XXX] Custom Stringer value 2562047h47m16.854s=2562047h47m16.854s
+INFO [XX-XX|XX:XX:XX.XXX] a custom stringer that emits quoted text output="output with 'quotes'"
INFO [XX-XX|XX:XX:XX.XXX] Lazy evaluation of value key="lazy value"
-INFO [XX-XX|XX:XX:XX.XXX] "A message with wonky 💩 characters"
-INFO [XX-XX|XX:XX:XX.XXX] "A multiline message \nINFO [10-18|14:11:31.106] with wonky characters 💩"
-INFO [XX-XX|XX:XX:XX.XXX] A multiline message
-LALA [XXZXXZXXZXXZXXZXXX] Actually part of message above
+INFO [XX-XX|XX:XX:XX.XXX] "A message with wonky 💩 characters"
+INFO [XX-XX|XX:XX:XX.XXX] "A multiline message \nINFO [10-18|14:11:31.106] with wonky characters 💩"
+INFO [XX-XX|XX:XX:XX.XXX] A multiline message
+LALA [ZZZZZZZZZZZZZZZZZZ] Actually part of message above
INFO [XX-XX|XX:XX:XX.XXX] boolean true=true false=false
INFO [XX-XX|XX:XX:XX.XXX] repeated-key 1 foo=alpha foo=beta
INFO [XX-XX|XX:XX:XX.XXX] repeated-key 2 xx=short xx=longer
-INFO [XX-XX|XX:XX:XX.XXX] log at level info
-WARN [XX-XX|XX:XX:XX.XXX] log at level warn
-ERROR[XX-XX|XX:XX:XX.XXX] log at level error
+INFO [XX-XX|XX:XX:XX.XXX] log at level info
+WARN [XX-XX|XX:XX:XX.XXX] log at level warn
+ERROR[XX-XX|XX:XX:XX.XXX] log at level error
INFO [XX-XX|XX:XX:XX.XXX] test bar=short a="aligned left"
INFO [XX-XX|XX:XX:XX.XXX] test bar="a long message" a=1
INFO [XX-XX|XX:XX:XX.XXX] test bar=short a="aligned right"
-INFO [XX-XX|XX:XX:XX.XXX] The following logs should align so that the key-fields make 5 columns
+INFO [XX-XX|XX:XX:XX.XXX] The following logs should align so that the key-fields make 5 columns
INFO [XX-XX|XX:XX:XX.XXX] Inserted known block number=1012 hash=000000..001234 txs=200 gas=1,123,123 other=first
INFO [XX-XX|XX:XX:XX.XXX] Inserted new block number=1 hash=000000..001235 txs=2 gas=1123 other=second
INFO [XX-XX|XX:XX:XX.XXX] Inserted known block number=99 hash=000000..012322 txs=10 gas=1 other=third
diff --git a/cmd/geth/verkle.go b/cmd/geth/verkle.go
index aa79889e8c..420b063d8b 100644
--- a/cmd/geth/verkle.go
+++ b/cmd/geth/verkle.go
@@ -84,7 +84,7 @@ func checkChildren(root verkle.VerkleNode, resolver verkle.NodeResolverFn) error
return fmt.Errorf("could not find child %x in db: %w", childC, err)
}
// depth is set to 0, the tree isn't rebuilt so it's not a problem
- childN, err := verkle.ParseNode(childS, 0, childC[:])
+ childN, err := verkle.ParseNode(childS, 0)
if err != nil {
return fmt.Errorf("decode error child %x in db: %w", child.Commitment().Bytes(), err)
}
@@ -145,7 +145,7 @@ func verifyVerkle(ctx *cli.Context) error {
if err != nil {
return err
}
- root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
+ root, err := verkle.ParseNode(serializedRoot, 0)
if err != nil {
return err
}
@@ -195,7 +195,7 @@ func expandVerkle(ctx *cli.Context) error {
if err != nil {
return err
}
- root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
+ root, err := verkle.ParseNode(serializedRoot, 0)
if err != nil {
return err
}
diff --git a/cmd/p2psim/main.go b/cmd/p2psim/main.go
index a3546d405b..a0f5f0d288 100644
--- a/cmd/p2psim/main.go
+++ b/cmd/p2psim/main.go
@@ -417,9 +417,7 @@ func rpcNode(ctx *cli.Context) error {
}
func rpcSubscribe(client *rpc.Client, out io.Writer, method string, args ...string) error {
- parts := strings.SplitN(method, "_", 2)
- namespace := parts[0]
- method = parts[1]
+ namespace, method, _ := strings.Cut(method, "_")
ch := make(chan interface{})
subArgs := make([]interface{}, len(args)+1)
subArgs[0] = method
diff --git a/cmd/utils/cmd.go b/cmd/utils/cmd.go
index 16b1260572..a7563a081e 100644
--- a/cmd/utils/cmd.go
+++ b/cmd/utils/cmd.go
@@ -460,7 +460,7 @@ func ImportLDBData(db ethdb.Database, f string, startIndex int64, interrupt chan
case OpBatchAdd:
batch.Put(key, val)
default:
- return fmt.Errorf("unknown op %d\n", op)
+ return fmt.Errorf("unknown op %d", op)
}
if batch.ValueSize() > ethdb.IdealBatchSize {
if err := batch.Write(); err != nil {
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index e9a7c7c110..8bbacac51d 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -2212,9 +2212,10 @@ func MakeConsolePreloads(ctx *cli.Context) []string {
}
// MakeTrieDatabase constructs a trie database based on the configured scheme.
-func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool) *trie.Database {
+func MakeTrieDatabase(ctx *cli.Context, disk ethdb.Database, preimage bool, readOnly bool, isVerkle bool) *trie.Database {
config := &trie.Config{
Preimages: preimage,
+ IsVerkle: isVerkle,
}
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), disk)
if err != nil {
diff --git a/common/bitutil/compress_test.go b/common/bitutil/compress_test.go
index 13a13011dc..c6f6fe8bcf 100644
--- a/common/bitutil/compress_test.go
+++ b/common/bitutil/compress_test.go
@@ -18,6 +18,7 @@ package bitutil
import (
"bytes"
+ "fmt"
"math/rand"
"testing"
@@ -48,19 +49,23 @@ func TestEncodingCycle(t *testing.T) {
"0xdf7070533534333636313639343638373532313536346c1bc333393438373130707063363430353639343638373532313536346c1bc333393438336336346c65fe",
}
for i, tt := range tests {
- data := hexutil.MustDecode(tt)
-
- proc, err := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
- if err != nil {
- t.Errorf("test %d: failed to decompress compressed data: %v", i, err)
- continue
- }
- if !bytes.Equal(data, proc) {
- t.Errorf("test %d: compress/decompress mismatch: have %x, want %x", i, proc, data)
+ if err := testEncodingCycle(hexutil.MustDecode(tt)); err != nil {
+ t.Errorf("test %d: %v", i, err)
}
}
}
+func testEncodingCycle(data []byte) error {
+ proc, err := bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
+ if err != nil {
+ return fmt.Errorf("failed to decompress compressed data: %v", err)
+ }
+ if !bytes.Equal(data, proc) {
+ return fmt.Errorf("compress/decompress mismatch: have %x, want %x", proc, data)
+ }
+ return nil
+}
+
// Tests that data bitset decoding and rencoding works and is bijective.
func TestDecodingCycle(t *testing.T) {
tests := []struct {
@@ -179,3 +184,40 @@ func benchmarkEncoding(b *testing.B, bytes int, fill float64) {
bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
}
}
+
+func FuzzEncoder(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ if err := testEncodingCycle(data); err != nil {
+ t.Fatal(err)
+ }
+ })
+}
+func FuzzDecoder(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ fuzzDecode(data)
+ })
+}
+
+// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and
+// reencoding algorithm.
+func fuzzDecode(data []byte) {
+ blob, err := 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 := 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 := DecompressBytes(data, 1024)
+ if err != nil {
+ panic(err)
+ }
+ if !bytes.Equal(decomp, blob) {
+ panic("content mismatch")
+ }
+}
diff --git a/console/bridge.go b/console/bridge.go
index c67686d6c3..37578041ca 100644
--- a/console/bridge.go
+++ b/console/bridge.go
@@ -78,7 +78,7 @@ func (b *bridge) NewAccount(call jsre.Call) (goja.Value, error) {
return nil, err
}
if password != confirm {
- return nil, errors.New("passwords don't match!")
+ return nil, errors.New("passwords don't match")
}
// A single string password was specified, use that
case len(call.Arguments) == 1 && call.Argument(0).ToString() != nil:
diff --git a/core/asm/asm_test.go b/core/asm/asm_test.go
index 92b26b67a5..cd7520ec63 100644
--- a/core/asm/asm_test.go
+++ b/core/asm/asm_test.go
@@ -22,53 +22,37 @@ import (
"encoding/hex"
)
-// Tests disassembling the instructions for valid evm code
-func TestInstructionIteratorValid(t *testing.T) {
- cnt := 0
- script, _ := hex.DecodeString("61000000")
+// Tests disassembling instructions
+func TestInstructionIterator(t *testing.T) {
+ for i, tc := range []struct {
+ want int
+ code string
+ wantErr string
+ }{
+ {2, "61000000", ""}, // valid code
+ {0, "6100", "incomplete push instruction at 0"}, // invalid code
+ {2, "5900", ""}, // push0
+ {0, "", ""}, // empty
- it := NewInstructionIterator(script)
- for it.Next() {
- cnt++
- }
-
- if err := it.Error(); err != nil {
- t.Errorf("Expected 2, but encountered error %v instead.", err)
- }
- if cnt != 2 {
- t.Errorf("Expected 2, but got %v instead.", cnt)
- }
-}
-
-// Tests disassembling the instructions for invalid evm code
-func TestInstructionIteratorInvalid(t *testing.T) {
- cnt := 0
- script, _ := hex.DecodeString("6100")
-
- it := NewInstructionIterator(script)
- for it.Next() {
- cnt++
- }
-
- if it.Error() == nil {
- t.Errorf("Expected an error, but got %v instead.", cnt)
- }
-}
-
-// Tests disassembling the instructions for empty evm code
-func TestInstructionIteratorEmpty(t *testing.T) {
- cnt := 0
- script, _ := hex.DecodeString("")
-
- it := NewInstructionIterator(script)
- for it.Next() {
- cnt++
- }
-
- if err := it.Error(); err != nil {
- t.Errorf("Expected 0, but encountered error %v instead.", err)
- }
- if cnt != 0 {
- t.Errorf("Expected 0, but got %v instead.", cnt)
+ } {
+ var (
+ have int
+ code, _ = hex.DecodeString(tc.code)
+ it = NewInstructionIterator(code)
+ )
+ for it.Next() {
+ have++
+ }
+ var haveErr = ""
+ if it.Error() != nil {
+ haveErr = it.Error().Error()
+ }
+ if haveErr != tc.wantErr {
+ t.Errorf("test %d: encountered error: %q want %q", i, haveErr, tc.wantErr)
+ continue
+ }
+ if have != tc.want {
+ t.Errorf("wrong instruction count, have %d want %d", have, tc.want)
+ }
}
}
diff --git a/core/genesis.go b/core/genesis.go
index 1045815fab..60c2f9a8bc 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -37,6 +37,7 @@ import (
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
+ "github.com/ethereum/go-ethereum/trie/triedb/pathdb"
)
//go:generate go run github.com/fjl/gencodec -type Genesis -field-override genesisSpecMarshaling -out gen_genesis.go
@@ -121,10 +122,20 @@ func (ga *GenesisAlloc) UnmarshalJSON(data []byte) error {
}
// hash computes the state root according to the genesis specification.
-func (ga *GenesisAlloc) hash() (common.Hash, error) {
+func (ga *GenesisAlloc) hash(isVerkle bool) (common.Hash, error) {
+ // If a genesis-time verkle trie is requested, create a trie config
+ // with the verkle trie enabled so that the tree can be initialized
+ // as such.
+ var config *trie.Config
+ if isVerkle {
+ config = &trie.Config{
+ PathDB: pathdb.Defaults,
+ IsVerkle: true,
+ }
+ }
// Create an ephemeral in-memory database for computing hash,
// all the derived states will be discarded to not pollute disk.
- db := state.NewDatabase(rawdb.NewMemoryDatabase())
+ db := state.NewDatabaseWithConfig(rawdb.NewMemoryDatabase(), config)
statedb, err := state.New(types.EmptyRootHash, db, nil)
if err != nil {
return common.Hash{}, err
@@ -410,9 +421,15 @@ func (g *Genesis) configOrDefault(ghash common.Hash) *params.ChainConfig {
}
}
+// IsVerkle indicates whether the state is already stored in a verkle
+// tree at genesis time.
+func (g *Genesis) IsVerkle() bool {
+ return g.Config.IsVerkle(new(big.Int).SetUint64(g.Number), g.Timestamp)
+}
+
// ToBlock returns the genesis block according to genesis specification.
func (g *Genesis) ToBlock() *types.Block {
- root, err := g.Alloc.hash()
+ root, err := g.Alloc.hash(g.IsVerkle())
if err != nil {
panic(err)
}
diff --git a/core/genesis_test.go b/core/genesis_test.go
index fac88ff373..1d85b510ca 100644
--- a/core/genesis_test.go
+++ b/core/genesis_test.go
@@ -17,6 +17,7 @@
package core
import (
+ "bytes"
"encoding/json"
"math/big"
"reflect"
@@ -231,7 +232,7 @@ func TestReadWriteGenesisAlloc(t *testing.T) {
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
{2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
}
- hash, _ = alloc.hash()
+ hash, _ = alloc.hash(false)
)
blob, _ := json.Marshal(alloc)
rawdb.WriteGenesisStateSpec(db, hash, blob)
@@ -261,3 +262,66 @@ func newDbConfig(scheme string) *trie.Config {
}
return &trie.Config{PathDB: pathdb.Defaults}
}
+
+func TestVerkleGenesisCommit(t *testing.T) {
+ var verkleTime uint64 = 0
+ verkleConfig := ¶ms.ChainConfig{
+ ChainID: big.NewInt(1),
+ HomesteadBlock: big.NewInt(0),
+ DAOForkBlock: nil,
+ DAOForkSupport: false,
+ EIP150Block: big.NewInt(0),
+ EIP155Block: big.NewInt(0),
+ EIP158Block: big.NewInt(0),
+ ByzantiumBlock: big.NewInt(0),
+ ConstantinopleBlock: big.NewInt(0),
+ PetersburgBlock: big.NewInt(0),
+ IstanbulBlock: big.NewInt(0),
+ MuirGlacierBlock: big.NewInt(0),
+ BerlinBlock: big.NewInt(0),
+ LondonBlock: big.NewInt(0),
+ ArrowGlacierBlock: big.NewInt(0),
+ GrayGlacierBlock: big.NewInt(0),
+ MergeNetsplitBlock: nil,
+ ShanghaiTime: &verkleTime,
+ CancunTime: &verkleTime,
+ PragueTime: &verkleTime,
+ VerkleTime: &verkleTime,
+ TerminalTotalDifficulty: big.NewInt(0),
+ TerminalTotalDifficultyPassed: true,
+ Ethash: nil,
+ Clique: nil,
+ }
+
+ genesis := &Genesis{
+ BaseFee: big.NewInt(params.InitialBaseFee),
+ Config: verkleConfig,
+ Timestamp: verkleTime,
+ Difficulty: big.NewInt(0),
+ Alloc: GenesisAlloc{
+ {1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
+ },
+ }
+
+ expected := common.Hex2Bytes("14398d42be3394ff8d50681816a4b7bf8d8283306f577faba2d5bc57498de23b")
+ got := genesis.ToBlock().Root().Bytes()
+ if !bytes.Equal(got, expected) {
+ t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
+ }
+
+ db := rawdb.NewMemoryDatabase()
+ triedb := trie.NewDatabase(db, &trie.Config{IsVerkle: true, PathDB: pathdb.Defaults})
+ block := genesis.MustCommit(db, triedb)
+ if !bytes.Equal(block.Root().Bytes(), expected) {
+ t.Fatalf("invalid genesis state root, expected %x, got %x", expected, got)
+ }
+
+ // Test that the trie is verkle
+ if !triedb.IsVerkle() {
+ t.Fatalf("expected trie to be verkle")
+ }
+
+ if !rawdb.ExistsAccountTrieNode(db, nil) {
+ t.Fatal("could not find node")
+ }
+}
diff --git a/core/state/database.go b/core/state/database.go
index 9467c8f72e..b55f870d90 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -20,6 +20,7 @@ import (
"errors"
"fmt"
+ "github.com/crate-crypto/go-ipa/banderwagon"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/lru"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -28,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie"
"github.com/ethereum/go-ethereum/trie/trienode"
+ "github.com/ethereum/go-ethereum/trie/utils"
)
const (
@@ -36,6 +38,12 @@ const (
// Cache size granted for caching clean code.
codeCacheSize = 64 * 1024 * 1024
+
+ // commitmentSize is the size of commitment stored in cache.
+ commitmentSize = banderwagon.UncompressedSize
+
+ // Cache item granted for caching commitment results.
+ commitmentCacheItems = 64 * 1024 * 1024 / (commitmentSize + common.AddressLength)
)
// Database wraps access to tries and contract code.
@@ -44,7 +52,7 @@ type Database interface {
OpenTrie(root common.Hash) (Trie, error)
// OpenStorageTrie opens the storage trie of an account.
- OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (Trie, error)
+ OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, trie Trie) (Trie, error)
// CopyTrie returns an independent copy of the given trie.
CopyTrie(Trie) Trie
@@ -70,11 +78,6 @@ type Trie interface {
// TODO(fjl): remove this when StateTrie is removed
GetKey([]byte) []byte
- // GetStorage returns the value for key stored in the trie. The value bytes
- // must not be modified by the caller. If a node was not found in the database,
- // a trie.MissingNodeError is returned.
- GetStorage(addr common.Address, key []byte) ([]byte, error)
-
// GetAccount abstracts an account read from the trie. It retrieves the
// account blob from the trie with provided account address and decodes it
// with associated decoding algorithm. If the specified account is not in
@@ -83,27 +86,32 @@ type Trie interface {
// be returned.
GetAccount(address common.Address) (*types.StateAccount, error)
- // UpdateStorage associates key with value in the trie. If value has length zero,
- // any existing value is deleted from the trie. The value bytes must not be modified
- // by the caller while they are stored in the trie. If a node was not found in the
- // database, a trie.MissingNodeError is returned.
- UpdateStorage(addr common.Address, key, value []byte) error
+ // GetStorage returns the value for key stored in the trie. The value bytes
+ // must not be modified by the caller. If a node was not found in the database,
+ // a trie.MissingNodeError is returned.
+ GetStorage(addr common.Address, key []byte) ([]byte, error)
// UpdateAccount abstracts an account write to the trie. It encodes the
// provided account object with associated algorithm and then updates it
// in the trie with provided address.
UpdateAccount(address common.Address, account *types.StateAccount) error
- // UpdateContractCode abstracts code write to the trie. It is expected
- // to be moved to the stateWriter interface when the latter is ready.
- UpdateContractCode(address common.Address, codeHash common.Hash, code []byte) error
+ // UpdateStorage associates key with value in the trie. If value has length zero,
+ // any existing value is deleted from the trie. The value bytes must not be modified
+ // by the caller while they are stored in the trie. If a node was not found in the
+ // database, a trie.MissingNodeError is returned.
+ UpdateStorage(addr common.Address, key, value []byte) error
+
+ // DeleteAccount abstracts an account deletion from the trie.
+ DeleteAccount(address common.Address) error
// DeleteStorage removes any existing value for key from the trie. If a node
// was not found in the database, a trie.MissingNodeError is returned.
DeleteStorage(addr common.Address, key []byte) error
- // DeleteAccount abstracts an account deletion from the trie.
- DeleteAccount(address common.Address) error
+ // UpdateContractCode abstracts code write to the trie. It is expected
+ // to be moved to the stateWriter interface when the latter is ready.
+ UpdateContractCode(address common.Address, codeHash common.Hash, code []byte) error
// Hash returns the root hash of the trie. It does not write to the database and
// can be used even if the trie doesn't have one.
@@ -170,6 +178,9 @@ type cachingDB struct {
// OpenTrie opens the main account trie at a specific root hash.
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
+ if db.triedb.IsVerkle() {
+ return trie.NewVerkleTrie(root, db.triedb, utils.NewPointCache(commitmentCacheItems))
+ }
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
if err != nil {
return nil, err
@@ -178,7 +189,13 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
}
// OpenStorageTrie opens the storage trie of an account.
-func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (Trie, error) {
+func (db *cachingDB) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, self Trie) (Trie, error) {
+ // In the verkle case, there is only one tree. But the two-tree structure
+ // is hardcoded in the codebase. So we need to return the same trie in this
+ // case.
+ if db.triedb.IsVerkle() {
+ return self, nil
+ }
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb)
if err != nil {
return nil, err
diff --git a/core/state/iterator.go b/core/state/iterator.go
index 683efd73de..dc84ce689b 100644
--- a/core/state/iterator.go
+++ b/core/state/iterator.go
@@ -123,7 +123,7 @@ func (it *nodeIterator) step() error {
address := common.BytesToAddress(preimage)
// Traverse the storage slots belong to the account
- dataTrie, err := it.state.db.OpenStorageTrie(it.state.originalRoot, address, account.Root)
+ dataTrie, err := it.state.db.OpenStorageTrie(it.state.originalRoot, address, account.Root, it.state.trie)
if err != nil {
return err
}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index d42d2c34d8..fc66b48114 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -145,7 +145,7 @@ func (s *stateObject) getTrie() (Trie, error) {
s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root)
}
if s.trie == nil {
- tr, err := s.db.db.OpenStorageTrie(s.db.originalRoot, s.address, s.data.Root)
+ tr, err := s.db.db.OpenStorageTrie(s.db.originalRoot, s.address, s.data.Root, s.db.trie)
if err != nil {
return nil, err
}
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 195e463c28..674227857c 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -998,7 +998,7 @@ func (s *StateDB) fastDeleteStorage(addrHash common.Hash, root common.Hash) (boo
// employed when the associated state snapshot is not available. It iterates the
// storage slots along with all internal trie nodes via trie directly.
func (s *StateDB) slowDeleteStorage(addr common.Address, addrHash common.Hash, root common.Hash) (bool, common.StorageSize, map[common.Hash][]byte, *trienode.NodeSet, error) {
- tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root)
+ tr, err := s.db.OpenStorageTrie(s.originalRoot, addr, root, s.trie)
if err != nil {
return false, 0, nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err)
}
diff --git a/core/state/trie_prefetcher.go b/core/state/trie_prefetcher.go
index 772c698dd0..c2a49417d4 100644
--- a/core/state/trie_prefetcher.go
+++ b/core/state/trie_prefetcher.go
@@ -305,7 +305,9 @@ func (sf *subfetcher) loop() {
}
sf.trie = trie
} else {
- trie, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root)
+ // The trie argument can be nil as verkle doesn't support prefetching
+ // yet. TODO FIX IT(rjl493456442), otherwise code will panic here.
+ trie, err := sf.db.OpenStorageTrie(sf.state, sf.addr, sf.root, nil)
if err != nil {
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
return
diff --git a/core/types/hashes.go b/core/types/hashes.go
index 3a787aa136..43e9130fd1 100644
--- a/core/types/hashes.go
+++ b/core/types/hashes.go
@@ -23,7 +23,7 @@ import (
)
var (
- // EmptyRootHash is the known root hash of an empty trie.
+ // EmptyRootHash is the known root hash of an empty merkle trie.
EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
// EmptyUncleHash is the known hash of the empty uncle set.
@@ -40,6 +40,9 @@ var (
// EmptyWithdrawalsHash is the known hash of the empty withdrawal set.
EmptyWithdrawalsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
+
+ // EmptyVerkleHash is the known hash of an empty verkle trie.
+ EmptyVerkleHash = common.Hash{}
)
// TrieRootHash returns the hash itself if it's non-empty or the predefined
diff --git a/core/types/rlp_fuzzer_test.go b/core/types/rlp_fuzzer_test.go
new file mode 100644
index 0000000000..a3b9f72436
--- /dev/null
+++ b/core/types/rlp_fuzzer_test.go
@@ -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 .
+
+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)
+ }
+ }
+}
diff --git a/tests/fuzzers/rlp/rlp_test.go b/core/vm/contracts_fuzz_test.go
similarity index 57%
rename from tests/fuzzers/rlp/rlp_test.go
rename to core/vm/contracts_fuzz_test.go
index 377b3961bf..87c1fff7cc 100644
--- a/tests/fuzzers/rlp/rlp_test.go
+++ b/core/vm/contracts_fuzz_test.go
@@ -14,12 +14,31 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-package rlp
+package vm
-import "testing"
+import (
+ "testing"
-func Fuzz(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- fuzz(data)
+ "github.com/ethereum/go-ethereum/common"
+)
+
+func FuzzPrecompiledContracts(f *testing.F) {
+ // Create list of addresses
+ var addrs []common.Address
+ for k := range allPrecompiles {
+ addrs = append(addrs, k)
+ }
+ f.Fuzz(func(t *testing.T, addr uint8, input []byte) {
+ a := addrs[int(addr)%len(addrs)]
+ p := allPrecompiles[a]
+ gas := p.RequiredGas(input)
+ if gas > 10_000_000 {
+ return
+ }
+ inWant := string(input)
+ RunPrecompiledContract(p, input, gas)
+ if inHave := string(input); inWant != inHave {
+ t.Errorf("Precompiled %v modified input data", a)
+ }
})
}
diff --git a/core/vm/opcodes.go b/core/vm/opcodes.go
index c7a3a163be..2b9231fe1a 100644
--- a/core/vm/opcodes.go
+++ b/core/vm/opcodes.go
@@ -25,7 +25,7 @@ type OpCode byte
// IsPush specifies if an opcode is a PUSH opcode.
func (op OpCode) IsPush() bool {
- return PUSH1 <= op && op <= PUSH32
+ return PUSH0 <= op && op <= PUSH32
}
// 0x0 range - arithmetic ops.
diff --git a/tests/fuzzers/runtime/runtime_test.go b/core/vm/runtime/runtime_fuzz_test.go
similarity index 87%
rename from tests/fuzzers/runtime/runtime_test.go
rename to core/vm/runtime/runtime_fuzz_test.go
index 2d73a56ca1..8a4d31d819 100644
--- a/tests/fuzzers/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_fuzz_test.go
@@ -18,13 +18,11 @@ package runtime
import (
"testing"
-
- "github.com/ethereum/go-ethereum/core/vm/runtime"
)
-func Fuzz(f *testing.F) {
+func FuzzVmRuntime(f *testing.F) {
f.Fuzz(func(t *testing.T, code, input []byte) {
- runtime.Execute(code, input, &runtime.Config{
+ Execute(code, input, &Config{
GasLimit: 12000000,
})
})
diff --git a/tests/fuzzers/snap/fuzz_handler.go b/eth/protocols/snap/handler_fuzzing_test.go
similarity index 77%
rename from tests/fuzzers/snap/fuzz_handler.go
rename to eth/protocols/snap/handler_fuzzing_test.go
index 20521bb92a..daed7ed44a 100644
--- a/tests/fuzzers/snap/fuzz_handler.go
+++ b/eth/protocols/snap/handler_fuzzing_test.go
@@ -21,6 +21,7 @@ import (
"encoding/binary"
"fmt"
"math/big"
+ "testing"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -28,7 +29,6 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/protocols/snap"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
"github.com/ethereum/go-ethereum/params"
@@ -36,6 +36,56 @@ import (
fuzz "github.com/google/gofuzz"
)
+func FuzzARange(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ doFuzz(data, &GetAccountRangePacket{}, GetAccountRangeMsg)
+ })
+}
+
+func FuzzSRange(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ doFuzz(data, &GetStorageRangesPacket{}, GetStorageRangesMsg)
+ })
+}
+
+func FuzzByteCodes(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ doFuzz(data, &GetByteCodesPacket{}, GetByteCodesMsg)
+ })
+}
+
+func FuzzTrieNodes(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ doFuzz(data, &GetTrieNodesPacket{}, GetTrieNodesMsg)
+ })
+}
+
+func doFuzz(input []byte, obj interface{}, code int) {
+ bc := getChain()
+ defer bc.Stop()
+ fuzz.NewFromGoFuzz(input).Fuzz(obj)
+ var data []byte
+ switch p := obj.(type) {
+ case *GetTrieNodesPacket:
+ p.Root = trieRoot
+ data, _ = rlp.EncodeToBytes(obj)
+ default:
+ data, _ = rlp.EncodeToBytes(obj)
+ }
+ cli := &dummyRW{
+ code: uint64(code),
+ data: data,
+ }
+ peer := NewFakePeer(65, "gazonk01", cli)
+ err := HandleMessage(&dummyBackend{bc}, peer)
+ switch {
+ case err == nil && cli.writeCount != 1:
+ panic(fmt.Sprintf("Expected 1 response, got %d", cli.writeCount))
+ case err != nil && cli.writeCount != 0:
+ panic(fmt.Sprintf("Expected 0 response, got %d", cli.writeCount))
+ }
+}
+
var trieRoot common.Hash
func getChain() *core.BlockChain {
@@ -86,10 +136,10 @@ type dummyBackend struct {
chain *core.BlockChain
}
-func (d *dummyBackend) Chain() *core.BlockChain { return d.chain }
-func (d *dummyBackend) RunPeer(*snap.Peer, snap.Handler) error { return nil }
-func (d *dummyBackend) PeerInfo(enode.ID) interface{} { return "Foo" }
-func (d *dummyBackend) Handle(*snap.Peer, snap.Packet) error { return nil }
+func (d *dummyBackend) Chain() *core.BlockChain { return d.chain }
+func (d *dummyBackend) RunPeer(*Peer, Handler) error { return nil }
+func (d *dummyBackend) PeerInfo(enode.ID) interface{} { return "Foo" }
+func (d *dummyBackend) Handle(*Peer, Packet) error { return nil }
type dummyRW struct {
code uint64
@@ -110,34 +160,3 @@ func (d *dummyRW) WriteMsg(msg p2p.Msg) error {
d.writeCount++
return nil
}
-
-func doFuzz(input []byte, obj interface{}, code int) int {
- if len(input) > 1024*4 {
- return -1
- }
- bc := getChain()
- defer bc.Stop()
- backend := &dummyBackend{bc}
- fuzz.NewFromGoFuzz(input).Fuzz(obj)
- var data []byte
- switch p := obj.(type) {
- case *snap.GetTrieNodesPacket:
- p.Root = trieRoot
- data, _ = rlp.EncodeToBytes(obj)
- default:
- data, _ = rlp.EncodeToBytes(obj)
- }
- cli := &dummyRW{
- code: uint64(code),
- data: data,
- }
- peer := snap.NewFakePeer(65, "gazonk01", cli)
- err := snap.HandleMessage(backend, peer)
- switch {
- case err == nil && cli.writeCount != 1:
- panic(fmt.Sprintf("Expected 1 response, got %d", cli.writeCount))
- case err != nil && cli.writeCount != 0:
- panic(fmt.Sprintf("Expected 0 response, got %d", cli.writeCount))
- }
- return 1
-}
diff --git a/ethdb/memorydb/memorydb.go b/ethdb/memorydb/memorydb.go
index f9f74322b5..2a939f9a18 100644
--- a/ethdb/memorydb/memorydb.go
+++ b/ethdb/memorydb/memorydb.go
@@ -207,7 +207,7 @@ func (db *Database) Len() int {
// keyvalue is a key-value tuple tagged with a deletion field to allow creating
// memory-database write batches.
type keyvalue struct {
- key []byte
+ key string
value []byte
delete bool
}
@@ -222,14 +222,14 @@ type batch struct {
// Put inserts the given value into the batch for later committing.
func (b *batch) Put(key, value []byte) error {
- b.writes = append(b.writes, keyvalue{common.CopyBytes(key), common.CopyBytes(value), false})
+ b.writes = append(b.writes, keyvalue{string(key), common.CopyBytes(value), false})
b.size += len(key) + len(value)
return nil
}
// Delete inserts the a key removal into the batch for later committing.
func (b *batch) Delete(key []byte) error {
- b.writes = append(b.writes, keyvalue{common.CopyBytes(key), nil, true})
+ b.writes = append(b.writes, keyvalue{string(key), nil, true})
b.size += len(key)
return nil
}
@@ -249,10 +249,10 @@ func (b *batch) Write() error {
}
for _, keyvalue := range b.writes {
if keyvalue.delete {
- delete(b.db.db, string(keyvalue.key))
+ delete(b.db.db, keyvalue.key)
continue
}
- b.db.db[string(keyvalue.key)] = keyvalue.value
+ b.db.db[keyvalue.key] = keyvalue.value
}
return nil
}
@@ -267,12 +267,12 @@ func (b *batch) Reset() {
func (b *batch) Replay(w ethdb.KeyValueWriter) error {
for _, keyvalue := range b.writes {
if keyvalue.delete {
- if err := w.Delete(keyvalue.key); err != nil {
+ if err := w.Delete([]byte(keyvalue.key)); err != nil {
return err
}
continue
}
- if err := w.Put(keyvalue.key, keyvalue.value); err != nil {
+ if err := w.Put([]byte(keyvalue.key), keyvalue.value); err != nil {
return err
}
}
diff --git a/ethdb/memorydb/memorydb_test.go b/ethdb/memorydb/memorydb_test.go
index dba18ad306..51499c3b1f 100644
--- a/ethdb/memorydb/memorydb_test.go
+++ b/ethdb/memorydb/memorydb_test.go
@@ -17,6 +17,7 @@
package memorydb
import (
+ "encoding/binary"
"testing"
"github.com/ethereum/go-ethereum/ethdb"
@@ -30,3 +31,20 @@ func TestMemoryDB(t *testing.T) {
})
})
}
+
+// BenchmarkBatchAllocs measures the time/allocs for storing 120 kB of data
+func BenchmarkBatchAllocs(b *testing.B) {
+ b.ReportAllocs()
+ var key = make([]byte, 20)
+ var val = make([]byte, 100)
+ // 120 * 1_000 -> 120_000 == 120kB
+ for i := 0; i < b.N; i++ {
+ batch := New().NewBatch()
+ for j := uint64(0); j < 1000; j++ {
+ binary.BigEndian.PutUint64(key, j)
+ binary.BigEndian.PutUint64(val, j)
+ batch.Put(key, val)
+ }
+ batch.Write()
+ }
+}
diff --git a/go.mod b/go.mod
index 97a64a11b8..8ba27d8bb7 100644
--- a/go.mod
+++ b/go.mod
@@ -16,6 +16,7 @@ require (
github.com/cockroachdb/errors v1.8.1
github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593
github.com/consensys/gnark-crypto v0.12.1
+ github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233
github.com/crate-crypto/go-kzg-4844 v0.7.0
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set/v2 v2.1.0
@@ -27,13 +28,13 @@ require (
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
github.com/fsnotify/fsnotify v1.6.0
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
- github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b
+ github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46
github.com/go-stack/stack v1.8.1
github.com/gofrs/flock v0.8.1
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/golang/protobuf v1.5.3
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
- github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa
+ github.com/google/gofuzz v1.2.0
github.com/google/uuid v1.3.0
github.com/gorilla/websocket v1.4.2
github.com/graph-gophers/graphql-go v1.3.0
@@ -98,7 +99,6 @@ require (
github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
github.com/consensys/bavard v0.1.13 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
- github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/deepmap/oapi-codegen v1.6.0 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
diff --git a/go.sum b/go.sum
index a04b4e6c60..ad99f55446 100644
--- a/go.sum
+++ b/go.sum
@@ -100,6 +100,8 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bits-and-blooms/bitset v1.7.0 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo=
github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
+github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88=
+github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
@@ -146,8 +148,10 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80 h1:DuBDHVjgGMPki7bAyh91+3cF1Vh34sAEdH8JQgbc2R0=
-github.com/crate-crypto/go-ipa v0.0.0-20230601170251-1830d0757c80/go.mod h1:gzbVz57IDJgQ9rLQwfSk696JGWof8ftznEL9GoAv3NI=
+github.com/crate-crypto/go-ipa v0.0.0-20230914135612-d1b03fcb8e58 h1:PwUlswsGOrLB677lW4XrlWLeszY3BaDGbvZ6dYk28tQ=
+github.com/crate-crypto/go-ipa v0.0.0-20230914135612-d1b03fcb8e58/go.mod h1:J+gsi6D4peY0kyhaklyXFRVHOQWI2I5uU0c2+/90HYc=
+github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ=
+github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs=
github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA=
github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -204,8 +208,10 @@ github.com/garslo/gogen v0.0.0-20170306192744-1d203ffc1f61/go.mod h1:Q0X6pkwTILD
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
-github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b h1:vMT47RYsrftsHSTQhqXwC3BYflo38OLC3Y4LtXtLyU0=
-github.com/gballet/go-verkle v0.0.0-20230607174250-df487255f46b/go.mod h1:CDncRYVRSDqwakm282WEkjfaAj1hxU/v5RXxk5nXOiI=
+github.com/gballet/go-verkle v0.1.1-0.20231004173727-0a4e93ed640b h1:LHeiiSTL2FEGCP1ov6FqkikiViqygeVo1ZwJ1x3nYSE=
+github.com/gballet/go-verkle v0.1.1-0.20231004173727-0a4e93ed640b/go.mod h1:7JamHhSTnnHDhcI3G8r4sWaD9XlleriqVlC3FeAQJKM=
+github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE=
+github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc=
github.com/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
@@ -302,8 +308,8 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64=
-github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -424,7 +430,6 @@ github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvf
github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c=
-github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
@@ -737,6 +742,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
+golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js
index 7a09fddab0..f23c65584c 100644
--- a/internal/jsre/deps/web3.js
+++ b/internal/jsre/deps/web3.js
@@ -1033,7 +1033,7 @@ var formatOutputInt = function (param) {
*
* @method formatOutputUInt
* @param {SolidityParam}
- * @returns {BigNumeber} right-aligned output bytes formatted to uint
+ * @returns {BigNumber} right-aligned output bytes formatted to uint
*/
var formatOutputUInt = function (param) {
var value = param.staticPart() || "0";
diff --git a/les/benchmark.go b/les/benchmark.go
index ab93518349..d1efa2f5d3 100644
--- a/les/benchmark.go
+++ b/les/benchmark.go
@@ -338,7 +338,7 @@ func (h *serverHandler) measure(setup *benchmarkSetup, count int) error {
case <-h.closeCh:
clientPipe.Close()
serverPipe.Close()
- return errors.New("Benchmark cancelled")
+ return errors.New("benchmark cancelled")
}
setup.totalTime += time.Duration(mclock.Now() - start)
diff --git a/les/peer.go b/les/peer.go
index 58cb928700..b38a393d4c 100644
--- a/les/peer.go
+++ b/les/peer.go
@@ -1000,7 +1000,7 @@ func (p *clientPeer) Handshake(td *big.Int, head common.Hash, headNum uint64, ge
}
}
if recentTx != txIndexUnlimited && p.version < lpv4 {
- return errors.New("Cannot serve old clients without a complete tx index")
+ return errors.New("cannot serve old clients without a complete tx index")
}
// Note: clientPeer.headInfo should contain the last head announced to the client by us.
// The values announced in the handshake are dummy values for compatibility reasons and should be ignored.
diff --git a/les/peer_test.go b/les/peer_test.go
index 0881dd292b..d6ca0eac7c 100644
--- a/les/peer_test.go
+++ b/les/peer_test.go
@@ -143,7 +143,7 @@ func TestHandshake(t *testing.T) {
return err
}
if reqType != announceTypeSigned {
- return errors.New("Expected announceTypeSigned")
+ return errors.New("expected announceTypeSigned")
}
return nil
})
diff --git a/les/server_requests.go b/les/server_requests.go
index 9a249f04c9..cc5b601713 100644
--- a/les/server_requests.go
+++ b/les/server_requests.go
@@ -430,7 +430,7 @@ func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) {
p.bumpInvalid()
continue
}
- trie, err = statedb.OpenStorageTrie(root, address, account.Root)
+ trie, err = statedb.OpenStorageTrie(root, address, account.Root, nil)
if trie == nil || err != nil {
p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "account", address, "root", account.Root, "err", err)
continue
diff --git a/les/vflux/client/valuetracker.go b/les/vflux/client/valuetracker.go
index 806d0c7d75..e0d1010ffe 100644
--- a/les/vflux/client/valuetracker.go
+++ b/les/vflux/client/valuetracker.go
@@ -257,7 +257,7 @@ func (vt *ValueTracker) loadFromDb(mapping []string) error {
}
if version != vtVersion {
log.Error("Unknown ValueTracker version", "stored", version, "current", nvtVersion)
- return fmt.Errorf("Unknown ValueTracker version %d (current version is %d)", version, vtVersion)
+ return fmt.Errorf("unknown ValueTracker version %d (current version is %d)", version, vtVersion)
}
var vte valueTrackerEncV1
if err := rlp.Decode(r, &vte); err != nil {
@@ -295,7 +295,7 @@ loop:
} else {
if vte.RefBasketMapping >= uint(len(vt.mappings)) {
log.Error("Unknown request basket mapping", "stored", vte.RefBasketMapping, "current", vt.currentMapping)
- return fmt.Errorf("Unknown request basket mapping %d (current version is %d)", vte.RefBasketMapping, vt.currentMapping)
+ return fmt.Errorf("unknown request basket mapping %d (current version is %d)", vte.RefBasketMapping, vt.currentMapping)
}
vt.refBasket.basket = vte.RefBasket.convertMapping(vt.mappings[vte.RefBasketMapping], mapping, vt.initRefBasket)
}
diff --git a/light/odr_test.go b/light/odr_test.go
index c415d73e7e..de12f9b7ef 100644
--- a/light/odr_test.go
+++ b/light/odr_test.go
@@ -89,7 +89,7 @@ func (odr *testOdr) Retrieve(ctx context.Context, req OdrRequest) error {
t state.Trie
)
if len(req.Id.AccountAddress) > 0 {
- t, err = odr.serverState.OpenStorageTrie(req.Id.StateRoot, common.BytesToAddress(req.Id.AccountAddress), req.Id.Root)
+ t, err = odr.serverState.OpenStorageTrie(req.Id.StateRoot, common.BytesToAddress(req.Id.AccountAddress), req.Id.Root, nil)
} else {
t, err = odr.serverState.OpenTrie(req.Id.Root)
}
diff --git a/light/trie.go b/light/trie.go
index 1847f1e71b..1d93bdf415 100644
--- a/light/trie.go
+++ b/light/trie.go
@@ -55,7 +55,7 @@ func (db *odrDatabase) OpenTrie(root common.Hash) (state.Trie, error) {
return &odrTrie{db: db, id: db.id}, nil
}
-func (db *odrDatabase) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash) (state.Trie, error) {
+func (db *odrDatabase) OpenStorageTrie(stateRoot common.Hash, address common.Address, root common.Hash, _ state.Trie) (state.Trie, error) {
return &odrTrie{db: db, id: StorageTrieID(db.id, address, root)}, nil
}
diff --git a/metrics/disk_nop.go b/metrics/disk_nop.go
index 58fa4e02f8..41bbe9adb2 100644
--- a/metrics/disk_nop.go
+++ b/metrics/disk_nop.go
@@ -23,5 +23,5 @@ import "errors"
// ReadDiskStats retrieves the disk IO stats belonging to the current process.
func ReadDiskStats(stats *DiskStats) error {
- return errors.New("Not implemented")
+ return errors.New("not implemented")
}
diff --git a/metrics/gauge_float64_test.go b/metrics/gauge_float64_test.go
index f0ac7ea5e7..194a18821f 100644
--- a/metrics/gauge_float64_test.go
+++ b/metrics/gauge_float64_test.go
@@ -36,7 +36,7 @@ func TestGaugeFloat64Snapshot(t *testing.T) {
g.Update(47.0)
snapshot := g.Snapshot()
g.Update(float64(0))
- if v := snapshot.Value(); 47.0 != v {
+ if v := snapshot.Value(); v != 47.0 {
t.Errorf("g.Value(): 47.0 != %v\n", v)
}
}
@@ -45,7 +45,7 @@ func TestGetOrRegisterGaugeFloat64(t *testing.T) {
r := NewRegistry()
NewRegisteredGaugeFloat64("foo", r).Update(47.0)
t.Logf("registry: %v", r)
- if g := GetOrRegisterGaugeFloat64("foo", r).Snapshot(); 47.0 != g.Value() {
+ if g := GetOrRegisterGaugeFloat64("foo", r).Snapshot(); g.Value() != 47.0 {
t.Fatal(g)
}
}
diff --git a/miner/miner_test.go b/miner/miner_test.go
index 36d5166c6d..411d6026ce 100644
--- a/miner/miner_test.go
+++ b/miner/miner_test.go
@@ -99,6 +99,7 @@ func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent)
}
func TestMiner(t *testing.T) {
+ t.Parallel()
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
@@ -128,6 +129,7 @@ func TestMiner(t *testing.T) {
// An initial FailedEvent should allow mining to stop on a subsequent
// downloader StartEvent.
func TestMinerDownloaderFirstFails(t *testing.T) {
+ t.Parallel()
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
@@ -161,6 +163,7 @@ func TestMinerDownloaderFirstFails(t *testing.T) {
}
func TestMinerStartStopAfterDownloaderEvents(t *testing.T) {
+ t.Parallel()
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
@@ -185,6 +188,7 @@ func TestMinerStartStopAfterDownloaderEvents(t *testing.T) {
}
func TestStartWhileDownload(t *testing.T) {
+ t.Parallel()
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
waitForMiningState(t, miner, false)
@@ -199,6 +203,7 @@ func TestStartWhileDownload(t *testing.T) {
}
func TestStartStopMiner(t *testing.T) {
+ t.Parallel()
miner, _, cleanup := createMiner(t)
defer cleanup(false)
waitForMiningState(t, miner, false)
@@ -209,6 +214,7 @@ func TestStartStopMiner(t *testing.T) {
}
func TestCloseMiner(t *testing.T) {
+ t.Parallel()
miner, _, cleanup := createMiner(t)
defer cleanup(true)
waitForMiningState(t, miner, false)
@@ -222,6 +228,7 @@ func TestCloseMiner(t *testing.T) {
// TestMinerSetEtherbase checks that etherbase becomes set even if mining isn't
// possible at the moment
func TestMinerSetEtherbase(t *testing.T) {
+ t.Parallel()
miner, mux, cleanup := createMiner(t)
defer cleanup(false)
miner.Start()
diff --git a/miner/ordering_test.go b/miner/ordering_test.go
index 59d478274d..e5868d7a06 100644
--- a/miner/ordering_test.go
+++ b/miner/ordering_test.go
@@ -30,10 +30,12 @@ import (
)
func TestTransactionPriceNonceSortLegacy(t *testing.T) {
+ t.Parallel()
testTransactionPriceNonceSort(t, nil)
}
func TestTransactionPriceNonceSort1559(t *testing.T) {
+ t.Parallel()
testTransactionPriceNonceSort(t, big.NewInt(0))
testTransactionPriceNonceSort(t, big.NewInt(5))
testTransactionPriceNonceSort(t, big.NewInt(50))
@@ -138,6 +140,7 @@ func testTransactionPriceNonceSort(t *testing.T, baseFee *big.Int) {
// Tests that if multiple transactions have the same price, the ones seen earlier
// are prioritized to avoid network spam attacks aiming for a specific ordering.
func TestTransactionTimeSort(t *testing.T) {
+ t.Parallel()
// Generate a batch of accounts to start with
keys := make([]*ecdsa.PrivateKey, 5)
for i := 0; i < len(keys); i++ {
diff --git a/miner/payload_building_test.go b/miner/payload_building_test.go
index 6f57363441..9283635224 100644
--- a/miner/payload_building_test.go
+++ b/miner/payload_building_test.go
@@ -30,6 +30,7 @@ import (
)
func TestBuildPayload(t *testing.T) {
+ t.Parallel()
var (
db = rawdb.NewMemoryDatabase()
recipient = common.HexToAddress("0xdeadbeef")
@@ -82,6 +83,7 @@ func TestBuildPayload(t *testing.T) {
}
func TestPayloadId(t *testing.T) {
+ t.Parallel()
ids := make(map[string]int)
for i, tt := range []*BuildPayloadArgs{
{
diff --git a/miner/worker_test.go b/miner/worker_test.go
index 9c4694c0e2..59fbbbcdca 100644
--- a/miner/worker_test.go
+++ b/miner/worker_test.go
@@ -167,6 +167,7 @@ func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consens
}
func TestGenerateAndImportBlock(t *testing.T) {
+ t.Parallel()
var (
db = rawdb.NewMemoryDatabase()
config = *params.AllCliqueProtocolChanges
@@ -210,9 +211,11 @@ func TestGenerateAndImportBlock(t *testing.T) {
}
func TestEmptyWorkEthash(t *testing.T) {
+ t.Parallel()
testEmptyWork(t, ethashChainConfig, ethash.NewFaker())
}
func TestEmptyWorkClique(t *testing.T) {
+ t.Parallel()
testEmptyWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase()))
}
@@ -252,10 +255,12 @@ func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consens
}
func TestAdjustIntervalEthash(t *testing.T) {
+ t.Parallel()
testAdjustInterval(t, ethashChainConfig, ethash.NewFaker())
}
func TestAdjustIntervalClique(t *testing.T) {
+ t.Parallel()
testAdjustInterval(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase()))
}
@@ -346,14 +351,17 @@ func testAdjustInterval(t *testing.T, chainConfig *params.ChainConfig, engine co
}
func TestGetSealingWorkEthash(t *testing.T) {
+ t.Parallel()
testGetSealingWork(t, ethashChainConfig, ethash.NewFaker())
}
func TestGetSealingWorkClique(t *testing.T) {
+ t.Parallel()
testGetSealingWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase()))
}
func TestGetSealingWorkPostMerge(t *testing.T) {
+ t.Parallel()
local := new(params.ChainConfig)
*local = *ethashChainConfig
local.TerminalTotalDifficulty = big.NewInt(0)
diff --git a/oss-fuzz.sh b/oss-fuzz.sh
index 55660d08e3..8978de70dd 100644
--- a/oss-fuzz.sh
+++ b/oss-fuzz.sh
@@ -48,39 +48,27 @@ DOG
cd -
}
-function build_native_go_fuzzer() {
- fuzzer=$1
- function=$2
- path=$3
- tags="-tags gofuzz"
-
- if [[ $SANITIZER == *coverage* ]]; then
- coverbuild $path $function $fuzzer $coverpkg
- else
- go-118-fuzz-build $tags -o $fuzzer.a -func $function $path
- $CXX $CXXFLAGS $LIB_FUZZING_ENGINE $fuzzer.a -o $OUT/$fuzzer
- fi
-}
-
function compile_fuzzer() {
- path=$GOPATH/src/github.com/ethereum/go-ethereum/$1
+ package=$1
function=$2
fuzzer=$3
+ file=$4
+
+ path=$GOPATH/src/$package
echo "Building $fuzzer"
cd $path
# Install build dependencies
- go install github.com/AdamKorcz/go-118-fuzz-build@latest
- go get github.com/AdamKorcz/go-118-fuzz-build/testing
+ go mod tidy
+ go get github.com/holiman/gofuzz-shim/testing
- # Test if file contains a line with "func $function(" and "testing.F".
- if [ $(grep -r "func $function(" $path | grep "testing.F" | wc -l) -eq 1 ]
- then
- build_native_go_fuzzer $fuzzer $function $path
- else
- echo "Could not find the function: func ${function}(f *testing.F)"
- fi
+ if [[ $SANITIZER == *coverage* ]]; then
+ coverbuild $path $function $fuzzer $coverpkg
+ else
+ gofuzz-shim --func $function --package $package -f $file -o $fuzzer.a
+ $CXX $CXXFLAGS $LIB_FUZZING_ENGINE $fuzzer.a -o $OUT/$fuzzer
+ fi
## Check if there exists a seed corpus file
corpusfile="${path}/testdata/${fuzzer}_seed_corpus.zip"
@@ -92,42 +80,140 @@ function compile_fuzzer() {
cd -
}
-compile_fuzzer tests/fuzzers/bitutil FuzzEncoder fuzzBitutilEncoder
-compile_fuzzer tests/fuzzers/bitutil FuzzDecoder fuzzBitutilDecoder
-compile_fuzzer tests/fuzzers/bn256 FuzzAdd fuzzBn256Add
-compile_fuzzer tests/fuzzers/bn256 FuzzMul fuzzBn256Mul
-compile_fuzzer tests/fuzzers/bn256 FuzzPair fuzzBn256Pair
-compile_fuzzer tests/fuzzers/runtime Fuzz fuzzVmRuntime
-compile_fuzzer tests/fuzzers/keystore Fuzz fuzzKeystore
-compile_fuzzer tests/fuzzers/txfetcher Fuzz fuzzTxfetcher
-compile_fuzzer tests/fuzzers/rlp Fuzz fuzzRlp
-compile_fuzzer tests/fuzzers/trie Fuzz fuzzTrie
-compile_fuzzer tests/fuzzers/stacktrie Fuzz fuzzStackTrie
-compile_fuzzer tests/fuzzers/difficulty Fuzz fuzzDifficulty
-compile_fuzzer tests/fuzzers/abi Fuzz fuzzAbi
-compile_fuzzer tests/fuzzers/les Fuzz fuzzLes
-compile_fuzzer tests/fuzzers/secp256k1 Fuzz fuzzSecp256k1
-compile_fuzzer tests/fuzzers/vflux FuzzClientPool fuzzClientPool
+go install github.com/holiman/gofuzz-shim@latest
+repo=$GOPATH/src/github.com/ethereum/go-ethereum
+compile_fuzzer github.com/ethereum/go-ethereum/accounts/abi \
+ FuzzABI fuzzAbi \
+ $repo/accounts/abi/abifuzzer_test.go
-compile_fuzzer tests/fuzzers/bls12381 FuzzG1Add fuzz_g1_add
-compile_fuzzer tests/fuzzers/bls12381 FuzzG1Mul fuzz_g1_mul
-compile_fuzzer tests/fuzzers/bls12381 FuzzG1MultiExp fuzz_g1_multiexp
-compile_fuzzer tests/fuzzers/bls12381 FuzzG2Add fuzz_g2_add
-compile_fuzzer tests/fuzzers/bls12381 FuzzG2Mul fuzz_g2_mul
-compile_fuzzer tests/fuzzers/bls12381 FuzzG2MultiExp fuzz_g2_multiexp
-compile_fuzzer tests/fuzzers/bls12381 FuzzPairing fuzz_pairing
-compile_fuzzer tests/fuzzers/bls12381 FuzzMapG1 fuzz_map_g1
-compile_fuzzer tests/fuzzers/bls12381 FuzzMapG2 fuzz_map_g2
+compile_fuzzer github.com/ethereum/go-ethereum/common/bitutil \
+ FuzzEncoder fuzzBitutilEncoder \
+ $repo/common/bitutil/compress_test.go
-compile_fuzzer tests/fuzzers/bls12381 FuzzCrossG1Add fuzz_cross_g1_add
-compile_fuzzer tests/fuzzers/bls12381 FuzzCrossG1MultiExp fuzz_cross_g1_multiexp
-compile_fuzzer tests/fuzzers/bls12381 FuzzCrossG2Add fuzz_cross_g2_add
-compile_fuzzer tests/fuzzers/bls12381 FuzzCrossPairing fuzz_cross_pairing
+compile_fuzzer github.com/ethereum/go-ethereum/common/bitutil \
+ FuzzDecoder fuzzBitutilDecoder \
+ $repo/common/bitutil/compress_test.go
-compile_fuzzer tests/fuzzers/snap FuzzARange fuzz_account_range
-compile_fuzzer tests/fuzzers/snap FuzzSRange fuzz_storage_range
-compile_fuzzer tests/fuzzers/snap FuzzByteCodes fuzz_byte_codes
-compile_fuzzer tests/fuzzers/snap FuzzTrieNodes fuzz_trie_nodes
+compile_fuzzer github.com/ethereum/go-ethereum/core/vm/runtime \
+ FuzzVmRuntime fuzzVmRuntime\
+ $repo/core/vm/runtime/runtime_fuzz_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/core/vm \
+ FuzzPrecompiledContracts fuzzPrecompiledContracts\
+ $repo/core/vm/contracts_fuzz_test.go,$repo/core/vm/contracts_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/core/types \
+ FuzzRLP fuzzRlp \
+ $repo/core/types/rlp_fuzzer_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/crypto/blake2b \
+ Fuzz fuzzBlake2b \
+ $repo/crypto/blake2b/blake2b_f_fuzz_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/accounts/keystore \
+ FuzzPassword fuzzKeystore \
+ $repo/accounts/keystore/keystore_fuzzing_test.go
+
+pkg=$repo/trie/
+compile_fuzzer github.com/ethereum/go-ethereum/trie \
+ FuzzTrie fuzzTrie \
+ $pkg/trie_test.go,$pkg/database_test.go,$pkg/tracer_test.go,$pkg/proof_test.go,$pkg/iterator_test.go,$pkg/sync_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/trie \
+ FuzzStackTrie fuzzStackTrie \
+ $pkg/stacktrie_fuzzer_test.go,$pkg/iterator_test.go,$pkg/trie_test.go,$pkg/database_test.go,$pkg/tracer_test.go,$pkg/proof_test.go,$pkg/sync_test.go
+
+#compile_fuzzer tests/fuzzers/snap FuzzARange fuzz_account_range
+compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/snap \
+ FuzzARange fuzz_account_range \
+ $repo/eth/protocols/snap/handler_fuzzing_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/snap \
+ FuzzSRange fuzz_storage_range \
+ $repo/eth/protocols/snap/handler_fuzzing_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/snap \
+ FuzzByteCodes fuzz_byte_codes \
+ $repo/eth/protocols/snap/handler_fuzzing_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/eth/protocols/snap \
+ FuzzTrieNodes fuzz_trie_nodes\
+ $repo/eth/protocols/snap/handler_fuzzing_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bn256 \
+ FuzzAdd fuzzBn256Add\
+ $repo/tests/fuzzers/bn256/bn256_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bn256 \
+ FuzzMul fuzzBn256Mul \
+ $repo/tests/fuzzers/bn256/bn256_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bn256 \
+ FuzzPair fuzzBn256Pair \
+ $repo/tests/fuzzers/bn256/bn256_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/txfetcher \
+ Fuzz fuzzTxfetcher \
+ $repo/tests/fuzzers/txfetcher/txfetcher_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzG1Add fuzz_g1_add\
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzG1Mul fuzz_g1_mul\
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzG1MultiExp fuzz_g1_multiexp \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzG2Add fuzz_g2_add \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzG2Mul fuzz_g2_mul\
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzG2MultiExp fuzz_g2_multiexp \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzPairing fuzz_pairing \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzMapG1 fuzz_map_g1\
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzMapG2 fuzz_map_g2 \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzCrossG1Add fuzz_cross_g1_add \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzCrossG1MultiExp fuzz_cross_g1_multiexp \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzCrossG2Add fuzz_cross_g2_add \
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/bls12381 \
+ FuzzCrossPairing fuzz_cross_pairing\
+ $repo/tests/fuzzers/bls12381/bls12381_test.go
+
+compile_fuzzer github.com/ethereum/go-ethereum/tests/fuzzers/secp256k1 \
+ Fuzz fuzzSecp256k1\
+ $repo/tests/fuzzers/secp256k1/secp_test.go
+
+
+#compile_fuzzer tests/fuzzers/vflux FuzzClientPool fuzzClientPool
+#compile_fuzzer tests/fuzzers/difficulty Fuzz fuzzDifficulty
+#compile_fuzzer tests/fuzzers/les Fuzz fuzzLes
-#TODO: move this to tests/fuzzers, if possible
-compile_fuzzer crypto/blake2b Fuzz fuzzBlake2b
diff --git a/p2p/nat/nat.go b/p2p/nat/nat.go
index 61b6922988..2aa1f85585 100644
--- a/p2p/nat/nat.go
+++ b/p2p/nat/nat.go
@@ -61,12 +61,12 @@ type Interface interface {
// "pmp:192.168.0.1" uses NAT-PMP with the given gateway address
func Parse(spec string) (Interface, error) {
var (
- parts = strings.SplitN(spec, ":", 2)
- mech = strings.ToLower(parts[0])
- ip net.IP
+ before, after, found = strings.Cut(spec, ":")
+ mech = strings.ToLower(before)
+ ip net.IP
)
- if len(parts) > 1 {
- ip = net.ParseIP(parts[1])
+ if found {
+ ip = net.ParseIP(after)
if ip == nil {
return nil, errors.New("invalid IP address")
}
@@ -86,7 +86,7 @@ func Parse(spec string) (Interface, error) {
case "pmp", "natpmp", "nat-pmp":
return PMP(ip), nil
default:
- return nil, fmt.Errorf("unknown mechanism %q", parts[0])
+ return nil, fmt.Errorf("unknown mechanism %q", before)
}
}
diff --git a/p2p/simulations/http.go b/p2p/simulations/http.go
index 7a4f70e9b0..34521b4778 100644
--- a/p2p/simulations/http.go
+++ b/p2p/simulations/http.go
@@ -479,12 +479,12 @@ func (s *Server) StreamNetworkEvents(w http.ResponseWriter, req *http.Request) {
func NewMsgFilters(filterParam string) (MsgFilters, error) {
filters := make(MsgFilters)
for _, filter := range strings.Split(filterParam, "-") {
- protoCodes := strings.SplitN(filter, ":", 2)
- if len(protoCodes) != 2 || protoCodes[0] == "" || protoCodes[1] == "" {
+ proto, codes, found := strings.Cut(filter, ":")
+ if !found || proto == "" || codes == "" {
return nil, fmt.Errorf("invalid message filter: %s", filter)
}
- proto := protoCodes[0]
- for _, code := range strings.Split(protoCodes[1], ",") {
+
+ for _, code := range strings.Split(codes, ",") {
if code == "*" || code == "-1" {
filters[MsgFilter{Proto: proto, Code: -1}] = struct{}{}
continue
diff --git a/rpc/json.go b/rpc/json.go
index 8a3b162cab..78f7d7a650 100644
--- a/rpc/json.go
+++ b/rpc/json.go
@@ -86,8 +86,8 @@ func (msg *jsonrpcMessage) isUnsubscribe() bool {
}
func (msg *jsonrpcMessage) namespace() string {
- elem := strings.SplitN(msg.Method, serviceMethodSeparator, 2)
- return elem[0]
+ before, _, _ := strings.Cut(msg.Method, serviceMethodSeparator)
+ return before
}
func (msg *jsonrpcMessage) String() string {
diff --git a/rpc/service.go b/rpc/service.go
index 8485cab3aa..a180b8db93 100644
--- a/rpc/service.go
+++ b/rpc/service.go
@@ -93,13 +93,13 @@ func (r *serviceRegistry) registerName(name string, rcvr interface{}) error {
// callback returns the callback corresponding to the given RPC method name.
func (r *serviceRegistry) callback(method string) *callback {
- elem := strings.SplitN(method, serviceMethodSeparator, 2)
- if len(elem) != 2 {
+ before, after, found := strings.Cut(method, serviceMethodSeparator)
+ if !found {
return nil
}
r.mu.Lock()
defer r.mu.Unlock()
- return r.services[elem[0]].callbacks[elem[1]]
+ return r.services[before].callbacks[after]
}
// subscription returns a subscription callback in the given service.
diff --git a/tests/block_test.go b/tests/block_test.go
index 5764ae33e4..e913ecbc90 100644
--- a/tests/block_test.go
+++ b/tests/block_test.go
@@ -17,6 +17,8 @@
package tests
import (
+ "math/rand"
+ "runtime"
"testing"
"github.com/ethereum/go-ethereum/common"
@@ -49,6 +51,9 @@ func TestBlockchain(t *testing.T) {
bt.skipLoad(`.*randomStatetest94.json.*`)
bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) {
+ if runtime.GOARCH == "386" && runtime.GOOS == "windows" && rand.Int63()%2 == 0 {
+ t.Skip("test (randomly) skipped on 32-bit windows")
+ }
execBlockTest(t, bt, test)
})
// There is also a LegacyTests folder, containing blockchain tests generated
diff --git a/tests/fuzzers/bitutil/compress_test.go b/tests/fuzzers/bitutil/compress_test.go
deleted file mode 100644
index ed9d27eb30..0000000000
--- a/tests/fuzzers/bitutil/compress_test.go
+++ /dev/null
@@ -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 .
-
-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")
- }
-}
diff --git a/tests/fuzzers/keystore/keystore-fuzzer.go b/tests/fuzzers/keystore/keystore-fuzzer.go
deleted file mode 100644
index 07a85d77b5..0000000000
--- a/tests/fuzzers/keystore/keystore-fuzzer.go
+++ /dev/null
@@ -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 .
-
-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
-}
diff --git a/tests/fuzzers/rlp/corpus/block_with_uncle.rlp b/tests/fuzzers/rlp/corpus/block_with_uncle.rlp
deleted file mode 100644
index 1b49fe6a09..0000000000
Binary files a/tests/fuzzers/rlp/corpus/block_with_uncle.rlp and /dev/null differ
diff --git a/tests/fuzzers/rlp/corpus/r.bin b/tests/fuzzers/rlp/corpus/r.bin
deleted file mode 100644
index cb98a76a8a..0000000000
--- a/tests/fuzzers/rlp/corpus/r.bin
+++ /dev/null
@@ -1 +0,0 @@
-Ë€€€À€ÀÃÀÀÀÀ
\ No newline at end of file
diff --git a/tests/fuzzers/rlp/corpus/transaction.rlp b/tests/fuzzers/rlp/corpus/transaction.rlp
deleted file mode 100644
index 80eea1aec6..0000000000
--- a/tests/fuzzers/rlp/corpus/transaction.rlp
+++ /dev/null
@@ -1,2 +0,0 @@
-øNƒ“à€€€‚
-• aùËåÀP?-'´{ÏЋDY¯³fÆj\ÿE÷ ~ì•ÒçF?1(íij6@Év±LÀÝÚ‘‘
\ No newline at end of file
diff --git a/tests/fuzzers/rlp/rlp_fuzzer.go b/tests/fuzzers/rlp/rlp_fuzzer.go
deleted file mode 100644
index 0da8ccdd77..0000000000
--- a/tests/fuzzers/rlp/rlp_fuzzer.go
+++ /dev/null
@@ -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 .
-
-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
-}
diff --git a/tests/fuzzers/secp256k1/secp_test.go b/tests/fuzzers/secp256k1/secp_test.go
index fbdd8e6ac2..ca3039764b 100644
--- a/tests/fuzzers/secp256k1/secp_test.go
+++ b/tests/fuzzers/secp256k1/secp_test.go
@@ -35,7 +35,7 @@ func Fuzz(f *testing.F) {
})
}
-func fuzz(dataP1, dataP2 []byte) int {
+func fuzz(dataP1, dataP2 []byte) {
var (
curveA = secp256k1.S256()
curveB = btcec.S256()
@@ -50,5 +50,4 @@ func fuzz(dataP1, dataP2 []byte) int {
fmt.Printf("%s %s %s %s\n", x1, y1, x2, y2)
panic(fmt.Sprintf("Addition failed: geth: %s %s btcd: %s %s", resAX, resAY, resBX, resBY))
}
- return 0
}
diff --git a/tests/fuzzers/snap/fuzz_test.go b/tests/fuzzers/snap/fuzz_test.go
deleted file mode 100644
index 1c39f2bb50..0000000000
--- a/tests/fuzzers/snap/fuzz_test.go
+++ /dev/null
@@ -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 .
-
-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)
- })
-}
diff --git a/tests/fuzzers/stacktrie/trie_fuzzer.go b/tests/fuzzers/stacktrie/trie_fuzzer.go
deleted file mode 100644
index 9e02176e3e..0000000000
--- a/tests/fuzzers/stacktrie/trie_fuzzer.go
+++ /dev/null
@@ -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 .
-
-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
-}
diff --git a/tests/fuzzers/stacktrie/trie_test.go b/tests/fuzzers/stacktrie/trie_test.go
deleted file mode 100644
index f6f755f76a..0000000000
--- a/tests/fuzzers/stacktrie/trie_test.go
+++ /dev/null
@@ -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 .
-
-package stacktrie
-
-import "testing"
-
-func Fuzz(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- fuzz(data)
- })
-}
diff --git a/tests/fuzzers/trie/trie-fuzzer.go b/tests/fuzzers/trie/trie-fuzzer.go
deleted file mode 100644
index a505fa78a0..0000000000
--- a/tests/fuzzers/trie/trie-fuzzer.go
+++ /dev/null
@@ -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 .
-
-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
-}
diff --git a/tests/fuzzers/trie/trie_test.go b/tests/fuzzers/trie/trie_test.go
deleted file mode 100644
index a7d28a806e..0000000000
--- a/tests/fuzzers/trie/trie_test.go
+++ /dev/null
@@ -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 .
-
-package trie
-
-import "testing"
-
-func Fuzz(f *testing.F) {
- f.Fuzz(func(t *testing.T, data []byte) {
- fuzz(data)
- })
-}
diff --git a/tests/state_test.go b/tests/state_test.go
index 094dafcafd..ae78a53a7e 100644
--- a/tests/state_test.go
+++ b/tests/state_test.go
@@ -21,9 +21,11 @@ import (
"bytes"
"fmt"
"math/big"
+ "math/rand"
"os"
"path/filepath"
"reflect"
+ "runtime"
"strings"
"testing"
"time"
@@ -76,6 +78,10 @@ func TestState(t *testing.T) {
benchmarksDir,
} {
st.walk(t, dir, func(t *testing.T, name string, test *StateTest) {
+ if runtime.GOARCH == "386" && runtime.GOOS == "windows" && rand.Int63()%2 == 0 {
+ t.Skip("test (randomly) skipped on 32-bit windows")
+ return
+ }
for _, subtest := range test.Subtests() {
subtest := subtest
key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index)
diff --git a/trie/database.go b/trie/database.go
index 1e59f0908f..321b4f8955 100644
--- a/trie/database.go
+++ b/trie/database.go
@@ -31,6 +31,7 @@ import (
// Config defines all necessary options for database.
type Config struct {
Preimages bool // Flag whether the preimage of node key is recorded
+ IsVerkle bool // Flag whether the db is holding a verkle tree
HashDB *hashdb.Config // Configs for hash-based scheme
PathDB *pathdb.Config // Configs for experimental path-based scheme
}
@@ -318,3 +319,8 @@ func (db *Database) SetBufferSize(size int) error {
}
return pdb.SetBufferSize(size)
}
+
+// IsVerkle returns the indicator if the database is holding a verkle tree.
+func (db *Database) IsVerkle() bool {
+ return db.config.IsVerkle
+}
diff --git a/trie/hasher.go b/trie/hasher.go
index e594d6d6b2..1e063d8020 100644
--- a/trie/hasher.go
+++ b/trie/hasher.go
@@ -84,20 +84,19 @@ func (h *hasher) hash(n node, force bool) (hashed node, cached node) {
}
return hashed, cached
default:
- // Value and hash nodes don't have children so they're left as were
+ // Value and hash nodes don't have children, so they're left as were
return n, n
}
}
// hashShortNodeChildren collapses the short node. The returned collapsed node
// holds a live reference to the Key, and must not be modified.
-// The cached
func (h *hasher) hashShortNodeChildren(n *shortNode) (collapsed, cached *shortNode) {
// Hash the short node's child, caching the newly hashed subtree
collapsed, cached = n.copy(), n.copy()
// Previously, we did copy this one. We don't seem to need to actually
// do that, since we don't overwrite/reuse keys
- //cached.Key = common.CopyBytes(n.Key)
+ // cached.Key = common.CopyBytes(n.Key)
collapsed.Key = hexToCompact(n.Key)
// Unless the child is a valuenode or hashnode, hash it
switch n.Val.(type) {
@@ -153,7 +152,7 @@ func (h *hasher) shortnodeToHash(n *shortNode, force bool) node {
return h.hashData(enc)
}
-// shortnodeToHash is used to creates a hashNode from a set of hashNodes, (which
+// fullnodeToHash is used to create a hashNode from a fullNode, (which
// may contain nil values)
func (h *hasher) fullnodeToHash(n *fullNode, force bool) node {
n.encode(h.encbuf)
@@ -203,7 +202,7 @@ func (h *hasher) proofHash(original node) (collapsed, hashed node) {
fn, _ := h.hashFullNodeChildren(n)
return fn, h.fullnodeToHash(fn, false)
default:
- // Value and hash nodes don't have children so they're left as were
+ // Value and hash nodes don't have children, so they're left as were
return n, n
}
}
diff --git a/trie/iterator.go b/trie/iterator.go
index 6f054a7245..83ccc0740f 100644
--- a/trie/iterator.go
+++ b/trie/iterator.go
@@ -144,7 +144,8 @@ type nodeIterator struct {
path []byte // Path to the current node
err error // Failure set in case of an internal error in the iterator
- resolver NodeResolver // optional node resolver for avoiding disk hits
+ resolver NodeResolver // optional node resolver for avoiding disk hits
+ pool []*nodeIteratorState // local pool for iteratorstates
}
// errIteratorEnd is stored in nodeIterator.err when iteration is done.
@@ -172,6 +173,24 @@ func newNodeIterator(trie *Trie, start []byte) NodeIterator {
return it
}
+func (it *nodeIterator) putInPool(item *nodeIteratorState) {
+ if len(it.pool) < 40 {
+ item.node = nil
+ it.pool = append(it.pool, item)
+ }
+}
+
+func (it *nodeIterator) getFromPool() *nodeIteratorState {
+ idx := len(it.pool) - 1
+ if idx < 0 {
+ return new(nodeIteratorState)
+ }
+ el := it.pool[idx]
+ it.pool[idx] = nil
+ it.pool = it.pool[:idx]
+ return el
+}
+
func (it *nodeIterator) AddResolver(resolver NodeResolver) {
it.resolver = resolver
}
@@ -423,8 +442,9 @@ func (st *nodeIteratorState) resolve(it *nodeIterator, path []byte) error {
return nil
}
-func findChild(n *fullNode, index int, path []byte, ancestor common.Hash) (node, *nodeIteratorState, []byte, int) {
+func (it *nodeIterator) findChild(n *fullNode, index int, ancestor common.Hash) (node, *nodeIteratorState, []byte, int) {
var (
+ path = it.path
child node
state *nodeIteratorState
childPath []byte
@@ -433,13 +453,12 @@ func findChild(n *fullNode, index int, path []byte, ancestor common.Hash) (node,
if n.Children[index] != nil {
child = n.Children[index]
hash, _ := child.cache()
- state = &nodeIteratorState{
- hash: common.BytesToHash(hash),
- node: child,
- parent: ancestor,
- index: -1,
- pathlen: len(path),
- }
+ state = it.getFromPool()
+ state.hash = common.BytesToHash(hash)
+ state.node = child
+ state.parent = ancestor
+ state.index = -1
+ state.pathlen = len(path)
childPath = append(childPath, path...)
childPath = append(childPath, byte(index))
return child, state, childPath, index
@@ -452,7 +471,7 @@ func (it *nodeIterator) nextChild(parent *nodeIteratorState, ancestor common.Has
switch node := parent.node.(type) {
case *fullNode:
// Full node, move to the first non-nil child.
- if child, state, path, index := findChild(node, parent.index+1, it.path, ancestor); child != nil {
+ if child, state, path, index := it.findChild(node, parent.index+1, ancestor); child != nil {
parent.index = index - 1
return state, path, true
}
@@ -460,13 +479,12 @@ func (it *nodeIterator) nextChild(parent *nodeIteratorState, ancestor common.Has
// Short node, return the pointer singleton child
if parent.index < 0 {
hash, _ := node.Val.cache()
- state := &nodeIteratorState{
- hash: common.BytesToHash(hash),
- node: node.Val,
- parent: ancestor,
- index: -1,
- pathlen: len(it.path),
- }
+ state := it.getFromPool()
+ state.hash = common.BytesToHash(hash)
+ state.node = node.Val
+ state.parent = ancestor
+ state.index = -1
+ state.pathlen = len(it.path)
path := append(it.path, node.Key...)
return state, path, true
}
@@ -480,7 +498,7 @@ func (it *nodeIterator) nextChildAt(parent *nodeIteratorState, ancestor common.H
switch n := parent.node.(type) {
case *fullNode:
// Full node, move to the first non-nil child before the desired key position
- child, state, path, index := findChild(n, parent.index+1, it.path, ancestor)
+ child, state, path, index := it.findChild(n, parent.index+1, ancestor)
if child == nil {
// No more children in this fullnode
return parent, it.path, false
@@ -492,7 +510,7 @@ func (it *nodeIterator) nextChildAt(parent *nodeIteratorState, ancestor common.H
}
// The child is before the seek position. Try advancing
for {
- nextChild, nextState, nextPath, nextIndex := findChild(n, index+1, it.path, ancestor)
+ nextChild, nextState, nextPath, nextIndex := it.findChild(n, index+1, ancestor)
// If we run out of children, or skipped past the target, return the
// previous one
if nextChild == nil || bytes.Compare(nextPath, key) >= 0 {
@@ -506,13 +524,12 @@ func (it *nodeIterator) nextChildAt(parent *nodeIteratorState, ancestor common.H
// Short node, return the pointer singleton child
if parent.index < 0 {
hash, _ := n.Val.cache()
- state := &nodeIteratorState{
- hash: common.BytesToHash(hash),
- node: n.Val,
- parent: ancestor,
- index: -1,
- pathlen: len(it.path),
- }
+ state := it.getFromPool()
+ state.hash = common.BytesToHash(hash)
+ state.node = n.Val
+ state.parent = ancestor
+ state.index = -1
+ state.pathlen = len(it.path)
path := append(it.path, n.Key...)
return state, path, true
}
@@ -533,6 +550,8 @@ func (it *nodeIterator) pop() {
it.path = it.path[:last.pathlen]
it.stack[len(it.stack)-1] = nil
it.stack = it.stack[:len(it.stack)-1]
+ // last is now unused
+ it.putInPool(last)
}
func compareNodes(a, b NodeIterator) int {
diff --git a/trie/iterator_test.go b/trie/iterator_test.go
index 57d1f06a16..9679b49ca7 100644
--- a/trie/iterator_test.go
+++ b/trie/iterator_test.go
@@ -616,3 +616,15 @@ func isTrieNode(scheme string, key, val []byte) (bool, []byte, common.Hash) {
}
return true, path, hash
}
+
+func BenchmarkIterator(b *testing.B) {
+ diskDb, srcDb, tr, _ := makeTestTrie(rawdb.HashScheme)
+ root := tr.Hash()
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ if err := checkTrieConsistency(diskDb, srcDb.Scheme(), root, false); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/trie/stacktrie_fuzzer_test.go b/trie/stacktrie_fuzzer_test.go
new file mode 100644
index 0000000000..1b3f9dbe9c
--- /dev/null
+++ b/trie/stacktrie_fuzzer_test.go
@@ -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 .
+
+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")
+ }
+}
diff --git a/trie/sync_test.go b/trie/sync_test.go
index 3b7986ef67..7032c6d2f7 100644
--- a/trie/sync_test.go
+++ b/trie/sync_test.go
@@ -571,7 +571,7 @@ func testIncompleteSync(t *testing.T, scheme string) {
hash := crypto.Keccak256Hash(result.Data)
if hash != root {
addedKeys = append(addedKeys, result.Path)
- addedHashes = append(addedHashes, crypto.Keccak256Hash(result.Data))
+ addedHashes = append(addedHashes, hash)
}
}
// Fetch the next batch to retrieve
diff --git a/trie/trie_test.go b/trie/trie_test.go
index 4315753548..6af0f67b9f 100644
--- a/trie/trie_test.go
+++ b/trie/trie_test.go
@@ -22,6 +22,7 @@ import (
"errors"
"fmt"
"hash"
+ "io"
"math/big"
"math/rand"
"reflect"
@@ -362,7 +363,9 @@ func TestRandomCases(t *testing.T) {
{op: 1, key: common.Hex2Bytes("980c393656413a15c8da01978ed9f89feb80b502f58f2d640e3a2f5f7a99a7018f1b573befd92053ac6f78fca4a87268"), value: common.Hex2Bytes("")}, // step 24
{op: 1, key: common.Hex2Bytes("fd"), value: common.Hex2Bytes("")}, // step 25
}
- runRandTest(rt)
+ if err := runRandTest(rt); err != nil {
+ t.Fatal(err)
+ }
}
// randTest performs random trie operations.
@@ -389,33 +392,45 @@ const (
)
func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
+ var finishedFn = func() bool {
+ size--
+ return size > 0
+ }
+ return reflect.ValueOf(generateSteps(finishedFn, r))
+}
+
+func generateSteps(finished func() bool, r io.Reader) randTest {
var allKeys [][]byte
+ var one = []byte{0}
genKey := func() []byte {
- if len(allKeys) < 2 || r.Intn(100) < 10 {
+ r.Read(one)
+ if len(allKeys) < 2 || one[0]%100 > 90 {
// new key
- key := make([]byte, r.Intn(50))
+ size := one[0] % 50
+ key := make([]byte, size)
r.Read(key)
allKeys = append(allKeys, key)
return key
}
// use existing key
- return allKeys[r.Intn(len(allKeys))]
+ idx := int(one[0]) % len(allKeys)
+ return allKeys[idx]
}
-
var steps randTest
- for i := 0; i < size; i++ {
- step := randTestStep{op: r.Intn(opMax)}
+ for !finished() {
+ r.Read(one)
+ step := randTestStep{op: int(one[0]) % opMax}
switch step.op {
case opUpdate:
step.key = genKey()
step.value = make([]byte, 8)
- binary.BigEndian.PutUint64(step.value, uint64(i))
+ binary.BigEndian.PutUint64(step.value, uint64(len(steps)))
case opGet, opDelete, opProve:
step.key = genKey()
}
steps = append(steps, step)
}
- return reflect.ValueOf(steps)
+ return steps
}
func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error {
@@ -460,7 +475,12 @@ func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error {
return nil
}
-func runRandTest(rt randTest) bool {
+// runRandTestBool coerces error to boolean, for use in quick.Check
+func runRandTestBool(rt randTest) bool {
+ return runRandTest(rt) == nil
+}
+
+func runRandTest(rt randTest) error {
var scheme = rawdb.HashScheme
if rand.Intn(2) == 0 {
scheme = rawdb.PathScheme
@@ -513,12 +533,12 @@ func runRandTest(rt randTest) bool {
newtr, err := New(TrieID(root), triedb)
if err != nil {
rt[i].err = err
- return false
+ return err
}
if nodes != nil {
if err := verifyAccessList(origTrie, newtr, nodes); err != nil {
rt[i].err = err
- return false
+ return err
}
}
tr = newtr
@@ -587,14 +607,14 @@ func runRandTest(rt randTest) bool {
}
// Abort the test on error.
if rt[i].err != nil {
- return false
+ return rt[i].err
}
}
- return true
+ return nil
}
func TestRandom(t *testing.T) {
- if err := quick.Check(runRandTest, nil); err != nil {
+ if err := quick.Check(runRandTestBool, nil); err != nil {
if cerr, ok := err.(*quick.CheckError); ok {
t.Fatalf("random test iteration %d failed: %s", cerr.Count, spew.Sdump(cerr.In))
}
@@ -1185,3 +1205,17 @@ func TestDecodeNode(t *testing.T) {
decodeNode(hash, elems)
}
}
+
+func FuzzTrie(f *testing.F) {
+ f.Fuzz(func(t *testing.T, data []byte) {
+ var steps = 500
+ var input = bytes.NewReader(data)
+ var finishedFn = func() bool {
+ steps--
+ return steps < 0 || input.Len() == 0
+ }
+ if err := runRandTest(generateSteps(finishedFn, input)); err != nil {
+ t.Fatal(err)
+ }
+ })
+}
diff --git a/trie/trienode/node.go b/trie/trienode/node.go
index 98d5588b6d..95315c2e9a 100644
--- a/trie/trienode/node.go
+++ b/trie/trienode/node.go
@@ -39,7 +39,7 @@ func (n *Node) Size() int {
// IsDeleted returns the indicator if the node is marked as deleted.
func (n *Node) IsDeleted() bool {
- return n.Hash == (common.Hash{})
+ return len(n.Blob) == 0
}
// New constructs a node with provided node information.
diff --git a/trie/utils/verkle.go b/trie/utils/verkle.go
new file mode 100644
index 0000000000..ce059edc64
--- /dev/null
+++ b/trie/utils/verkle.go
@@ -0,0 +1,342 @@
+// Copyright 2023 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 .
+
+package utils
+
+import (
+ "encoding/binary"
+ "sync"
+
+ "github.com/crate-crypto/go-ipa/bandersnatch/fr"
+ "github.com/ethereum/go-ethereum/common/lru"
+ "github.com/ethereum/go-ethereum/metrics"
+ "github.com/gballet/go-verkle"
+ "github.com/holiman/uint256"
+)
+
+const (
+ // The spec of verkle key encoding can be found here.
+ // https://notes.ethereum.org/@vbuterin/verkle_tree_eip#Tree-embedding
+ VersionLeafKey = 0
+ BalanceLeafKey = 1
+ NonceLeafKey = 2
+ CodeKeccakLeafKey = 3
+ CodeSizeLeafKey = 4
+)
+
+var (
+ zero = uint256.NewInt(0)
+ verkleNodeWidthLog2 = 8
+ headerStorageOffset = uint256.NewInt(64)
+ mainStorageOffsetLshVerkleNodeWidth = new(uint256.Int).Lsh(uint256.NewInt(256), 31-uint(verkleNodeWidthLog2))
+ codeOffset = uint256.NewInt(128)
+ verkleNodeWidth = uint256.NewInt(256)
+ codeStorageDelta = uint256.NewInt(0).Sub(codeOffset, headerStorageOffset)
+
+ index0Point *verkle.Point // pre-computed commitment of polynomial [2+256*64]
+
+ // cacheHitGauge is the metric to track how many cache hit occurred.
+ cacheHitGauge = metrics.NewRegisteredGauge("trie/verkle/cache/hit", nil)
+
+ // cacheMissGauge is the metric to track how many cache miss occurred.
+ cacheMissGauge = metrics.NewRegisteredGauge("trie/verkle/cache/miss", nil)
+)
+
+func init() {
+ // The byte array is the Marshalled output of the point computed as such:
+ //
+ // var (
+ // config = verkle.GetConfig()
+ // fr verkle.Fr
+ // )
+ // verkle.FromLEBytes(&fr, []byte{2, 64})
+ // point := config.CommitToPoly([]verkle.Fr{fr}, 1)
+ index0Point = new(verkle.Point)
+ err := index0Point.SetBytes([]byte{34, 25, 109, 242, 193, 5, 144, 224, 76, 52, 189, 92, 197, 126, 9, 145, 27, 152, 199, 130, 165, 3, 210, 27, 193, 131, 142, 28, 110, 26, 16, 191})
+ if err != nil {
+ panic(err)
+ }
+}
+
+// PointCache is the LRU cache for storing evaluated address commitment.
+type PointCache struct {
+ lru lru.BasicLRU[string, *verkle.Point]
+ lock sync.RWMutex
+}
+
+// NewPointCache returns the cache with specified size.
+func NewPointCache(maxItems int) *PointCache {
+ return &PointCache{
+ lru: lru.NewBasicLRU[string, *verkle.Point](maxItems),
+ }
+}
+
+// Get returns the cached commitment for the specified address, or computing
+// it on the flight.
+func (c *PointCache) Get(addr []byte) *verkle.Point {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+
+ p, ok := c.lru.Get(string(addr))
+ if ok {
+ cacheHitGauge.Inc(1)
+ return p
+ }
+ cacheMissGauge.Inc(1)
+ p = evaluateAddressPoint(addr)
+ c.lru.Add(string(addr), p)
+ return p
+}
+
+// GetStem returns the first 31 bytes of the tree key as the tree stem. It only
+// works for the account metadata whose treeIndex is 0.
+func (c *PointCache) GetStem(addr []byte) []byte {
+ p := c.Get(addr)
+ return pointToHash(p, 0)[:31]
+}
+
+// GetTreeKey performs both the work of the spec's get_tree_key function, and that
+// of pedersen_hash: it builds the polynomial in pedersen_hash without having to
+// create a mostly zero-filled buffer and "type cast" it to a 128-long 16-byte
+// array. Since at most the first 5 coefficients of the polynomial will be non-zero,
+// these 5 coefficients are created directly.
+func GetTreeKey(address []byte, treeIndex *uint256.Int, subIndex byte) []byte {
+ if len(address) < 32 {
+ var aligned [32]byte
+ address = append(aligned[:32-len(address)], address...)
+ }
+ // poly = [2+256*64, address_le_low, address_le_high, tree_index_le_low, tree_index_le_high]
+ var poly [5]fr.Element
+
+ // 32-byte address, interpreted as two little endian
+ // 16-byte numbers.
+ verkle.FromLEBytes(&poly[1], address[:16])
+ verkle.FromLEBytes(&poly[2], address[16:])
+
+ // treeIndex must be interpreted as a 32-byte aligned little-endian integer.
+ // e.g: if treeIndex is 0xAABBCC, we need the byte representation to be 0xCCBBAA00...00.
+ // poly[3] = LE({CC,BB,AA,00...0}) (16 bytes), poly[4]=LE({00,00,...}) (16 bytes).
+ //
+ // To avoid unnecessary endianness conversions for go-ipa, we do some trick:
+ // - poly[3]'s byte representation is the same as the *top* 16 bytes (trieIndexBytes[16:]) of
+ // 32-byte aligned big-endian representation (BE({00,...,AA,BB,CC})).
+ // - poly[4]'s byte representation is the same as the *low* 16 bytes (trieIndexBytes[:16]) of
+ // the 32-byte aligned big-endian representation (BE({00,00,...}).
+ trieIndexBytes := treeIndex.Bytes32()
+ verkle.FromBytes(&poly[3], trieIndexBytes[16:])
+ verkle.FromBytes(&poly[4], trieIndexBytes[:16])
+
+ cfg := verkle.GetConfig()
+ ret := cfg.CommitToPoly(poly[:], 0)
+
+ // add a constant point corresponding to poly[0]=[2+256*64].
+ ret.Add(ret, index0Point)
+
+ return pointToHash(ret, subIndex)
+}
+
+// GetTreeKeyWithEvaluatedAddress is basically identical to GetTreeKey, the only
+// difference is a part of polynomial is already evaluated.
+//
+// Specifically, poly = [2+256*64, address_le_low, address_le_high] is already
+// evaluated.
+func GetTreeKeyWithEvaluatedAddress(evaluated *verkle.Point, treeIndex *uint256.Int, subIndex byte) []byte {
+ var poly [5]fr.Element
+
+ poly[0].SetZero()
+ poly[1].SetZero()
+ poly[2].SetZero()
+
+ // little-endian, 32-byte aligned treeIndex
+ var index [32]byte
+ for i := 0; i < len(treeIndex); i++ {
+ binary.LittleEndian.PutUint64(index[i*8:(i+1)*8], treeIndex[i])
+ }
+ verkle.FromLEBytes(&poly[3], index[:16])
+ verkle.FromLEBytes(&poly[4], index[16:])
+
+ cfg := verkle.GetConfig()
+ ret := cfg.CommitToPoly(poly[:], 0)
+
+ // add the pre-evaluated address
+ ret.Add(ret, evaluated)
+
+ return pointToHash(ret, subIndex)
+}
+
+// VersionKey returns the verkle tree key of the version field for the specified account.
+func VersionKey(address []byte) []byte {
+ return GetTreeKey(address, zero, VersionLeafKey)
+}
+
+// BalanceKey returns the verkle tree key of the balance field for the specified account.
+func BalanceKey(address []byte) []byte {
+ return GetTreeKey(address, zero, BalanceLeafKey)
+}
+
+// NonceKey returns the verkle tree key of the nonce field for the specified account.
+func NonceKey(address []byte) []byte {
+ return GetTreeKey(address, zero, NonceLeafKey)
+}
+
+// CodeKeccakKey returns the verkle tree key of the code keccak field for
+// the specified account.
+func CodeKeccakKey(address []byte) []byte {
+ return GetTreeKey(address, zero, CodeKeccakLeafKey)
+}
+
+// CodeSizeKey returns the verkle tree key of the code size field for the
+// specified account.
+func CodeSizeKey(address []byte) []byte {
+ return GetTreeKey(address, zero, CodeSizeLeafKey)
+}
+
+func codeChunkIndex(chunk *uint256.Int) (*uint256.Int, byte) {
+ var (
+ chunkOffset = new(uint256.Int).Add(codeOffset, chunk)
+ treeIndex = new(uint256.Int).Div(chunkOffset, verkleNodeWidth)
+ subIndexMod = new(uint256.Int).Mod(chunkOffset, verkleNodeWidth)
+ )
+ var subIndex byte
+ if len(subIndexMod) != 0 {
+ subIndex = byte(subIndexMod[0])
+ }
+ return treeIndex, subIndex
+}
+
+// CodeChunkKey returns the verkle tree key of the code chunk for the
+// specified account.
+func CodeChunkKey(address []byte, chunk *uint256.Int) []byte {
+ treeIndex, subIndex := codeChunkIndex(chunk)
+ return GetTreeKey(address, treeIndex, subIndex)
+}
+
+func storageIndex(bytes []byte) (*uint256.Int, byte) {
+ // If the storage slot is in the header, we need to add the header offset.
+ var key uint256.Int
+ key.SetBytes(bytes)
+ if key.Cmp(codeStorageDelta) < 0 {
+ // This addition is always safe; it can't ever overflow since pos
+
+package utils
+
+import (
+ "bytes"
+ "testing"
+
+ "github.com/gballet/go-verkle"
+ "github.com/holiman/uint256"
+)
+
+func TestTreeKey(t *testing.T) {
+ var (
+ address = []byte{0x01}
+ addressEval = evaluateAddressPoint(address)
+ smallIndex = uint256.NewInt(1)
+ largeIndex = uint256.NewInt(10000)
+ smallStorage = []byte{0x1}
+ largeStorage = bytes.Repeat([]byte{0xff}, 16)
+ )
+ if !bytes.Equal(VersionKey(address), VersionKeyWithEvaluatedAddress(addressEval)) {
+ t.Fatal("Unmatched version key")
+ }
+ if !bytes.Equal(BalanceKey(address), BalanceKeyWithEvaluatedAddress(addressEval)) {
+ t.Fatal("Unmatched balance key")
+ }
+ if !bytes.Equal(NonceKey(address), NonceKeyWithEvaluatedAddress(addressEval)) {
+ t.Fatal("Unmatched nonce key")
+ }
+ if !bytes.Equal(CodeKeccakKey(address), CodeKeccakKeyWithEvaluatedAddress(addressEval)) {
+ t.Fatal("Unmatched code keccak key")
+ }
+ if !bytes.Equal(CodeSizeKey(address), CodeSizeKeyWithEvaluatedAddress(addressEval)) {
+ t.Fatal("Unmatched code size key")
+ }
+ if !bytes.Equal(CodeChunkKey(address, smallIndex), CodeChunkKeyWithEvaluatedAddress(addressEval, smallIndex)) {
+ t.Fatal("Unmatched code chunk key")
+ }
+ if !bytes.Equal(CodeChunkKey(address, largeIndex), CodeChunkKeyWithEvaluatedAddress(addressEval, largeIndex)) {
+ t.Fatal("Unmatched code chunk key")
+ }
+ if !bytes.Equal(StorageSlotKey(address, smallStorage), StorageSlotKeyWithEvaluatedAddress(addressEval, smallStorage)) {
+ t.Fatal("Unmatched storage slot key")
+ }
+ if !bytes.Equal(StorageSlotKey(address, largeStorage), StorageSlotKeyWithEvaluatedAddress(addressEval, largeStorage)) {
+ t.Fatal("Unmatched storage slot key")
+ }
+}
+
+// goos: darwin
+// goarch: amd64
+// pkg: github.com/ethereum/go-ethereum/trie/utils
+// cpu: VirtualApple @ 2.50GHz
+// BenchmarkTreeKey
+// BenchmarkTreeKey-8 398731 2961 ns/op 32 B/op 1 allocs/op
+func BenchmarkTreeKey(b *testing.B) {
+ // Initialize the IPA settings which can be pretty expensive.
+ verkle.GetConfig()
+
+ b.ReportAllocs()
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ BalanceKey([]byte{0x01})
+ }
+}
+
+// goos: darwin
+// goarch: amd64
+// pkg: github.com/ethereum/go-ethereum/trie/utils
+// cpu: VirtualApple @ 2.50GHz
+// BenchmarkTreeKeyWithEvaluation
+// BenchmarkTreeKeyWithEvaluation-8 513855 2324 ns/op 32 B/op 1 allocs/op
+func BenchmarkTreeKeyWithEvaluation(b *testing.B) {
+ // Initialize the IPA settings which can be pretty expensive.
+ verkle.GetConfig()
+
+ addr := []byte{0x01}
+ eval := evaluateAddressPoint(addr)
+
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ BalanceKeyWithEvaluatedAddress(eval)
+ }
+}
+
+// goos: darwin
+// goarch: amd64
+// pkg: github.com/ethereum/go-ethereum/trie/utils
+// cpu: VirtualApple @ 2.50GHz
+// BenchmarkStorageKey
+// BenchmarkStorageKey-8 230516 4584 ns/op 96 B/op 3 allocs/op
+func BenchmarkStorageKey(b *testing.B) {
+ // Initialize the IPA settings which can be pretty expensive.
+ verkle.GetConfig()
+
+ b.ReportAllocs()
+ b.ResetTimer()
+
+ for i := 0; i < b.N; i++ {
+ StorageSlotKey([]byte{0x01}, bytes.Repeat([]byte{0xff}, 32))
+ }
+}
+
+// goos: darwin
+// goarch: amd64
+// pkg: github.com/ethereum/go-ethereum/trie/utils
+// cpu: VirtualApple @ 2.50GHz
+// BenchmarkStorageKeyWithEvaluation
+// BenchmarkStorageKeyWithEvaluation-8 320125 3753 ns/op 96 B/op 3 allocs/op
+func BenchmarkStorageKeyWithEvaluation(b *testing.B) {
+ // Initialize the IPA settings which can be pretty expensive.
+ verkle.GetConfig()
+
+ addr := []byte{0x01}
+ eval := evaluateAddressPoint(addr)
+
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ StorageSlotKeyWithEvaluatedAddress(eval, bytes.Repeat([]byte{0xff}, 32))
+ }
+}
diff --git a/trie/verkle.go b/trie/verkle.go
new file mode 100644
index 0000000000..89e2e53408
--- /dev/null
+++ b/trie/verkle.go
@@ -0,0 +1,375 @@
+// Copyright 2023 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 .
+
+package trie
+
+import (
+ "encoding/binary"
+ "errors"
+ "fmt"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/trie/trienode"
+ "github.com/ethereum/go-ethereum/trie/utils"
+ "github.com/gballet/go-verkle"
+ "github.com/holiman/uint256"
+)
+
+var (
+ zero [32]byte
+ errInvalidRootType = errors.New("invalid node type for root")
+)
+
+// VerkleTrie is a wrapper around VerkleNode that implements the trie.Trie
+// interface so that Verkle trees can be reused verbatim.
+type VerkleTrie struct {
+ root verkle.VerkleNode
+ db *Database
+ cache *utils.PointCache
+ reader *trieReader
+}
+
+// NewVerkleTrie constructs a verkle tree based on the specified root hash.
+func NewVerkleTrie(root common.Hash, db *Database, cache *utils.PointCache) (*VerkleTrie, error) {
+ reader, err := newTrieReader(root, common.Hash{}, db)
+ if err != nil {
+ return nil, err
+ }
+ // Parse the root verkle node if it's not empty.
+ node := verkle.New()
+ if root != types.EmptyVerkleHash && root != types.EmptyRootHash {
+ blob, err := reader.node(nil, common.Hash{})
+ if err != nil {
+ return nil, err
+ }
+ node, err = verkle.ParseNode(blob, 0)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return &VerkleTrie{
+ root: node,
+ db: db,
+ cache: cache,
+ reader: reader,
+ }, nil
+}
+
+// GetKey returns the sha3 preimage of a hashed key that was previously used
+// to store a value.
+func (t *VerkleTrie) GetKey(key []byte) []byte {
+ return key
+}
+
+// GetAccount implements state.Trie, retrieving the account with the specified
+// account address. If the specified account is not in the verkle tree, nil will
+// be returned. If the tree is corrupted, an error will be returned.
+func (t *VerkleTrie) GetAccount(addr common.Address) (*types.StateAccount, error) {
+ var (
+ acc = &types.StateAccount{}
+ values [][]byte
+ err error
+ )
+ switch n := t.root.(type) {
+ case *verkle.InternalNode:
+ values, err = n.GetValuesAtStem(t.cache.GetStem(addr[:]), t.nodeResolver)
+ if err != nil {
+ return nil, fmt.Errorf("GetAccount (%x) error: %v", addr, err)
+ }
+ default:
+ return nil, errInvalidRootType
+ }
+ if values == nil {
+ return nil, nil
+ }
+ // Decode nonce in little-endian
+ if len(values[utils.NonceLeafKey]) > 0 {
+ acc.Nonce = binary.LittleEndian.Uint64(values[utils.NonceLeafKey])
+ }
+ // Decode balance in little-endian
+ var balance [32]byte
+ copy(balance[:], values[utils.BalanceLeafKey])
+ for i := 0; i < len(balance)/2; i++ {
+ balance[len(balance)-i-1], balance[i] = balance[i], balance[len(balance)-i-1]
+ }
+ acc.Balance = new(big.Int).SetBytes(balance[:])
+
+ // Decode codehash
+ acc.CodeHash = values[utils.CodeKeccakLeafKey]
+
+ // TODO account.Root is leave as empty. How should we handle the legacy account?
+ return acc, nil
+}
+
+// GetStorage implements state.Trie, retrieving the storage slot with the specified
+// account address and storage key. If the specified slot is not in the verkle tree,
+// nil will be returned. If the tree is corrupted, an error will be returned.
+func (t *VerkleTrie) GetStorage(addr common.Address, key []byte) ([]byte, error) {
+ k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()), key)
+ val, err := t.root.Get(k, t.nodeResolver)
+ if err != nil {
+ return nil, err
+ }
+ return common.TrimLeftZeroes(val), nil
+}
+
+// UpdateAccount implements state.Trie, writing the provided account into the tree.
+// If the tree is corrupted, an error will be returned.
+func (t *VerkleTrie) UpdateAccount(addr common.Address, acc *types.StateAccount) error {
+ var (
+ err error
+ nonce, balance [32]byte
+ values = make([][]byte, verkle.NodeWidth)
+ )
+ values[utils.VersionLeafKey] = zero[:]
+ values[utils.CodeKeccakLeafKey] = acc.CodeHash[:]
+
+ // Encode nonce in little-endian
+ binary.LittleEndian.PutUint64(nonce[:], acc.Nonce)
+ values[utils.NonceLeafKey] = nonce[:]
+
+ // Encode balance in little-endian
+ bytes := acc.Balance.Bytes()
+ if len(bytes) > 0 {
+ for i, b := range bytes {
+ balance[len(bytes)-i-1] = b
+ }
+ }
+ values[utils.BalanceLeafKey] = balance[:]
+
+ switch n := t.root.(type) {
+ case *verkle.InternalNode:
+ err = n.InsertValuesAtStem(t.cache.GetStem(addr[:]), values, t.nodeResolver)
+ if err != nil {
+ return fmt.Errorf("UpdateAccount (%x) error: %v", addr, err)
+ }
+ default:
+ return errInvalidRootType
+ }
+ // TODO figure out if the code size needs to be updated, too
+ return nil
+}
+
+// UpdateStorage implements state.Trie, writing the provided storage slot into
+// the tree. If the tree is corrupted, an error will be returned.
+func (t *VerkleTrie) UpdateStorage(address common.Address, key, value []byte) error {
+ // Left padding the slot value to 32 bytes.
+ var v [32]byte
+ if len(value) >= 32 {
+ copy(v[:], value[:32])
+ } else {
+ copy(v[32-len(value):], value[:])
+ }
+ k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(address.Bytes()), key)
+ return t.root.Insert(k, v[:], t.nodeResolver)
+}
+
+// DeleteAccount implements state.Trie, deleting the specified account from the
+// trie. If the account was not existent in the trie, no error will be returned.
+// If the trie is corrupted, an error will be returned.
+func (t *VerkleTrie) DeleteAccount(addr common.Address) error {
+ var (
+ err error
+ values = make([][]byte, verkle.NodeWidth)
+ )
+ for i := 0; i < verkle.NodeWidth; i++ {
+ values[i] = zero[:]
+ }
+ switch n := t.root.(type) {
+ case *verkle.InternalNode:
+ err = n.InsertValuesAtStem(t.cache.GetStem(addr.Bytes()), values, t.nodeResolver)
+ if err != nil {
+ return fmt.Errorf("DeleteAccount (%x) error: %v", addr, err)
+ }
+ default:
+ return errInvalidRootType
+ }
+ return nil
+}
+
+// DeleteStorage implements state.Trie, deleting the specified storage slot from
+// the trie. If the storage slot was not existent in the trie, no error will be
+// returned. If the trie is corrupted, an error will be returned.
+func (t *VerkleTrie) DeleteStorage(addr common.Address, key []byte) error {
+ var zero [32]byte
+ k := utils.StorageSlotKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()), key)
+ return t.root.Insert(k, zero[:], t.nodeResolver)
+}
+
+// Hash returns the root hash of the tree. It does not write to the database and
+// can be used even if the tree doesn't have one.
+func (t *VerkleTrie) Hash() common.Hash {
+ return t.root.Commit().Bytes()
+}
+
+// Commit writes all nodes to the tree's memory database.
+func (t *VerkleTrie) Commit(_ bool) (common.Hash, *trienode.NodeSet, error) {
+ root, ok := t.root.(*verkle.InternalNode)
+ if !ok {
+ return common.Hash{}, nil, errors.New("unexpected root node type")
+ }
+ nodes, err := root.BatchSerialize()
+ if err != nil {
+ return common.Hash{}, nil, fmt.Errorf("serializing tree nodes: %s", err)
+ }
+ nodeset := trienode.NewNodeSet(common.Hash{})
+ for _, node := range nodes {
+ // hash parameter is not used in pathdb
+ nodeset.AddNode(node.Path, trienode.New(common.Hash{}, node.SerializedBytes))
+ }
+ // Serialize root commitment form
+ return t.Hash(), nodeset, nil
+}
+
+// NodeIterator implements state.Trie, returning an iterator that returns
+// nodes of the trie. Iteration starts at the key after the given start key.
+//
+// TODO(gballet, rjl493456442) implement it.
+func (t *VerkleTrie) NodeIterator(startKey []byte) (NodeIterator, error) {
+ panic("not implemented")
+}
+
+// Prove implements state.Trie, constructing a Merkle proof for key. The result
+// contains all encoded nodes on the path to the value at key. The value itself
+// is also included in the last node and can be retrieved by verifying the proof.
+//
+// If the trie does not contain a value for key, the returned proof contains all
+// nodes of the longest existing prefix of the key (at least the root), ending
+// with the node that proves the absence of the key.
+//
+// TODO(gballet, rjl493456442) implement it.
+func (t *VerkleTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
+ panic("not implemented")
+}
+
+// Copy returns a deep-copied verkle tree.
+func (t *VerkleTrie) Copy() *VerkleTrie {
+ return &VerkleTrie{
+ root: t.root.Copy(),
+ db: t.db,
+ cache: t.cache,
+ reader: t.reader,
+ }
+}
+
+// IsVerkle indicates if the trie is a Verkle trie.
+func (t *VerkleTrie) IsVerkle() bool {
+ return true
+}
+
+// ChunkedCode represents a sequence of 32-bytes chunks of code (31 bytes of which
+// are actual code, and 1 byte is the pushdata offset).
+type ChunkedCode []byte
+
+// Copy the values here so as to avoid an import cycle
+const (
+ PUSH1 = byte(0x60)
+ PUSH32 = byte(0x7f)
+)
+
+// ChunkifyCode generates the chunked version of an array representing EVM bytecode
+func ChunkifyCode(code []byte) ChunkedCode {
+ var (
+ chunkOffset = 0 // offset in the chunk
+ chunkCount = len(code) / 31
+ codeOffset = 0 // offset in the code
+ )
+ if len(code)%31 != 0 {
+ chunkCount++
+ }
+ chunks := make([]byte, chunkCount*32)
+ for i := 0; i < chunkCount; i++ {
+ // number of bytes to copy, 31 unless the end of the code has been reached.
+ end := 31 * (i + 1)
+ if len(code) < end {
+ end = len(code)
+ }
+ copy(chunks[i*32+1:], code[31*i:end]) // copy the code itself
+
+ // chunk offset = taken from the last chunk.
+ if chunkOffset > 31 {
+ // skip offset calculation if push data covers the whole chunk
+ chunks[i*32] = 31
+ chunkOffset = 1
+ continue
+ }
+ chunks[32*i] = byte(chunkOffset)
+ chunkOffset = 0
+
+ // Check each instruction and update the offset it should be 0 unless
+ // a PUSH-N overflows.
+ for ; codeOffset < end; codeOffset++ {
+ if code[codeOffset] >= PUSH1 && code[codeOffset] <= PUSH32 {
+ codeOffset += int(code[codeOffset] - PUSH1 + 1)
+ if codeOffset+1 >= 31*(i+1) {
+ codeOffset++
+ chunkOffset = codeOffset - 31*(i+1)
+ break
+ }
+ }
+ }
+ }
+ return chunks
+}
+
+// UpdateContractCode implements state.Trie, writing the provided contract code
+// into the trie.
+func (t *VerkleTrie) UpdateContractCode(addr common.Address, codeHash common.Hash, code []byte) error {
+ var (
+ chunks = ChunkifyCode(code)
+ values [][]byte
+ key []byte
+ err error
+ )
+ for i, chunknr := 0, uint64(0); i < len(chunks); i, chunknr = i+32, chunknr+1 {
+ groupOffset := (chunknr + 128) % 256
+ if groupOffset == 0 /* start of new group */ || chunknr == 0 /* first chunk in header group */ {
+ values = make([][]byte, verkle.NodeWidth)
+ key = utils.CodeChunkKeyWithEvaluatedAddress(t.cache.Get(addr.Bytes()), uint256.NewInt(chunknr))
+ }
+ values[groupOffset] = chunks[i : i+32]
+
+ // Reuse the calculated key to also update the code size.
+ if i == 0 {
+ cs := make([]byte, 32)
+ binary.LittleEndian.PutUint64(cs, uint64(len(code)))
+ values[utils.CodeSizeLeafKey] = cs
+ }
+ if groupOffset == 255 || len(chunks)-i <= 32 {
+ switch root := t.root.(type) {
+ case *verkle.InternalNode:
+ err = root.InsertValuesAtStem(key[:31], values, t.nodeResolver)
+ if err != nil {
+ return fmt.Errorf("UpdateContractCode (addr=%x) error: %w", addr[:], err)
+ }
+ default:
+ return errInvalidRootType
+ }
+ }
+ }
+ return nil
+}
+
+func (t *VerkleTrie) ToDot() string {
+ return verkle.ToDot(t.root)
+}
+
+func (t *VerkleTrie) nodeResolver(path []byte) ([]byte, error) {
+ return t.reader.node(path, common.Hash{})
+}
diff --git a/trie/verkle_test.go b/trie/verkle_test.go
new file mode 100644
index 0000000000..44fb7dc29e
--- /dev/null
+++ b/trie/verkle_test.go
@@ -0,0 +1,97 @@
+// Copyright 2023 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 .
+
+package trie
+
+import (
+ "bytes"
+ "math/big"
+ "reflect"
+ "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/trie/triedb/pathdb"
+ "github.com/ethereum/go-ethereum/trie/utils"
+)
+
+var (
+ accounts = map[common.Address]*types.StateAccount{
+ common.Address{1}: {
+ Nonce: 100,
+ Balance: big.NewInt(100),
+ CodeHash: common.Hash{0x1}.Bytes(),
+ },
+ common.Address{2}: {
+ Nonce: 200,
+ Balance: big.NewInt(200),
+ CodeHash: common.Hash{0x2}.Bytes(),
+ },
+ }
+ storages = map[common.Address]map[common.Hash][]byte{
+ common.Address{1}: {
+ common.Hash{10}: []byte{10},
+ common.Hash{11}: []byte{11},
+ common.MaxHash: []byte{0xff},
+ },
+ common.Address{2}: {
+ common.Hash{20}: []byte{20},
+ common.Hash{21}: []byte{21},
+ common.MaxHash: []byte{0xff},
+ },
+ }
+)
+
+func TestVerkleTreeReadWrite(t *testing.T) {
+ db := NewDatabase(rawdb.NewMemoryDatabase(), &Config{
+ IsVerkle: true,
+ PathDB: pathdb.Defaults,
+ })
+ defer db.Close()
+
+ tr, _ := NewVerkleTrie(types.EmptyVerkleHash, db, utils.NewPointCache(100))
+
+ for addr, acct := range accounts {
+ if err := tr.UpdateAccount(addr, acct); err != nil {
+ t.Fatalf("Failed to update account, %v", err)
+ }
+ for key, val := range storages[addr] {
+ if err := tr.UpdateStorage(addr, key.Bytes(), val); err != nil {
+ t.Fatalf("Failed to update account, %v", err)
+ }
+ }
+ }
+
+ for addr, acct := range accounts {
+ stored, err := tr.GetAccount(addr)
+ if err != nil {
+ t.Fatalf("Failed to get account, %v", err)
+ }
+ if !reflect.DeepEqual(stored, acct) {
+ t.Fatal("account is not matched")
+ }
+ for key, val := range storages[addr] {
+ stored, err := tr.GetStorage(addr, key.Bytes())
+ if err != nil {
+ t.Fatalf("Failed to get storage, %v", err)
+ }
+ if !bytes.Equal(stored, val) {
+ t.Fatal("storage is not matched")
+ }
+ }
+ }
+}