mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-28 07:36:44 +00:00
Merge branch 'opmaster' into portal
This commit is contained in:
commit
0e9882efc3
86 changed files with 2044 additions and 1189 deletions
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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())
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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'
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 (
|
||||
|
|
|
|||
|
|
@ -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" }})
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
|
|
|
|||
6
cmd/geth/testdata/logging/logtest-logfmt.txt
vendored
6
cmd/geth/testdata/logging/logtest-logfmt.txt
vendored
|
|
@ -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"
|
||||
|
|
|
|||
18
cmd/geth/testdata/logging/logtest-terminal.txt
vendored
18
cmd/geth/testdata/logging/logtest-terminal.txt
vendored
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
147
core/types/rlp_fuzzer_test.go
Normal file
147
core/types/rlp_fuzzer_test.go
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
// Copyright 2019 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
func decodeEncode(input []byte, val interface{}) error {
|
||||
if err := rlp.DecodeBytes(input, val); err != nil {
|
||||
// not valid rlp, nothing to do
|
||||
return nil
|
||||
}
|
||||
// If it _were_ valid rlp, we can encode it again
|
||||
output, err := rlp.EncodeToBytes(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !bytes.Equal(input, output) {
|
||||
return fmt.Errorf("encode-decode is not equal, \ninput : %x\noutput: %x", input, output)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func FuzzRLP(f *testing.F) {
|
||||
f.Fuzz(fuzzRlp)
|
||||
}
|
||||
|
||||
func fuzzRlp(t *testing.T, input []byte) {
|
||||
if len(input) == 0 || len(input) > 500*1024 {
|
||||
return
|
||||
}
|
||||
rlp.Split(input)
|
||||
if elems, _, err := rlp.SplitList(input); err == nil {
|
||||
rlp.CountValues(elems)
|
||||
}
|
||||
rlp.NewStream(bytes.NewReader(input), 0).Decode(new(interface{}))
|
||||
if err := decodeEncode(input, new(interface{})); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
{
|
||||
var v struct {
|
||||
Int uint
|
||||
String string
|
||||
Bytes []byte
|
||||
}
|
||||
if err := decodeEncode(input, &v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
{
|
||||
type Types struct {
|
||||
Bool bool
|
||||
Raw rlp.RawValue
|
||||
Slice []*Types
|
||||
Iface []interface{}
|
||||
}
|
||||
var v Types
|
||||
if err := decodeEncode(input, &v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
{
|
||||
type AllTypes struct {
|
||||
Int uint
|
||||
String string
|
||||
Bytes []byte
|
||||
Bool bool
|
||||
Raw rlp.RawValue
|
||||
Slice []*AllTypes
|
||||
Array [3]*AllTypes
|
||||
Iface []interface{}
|
||||
}
|
||||
var v AllTypes
|
||||
if err := decodeEncode(input, &v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
{
|
||||
if err := decodeEncode(input, [10]byte{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
{
|
||||
var v struct {
|
||||
Byte [10]byte
|
||||
Rool [10]bool
|
||||
}
|
||||
if err := decodeEncode(input, &v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
{
|
||||
var h Header
|
||||
if err := decodeEncode(input, &h); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var b Block
|
||||
if err := decodeEncode(input, &b); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var tx Transaction
|
||||
if err := decodeEncode(input, &tx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var txs Transactions
|
||||
if err := decodeEncode(input, &txs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
var rs Receipts
|
||||
if err := decodeEncode(input, &rs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
{
|
||||
var v struct {
|
||||
AnIntPtr *big.Int
|
||||
AnInt big.Int
|
||||
AnU256Ptr *uint256.Int
|
||||
AnU256 uint256.Int
|
||||
NotAnU256 [4]uint64
|
||||
}
|
||||
if err := decodeEncode(input, &v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
})
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
6
go.mod
6
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
|
||||
|
|
|
|||
21
go.sum
21
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=
|
||||
|
|
|
|||
|
|
@ -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";
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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++ {
|
||||
|
|
|
|||
|
|
@ -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{
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
204
oss-fuzz.sh
204
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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,68 +0,0 @@
|
|||
// Copyright 2023 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package bitutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/bitutil"
|
||||
)
|
||||
|
||||
func FuzzEncoder(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
fuzzEncode(data)
|
||||
})
|
||||
}
|
||||
func FuzzDecoder(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
fuzzDecode(data)
|
||||
})
|
||||
}
|
||||
|
||||
// fuzzEncode implements a go-fuzz fuzzer method to test the bitset encoding and
|
||||
// decoding algorithm.
|
||||
func fuzzEncode(data []byte) {
|
||||
proc, _ := bitutil.DecompressBytes(bitutil.CompressBytes(data), len(data))
|
||||
if !bytes.Equal(data, proc) {
|
||||
panic("content mismatch")
|
||||
}
|
||||
}
|
||||
|
||||
// fuzzDecode implements a go-fuzz fuzzer method to test the bit decoding and
|
||||
// reencoding algorithm.
|
||||
func fuzzDecode(data []byte) {
|
||||
blob, err := bitutil.DecompressBytes(data, 1024)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
// re-compress it (it's OK if the re-compressed differs from the
|
||||
// original - the first input may not have been compressed at all)
|
||||
comp := bitutil.CompressBytes(blob)
|
||||
if len(comp) > len(blob) {
|
||||
// After compression, it must be smaller or equal
|
||||
panic("bad compression")
|
||||
}
|
||||
// But decompressing it once again should work
|
||||
decomp, err := bitutil.DecompressBytes(data, 1024)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !bytes.Equal(decomp, blob) {
|
||||
panic("content mismatch")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
// Copyright 2019 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package keystore
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||||
)
|
||||
|
||||
func fuzz(input []byte) int {
|
||||
ks := keystore.NewKeyStore("/tmp/ks", keystore.LightScryptN, keystore.LightScryptP)
|
||||
|
||||
a, err := ks.NewAccount(string(input))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if err := ks.Unlock(a, string(input)); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
os.Remove(a.URL.Path)
|
||||
return 1
|
||||
}
|
||||
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
Ë€€€À€ÀÃÀÀÀÀ
|
||||
|
|
@ -1,2 +0,0 @@
|
|||
øNƒ“à€€€‚
|
||||
• <EFBFBD>aùËåÀP?-'´{ÏЋD<>Y¯<1E>³fÆj\ÿE÷ ~ì<>•ÒçF?1(íij6<6A>@Év±LÀÝÚ‘‘
|
||||
|
|
@ -1,143 +0,0 @@
|
|||
// Copyright 2019 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package rlp
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
func decodeEncode(input []byte, val interface{}, i int) {
|
||||
if err := rlp.DecodeBytes(input, val); err == nil {
|
||||
output, err := rlp.EncodeToBytes(val)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if !bytes.Equal(input, output) {
|
||||
panic(fmt.Sprintf("case %d: encode-decode is not equal, \ninput : %x\noutput: %x", i, input, output))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fuzz(input []byte) int {
|
||||
if len(input) == 0 {
|
||||
return 0
|
||||
}
|
||||
if len(input) > 500*1024 {
|
||||
return 0
|
||||
}
|
||||
|
||||
var i int
|
||||
{
|
||||
rlp.Split(input)
|
||||
}
|
||||
{
|
||||
if elems, _, err := rlp.SplitList(input); err == nil {
|
||||
rlp.CountValues(elems)
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
rlp.NewStream(bytes.NewReader(input), 0).Decode(new(interface{}))
|
||||
}
|
||||
|
||||
{
|
||||
decodeEncode(input, new(interface{}), i)
|
||||
i++
|
||||
}
|
||||
{
|
||||
var v struct {
|
||||
Int uint
|
||||
String string
|
||||
Bytes []byte
|
||||
}
|
||||
decodeEncode(input, &v, i)
|
||||
i++
|
||||
}
|
||||
|
||||
{
|
||||
type Types struct {
|
||||
Bool bool
|
||||
Raw rlp.RawValue
|
||||
Slice []*Types
|
||||
Iface []interface{}
|
||||
}
|
||||
var v Types
|
||||
decodeEncode(input, &v, i)
|
||||
i++
|
||||
}
|
||||
{
|
||||
type AllTypes struct {
|
||||
Int uint
|
||||
String string
|
||||
Bytes []byte
|
||||
Bool bool
|
||||
Raw rlp.RawValue
|
||||
Slice []*AllTypes
|
||||
Array [3]*AllTypes
|
||||
Iface []interface{}
|
||||
}
|
||||
var v AllTypes
|
||||
decodeEncode(input, &v, i)
|
||||
i++
|
||||
}
|
||||
{
|
||||
decodeEncode(input, [10]byte{}, i)
|
||||
i++
|
||||
}
|
||||
{
|
||||
var v struct {
|
||||
Byte [10]byte
|
||||
Rool [10]bool
|
||||
}
|
||||
decodeEncode(input, &v, i)
|
||||
i++
|
||||
}
|
||||
{
|
||||
var h types.Header
|
||||
decodeEncode(input, &h, i)
|
||||
i++
|
||||
var b types.Block
|
||||
decodeEncode(input, &b, i)
|
||||
i++
|
||||
var t types.Transaction
|
||||
decodeEncode(input, &t, i)
|
||||
i++
|
||||
var txs types.Transactions
|
||||
decodeEncode(input, &txs, i)
|
||||
i++
|
||||
var rs types.Receipts
|
||||
decodeEncode(input, &rs, i)
|
||||
}
|
||||
{
|
||||
i++
|
||||
var v struct {
|
||||
AnIntPtr *big.Int
|
||||
AnInt big.Int
|
||||
AnU256Ptr *uint256.Int
|
||||
AnU256 uint256.Int
|
||||
NotAnU256 [4]uint64
|
||||
}
|
||||
decodeEncode(input, &v, i)
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
// Copyright 2023 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package snap
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/eth/protocols/snap"
|
||||
)
|
||||
|
||||
func FuzzARange(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
doFuzz(data, &snap.GetAccountRangePacket{}, snap.GetAccountRangeMsg)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzSRange(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
doFuzz(data, &snap.GetStorageRangesPacket{}, snap.GetStorageRangesMsg)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzByteCodes(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
doFuzz(data, &snap.GetByteCodesPacket{}, snap.GetByteCodesMsg)
|
||||
})
|
||||
}
|
||||
|
||||
func FuzzTrieNodes(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
doFuzz(data, &snap.GetTrieNodesPacket{}, snap.GetTrieNodesMsg)
|
||||
})
|
||||
}
|
||||
|
|
@ -1,248 +0,0 @@
|
|||
// Copyright 2020 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package stacktrie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"hash"
|
||||
"io"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type fuzzer struct {
|
||||
input io.Reader
|
||||
exhausted bool
|
||||
debugging bool
|
||||
}
|
||||
|
||||
func (f *fuzzer) read(size int) []byte {
|
||||
out := make([]byte, size)
|
||||
if _, err := f.input.Read(out); err != nil {
|
||||
f.exhausted = true
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (f *fuzzer) readSlice(min, max int) []byte {
|
||||
var a uint16
|
||||
binary.Read(f.input, binary.LittleEndian, &a)
|
||||
size := min + int(a)%(max-min)
|
||||
out := make([]byte, size)
|
||||
if _, err := f.input.Read(out); err != nil {
|
||||
f.exhausted = true
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// spongeDb is a dummy db backend which accumulates writes in a sponge
|
||||
type spongeDb struct {
|
||||
sponge hash.Hash
|
||||
debug bool
|
||||
}
|
||||
|
||||
func (s *spongeDb) Has(key []byte) (bool, error) { panic("implement me") }
|
||||
func (s *spongeDb) Get(key []byte) ([]byte, error) { return nil, errors.New("no such elem") }
|
||||
func (s *spongeDb) Delete(key []byte) error { panic("implement me") }
|
||||
func (s *spongeDb) NewBatch() ethdb.Batch { return &spongeBatch{s} }
|
||||
func (s *spongeDb) NewBatchWithSize(size int) ethdb.Batch { return &spongeBatch{s} }
|
||||
func (s *spongeDb) NewSnapshot() (ethdb.Snapshot, error) { panic("implement me") }
|
||||
func (s *spongeDb) Stat(property string) (string, error) { panic("implement me") }
|
||||
func (s *spongeDb) Compact(start []byte, limit []byte) error { panic("implement me") }
|
||||
func (s *spongeDb) Close() error { return nil }
|
||||
|
||||
func (s *spongeDb) Put(key []byte, value []byte) error {
|
||||
if s.debug {
|
||||
fmt.Printf("db.Put %x : %x\n", key, value)
|
||||
}
|
||||
s.sponge.Write(key)
|
||||
s.sponge.Write(value)
|
||||
return nil
|
||||
}
|
||||
func (s *spongeDb) NewIterator(prefix []byte, start []byte) ethdb.Iterator { panic("implement me") }
|
||||
|
||||
// spongeBatch is a dummy batch which immediately writes to the underlying spongedb
|
||||
type spongeBatch struct {
|
||||
db *spongeDb
|
||||
}
|
||||
|
||||
func (b *spongeBatch) Put(key, value []byte) error {
|
||||
b.db.Put(key, value)
|
||||
return nil
|
||||
}
|
||||
func (b *spongeBatch) Delete(key []byte) error { panic("implement me") }
|
||||
func (b *spongeBatch) ValueSize() int { return 100 }
|
||||
func (b *spongeBatch) Write() error { return nil }
|
||||
func (b *spongeBatch) Reset() {}
|
||||
func (b *spongeBatch) Replay(w ethdb.KeyValueWriter) error { return nil }
|
||||
|
||||
type kv struct {
|
||||
k, v []byte
|
||||
}
|
||||
|
||||
// Fuzz is the fuzzing entry-point.
|
||||
// The function must return
|
||||
//
|
||||
// - 1 if the fuzzer should increase priority of the
|
||||
// given input during subsequent fuzzing (for example, the input is lexically
|
||||
// correct and was parsed successfully);
|
||||
// - -1 if the input must not be added to corpus even if gives new coverage; and
|
||||
// - 0 otherwise
|
||||
//
|
||||
// other values are reserved for future use.
|
||||
func fuzz(data []byte) int {
|
||||
f := fuzzer{
|
||||
input: bytes.NewReader(data),
|
||||
exhausted: false,
|
||||
}
|
||||
return f.fuzz()
|
||||
}
|
||||
|
||||
func Debug(data []byte) int {
|
||||
f := fuzzer{
|
||||
input: bytes.NewReader(data),
|
||||
exhausted: false,
|
||||
debugging: true,
|
||||
}
|
||||
return f.fuzz()
|
||||
}
|
||||
|
||||
func (f *fuzzer) fuzz() int {
|
||||
// This spongeDb is used to check the sequence of disk-db-writes
|
||||
var (
|
||||
spongeA = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
|
||||
dbA = trie.NewDatabase(rawdb.NewDatabase(spongeA), nil)
|
||||
trieA = trie.NewEmpty(dbA)
|
||||
spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
|
||||
dbB = trie.NewDatabase(rawdb.NewDatabase(spongeB), nil)
|
||||
|
||||
options = trie.NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||
rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme())
|
||||
})
|
||||
trieB = trie.NewStackTrie(options)
|
||||
vals []kv
|
||||
useful bool
|
||||
maxElements = 10000
|
||||
// operate on unique keys only
|
||||
keys = make(map[string]struct{})
|
||||
)
|
||||
// Fill the trie with elements
|
||||
for i := 0; !f.exhausted && i < maxElements; i++ {
|
||||
k := f.read(32)
|
||||
v := f.readSlice(1, 500)
|
||||
if f.exhausted {
|
||||
// If it was exhausted while reading, the value may be all zeroes,
|
||||
// thus 'deletion' which is not supported on stacktrie
|
||||
break
|
||||
}
|
||||
if _, present := keys[string(k)]; present {
|
||||
// This key is a duplicate, ignore it
|
||||
continue
|
||||
}
|
||||
keys[string(k)] = struct{}{}
|
||||
vals = append(vals, kv{k: k, v: v})
|
||||
trieA.MustUpdate(k, v)
|
||||
useful = true
|
||||
}
|
||||
if !useful {
|
||||
return 0
|
||||
}
|
||||
// Flush trie -> database
|
||||
rootA, nodes, err := trieA.Commit(false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if nodes != nil {
|
||||
dbA.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
|
||||
}
|
||||
// Flush memdb -> disk (sponge)
|
||||
dbA.Commit(rootA, false)
|
||||
|
||||
// Stacktrie requires sorted insertion
|
||||
slices.SortFunc(vals, func(a, b kv) int {
|
||||
return bytes.Compare(a.k, b.k)
|
||||
})
|
||||
for _, kv := range vals {
|
||||
if f.debugging {
|
||||
fmt.Printf("{\"%#x\" , \"%#x\"} // stacktrie.Update\n", kv.k, kv.v)
|
||||
}
|
||||
trieB.MustUpdate(kv.k, kv.v)
|
||||
}
|
||||
rootB := trieB.Hash()
|
||||
trieB.Commit()
|
||||
if rootA != rootB {
|
||||
panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootB))
|
||||
}
|
||||
sumA := spongeA.sponge.Sum(nil)
|
||||
sumB := spongeB.sponge.Sum(nil)
|
||||
if !bytes.Equal(sumA, sumB) {
|
||||
panic(fmt.Sprintf("sequence differ: (trie) %x != %x (stacktrie)", sumA, sumB))
|
||||
}
|
||||
|
||||
// Ensure all the nodes are persisted correctly
|
||||
var (
|
||||
nodeset = make(map[string][]byte) // path -> blob
|
||||
optionsC = trie.NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||
if crypto.Keccak256Hash(blob) != hash {
|
||||
panic("invalid node blob")
|
||||
}
|
||||
nodeset[string(path)] = common.CopyBytes(blob)
|
||||
})
|
||||
trieC = trie.NewStackTrie(optionsC)
|
||||
checked int
|
||||
)
|
||||
for _, kv := range vals {
|
||||
trieC.MustUpdate(kv.k, kv.v)
|
||||
}
|
||||
rootC := trieC.Commit()
|
||||
if rootA != rootC {
|
||||
panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootC))
|
||||
}
|
||||
trieA, _ = trie.New(trie.TrieID(rootA), dbA)
|
||||
iterA := trieA.MustNodeIterator(nil)
|
||||
for iterA.Next(true) {
|
||||
if iterA.Hash() == (common.Hash{}) {
|
||||
if _, present := nodeset[string(iterA.Path())]; present {
|
||||
panic("unexpected tiny node")
|
||||
}
|
||||
continue
|
||||
}
|
||||
nodeBlob, present := nodeset[string(iterA.Path())]
|
||||
if !present {
|
||||
panic("missing node")
|
||||
}
|
||||
if !bytes.Equal(nodeBlob, iterA.NodeBlob()) {
|
||||
panic("node blob is not matched")
|
||||
}
|
||||
checked += 1
|
||||
}
|
||||
if checked != len(nodeset) {
|
||||
panic("node number is not matched")
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright 2023 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package stacktrie
|
||||
|
||||
import "testing"
|
||||
|
||||
func Fuzz(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
fuzz(data)
|
||||
})
|
||||
}
|
||||
|
|
@ -1,201 +0,0 @@
|
|||
// Copyright 2019 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package trie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
)
|
||||
|
||||
// randTest performs random trie operations.
|
||||
// Instances of this test are created by Generate.
|
||||
type randTest []randTestStep
|
||||
|
||||
type randTestStep struct {
|
||||
op int
|
||||
key []byte // for opUpdate, opDelete, opGet
|
||||
value []byte // for opUpdate
|
||||
err error // for debugging
|
||||
}
|
||||
|
||||
type proofDb struct{}
|
||||
|
||||
func (proofDb) Put(key []byte, value []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (proofDb) Delete(key []byte) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
const (
|
||||
opUpdate = iota
|
||||
opDelete
|
||||
opGet
|
||||
opHash
|
||||
opCommit
|
||||
opItercheckhash
|
||||
opProve
|
||||
opMax // boundary value, not an actual op
|
||||
)
|
||||
|
||||
type dataSource struct {
|
||||
input []byte
|
||||
reader *bytes.Reader
|
||||
}
|
||||
|
||||
func newDataSource(input []byte) *dataSource {
|
||||
return &dataSource{
|
||||
input, bytes.NewReader(input),
|
||||
}
|
||||
}
|
||||
func (ds *dataSource) readByte() byte {
|
||||
if b, err := ds.reader.ReadByte(); err != nil {
|
||||
return 0
|
||||
} else {
|
||||
return b
|
||||
}
|
||||
}
|
||||
func (ds *dataSource) Read(buf []byte) (int, error) {
|
||||
return ds.reader.Read(buf)
|
||||
}
|
||||
func (ds *dataSource) Ended() bool {
|
||||
return ds.reader.Len() == 0
|
||||
}
|
||||
|
||||
func Generate(input []byte) randTest {
|
||||
var allKeys [][]byte
|
||||
r := newDataSource(input)
|
||||
genKey := func() []byte {
|
||||
if len(allKeys) < 2 || r.readByte() < 0x0f {
|
||||
// new key
|
||||
key := make([]byte, r.readByte()%50)
|
||||
r.Read(key)
|
||||
allKeys = append(allKeys, key)
|
||||
return key
|
||||
}
|
||||
// use existing key
|
||||
return allKeys[int(r.readByte())%len(allKeys)]
|
||||
}
|
||||
|
||||
var steps randTest
|
||||
|
||||
for i := 0; !r.Ended(); i++ {
|
||||
step := randTestStep{op: int(r.readByte()) % opMax}
|
||||
switch step.op {
|
||||
case opUpdate:
|
||||
step.key = genKey()
|
||||
step.value = make([]byte, 8)
|
||||
binary.BigEndian.PutUint64(step.value, uint64(i))
|
||||
case opGet, opDelete, opProve:
|
||||
step.key = genKey()
|
||||
}
|
||||
steps = append(steps, step)
|
||||
if len(steps) > 500 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return steps
|
||||
}
|
||||
|
||||
// Fuzz is the fuzzing entry-point.
|
||||
// The function must return
|
||||
//
|
||||
// - 1 if the fuzzer should increase priority of the
|
||||
// given input during subsequent fuzzing (for example, the input is lexically
|
||||
// correct and was parsed successfully);
|
||||
// - -1 if the input must not be added to corpus even if gives new coverage; and
|
||||
// - 0 otherwise
|
||||
//
|
||||
// other values are reserved for future use.
|
||||
func fuzz(input []byte) int {
|
||||
program := Generate(input)
|
||||
if len(program) == 0 {
|
||||
return 0
|
||||
}
|
||||
if err := runRandTest(program); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
func runRandTest(rt randTest) error {
|
||||
var (
|
||||
triedb = trie.NewDatabase(rawdb.NewMemoryDatabase(), nil)
|
||||
tr = trie.NewEmpty(triedb)
|
||||
origin = types.EmptyRootHash
|
||||
values = make(map[string]string) // tracks content of the trie
|
||||
)
|
||||
for i, step := range rt {
|
||||
switch step.op {
|
||||
case opUpdate:
|
||||
tr.MustUpdate(step.key, step.value)
|
||||
values[string(step.key)] = string(step.value)
|
||||
case opDelete:
|
||||
tr.MustDelete(step.key)
|
||||
delete(values, string(step.key))
|
||||
case opGet:
|
||||
v := tr.MustGet(step.key)
|
||||
want := values[string(step.key)]
|
||||
if string(v) != want {
|
||||
rt[i].err = fmt.Errorf("mismatch for key %#x, got %#x want %#x", step.key, v, want)
|
||||
}
|
||||
case opHash:
|
||||
tr.Hash()
|
||||
case opCommit:
|
||||
hash, nodes, err := tr.Commit(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if nodes != nil {
|
||||
if err := triedb.Update(hash, origin, 0, trienode.NewWithNodeSet(nodes), nil); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
newtr, err := trie.New(trie.TrieID(hash), triedb)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tr = newtr
|
||||
origin = hash
|
||||
case opItercheckhash:
|
||||
checktr := trie.NewEmpty(triedb)
|
||||
it := trie.NewIterator(tr.MustNodeIterator(nil))
|
||||
for it.Next() {
|
||||
checktr.MustUpdate(it.Key, it.Value)
|
||||
}
|
||||
if tr.Hash() != checktr.Hash() {
|
||||
return errors.New("hash mismatch in opItercheckhash")
|
||||
}
|
||||
case opProve:
|
||||
rt[i].err = tr.Prove(step.key, proofDb{})
|
||||
}
|
||||
// Abort the test on error.
|
||||
if rt[i].err != nil {
|
||||
return rt[i].err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
@ -1,25 +0,0 @@
|
|||
// Copyright 2023 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package trie
|
||||
|
||||
import "testing"
|
||||
|
||||
func Fuzz(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
fuzz(data)
|
||||
})
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
155
trie/stacktrie_fuzzer_test.go
Normal file
155
trie/stacktrie_fuzzer_test.go
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
// Copyright 2020 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package trie
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||
"golang.org/x/crypto/sha3"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func FuzzStackTrie(f *testing.F) {
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
fuzz(data, false)
|
||||
})
|
||||
}
|
||||
|
||||
func fuzz(data []byte, debugging bool) {
|
||||
// This spongeDb is used to check the sequence of disk-db-writes
|
||||
var (
|
||||
input = bytes.NewReader(data)
|
||||
spongeA = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
|
||||
dbA = NewDatabase(rawdb.NewDatabase(spongeA), nil)
|
||||
trieA = NewEmpty(dbA)
|
||||
spongeB = &spongeDb{sponge: sha3.NewLegacyKeccak256()}
|
||||
dbB = NewDatabase(rawdb.NewDatabase(spongeB), nil)
|
||||
|
||||
options = NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||
rawdb.WriteTrieNode(spongeB, common.Hash{}, path, hash, blob, dbB.Scheme())
|
||||
})
|
||||
trieB = NewStackTrie(options)
|
||||
vals []*kv
|
||||
maxElements = 10000
|
||||
// operate on unique keys only
|
||||
keys = make(map[string]struct{})
|
||||
)
|
||||
// Fill the trie with elements
|
||||
for i := 0; input.Len() > 0 && i < maxElements; i++ {
|
||||
k := make([]byte, 32)
|
||||
input.Read(k)
|
||||
var a uint16
|
||||
binary.Read(input, binary.LittleEndian, &a)
|
||||
a = 1 + a%100
|
||||
v := make([]byte, a)
|
||||
input.Read(v)
|
||||
if input.Len() == 0 {
|
||||
// If it was exhausted while reading, the value may be all zeroes,
|
||||
// thus 'deletion' which is not supported on stacktrie
|
||||
break
|
||||
}
|
||||
if _, present := keys[string(k)]; present {
|
||||
// This key is a duplicate, ignore it
|
||||
continue
|
||||
}
|
||||
keys[string(k)] = struct{}{}
|
||||
vals = append(vals, &kv{k: k, v: v})
|
||||
trieA.MustUpdate(k, v)
|
||||
}
|
||||
if len(vals) == 0 {
|
||||
return
|
||||
}
|
||||
// Flush trie -> database
|
||||
rootA, nodes, err := trieA.Commit(false)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if nodes != nil {
|
||||
dbA.Update(rootA, types.EmptyRootHash, 0, trienode.NewWithNodeSet(nodes), nil)
|
||||
}
|
||||
// Flush memdb -> disk (sponge)
|
||||
dbA.Commit(rootA, false)
|
||||
|
||||
// Stacktrie requires sorted insertion
|
||||
slices.SortFunc(vals, (*kv).cmp)
|
||||
|
||||
for _, kv := range vals {
|
||||
if debugging {
|
||||
fmt.Printf("{\"%#x\" , \"%#x\"} // stacktrie.Update\n", kv.k, kv.v)
|
||||
}
|
||||
trieB.MustUpdate(kv.k, kv.v)
|
||||
}
|
||||
rootB := trieB.Hash()
|
||||
trieB.Commit()
|
||||
if rootA != rootB {
|
||||
panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootB))
|
||||
}
|
||||
sumA := spongeA.sponge.Sum(nil)
|
||||
sumB := spongeB.sponge.Sum(nil)
|
||||
if !bytes.Equal(sumA, sumB) {
|
||||
panic(fmt.Sprintf("sequence differ: (trie) %x != %x (stacktrie)", sumA, sumB))
|
||||
}
|
||||
|
||||
// Ensure all the nodes are persisted correctly
|
||||
var (
|
||||
nodeset = make(map[string][]byte) // path -> blob
|
||||
optionsC = NewStackTrieOptions().WithWriter(func(path []byte, hash common.Hash, blob []byte) {
|
||||
if crypto.Keccak256Hash(blob) != hash {
|
||||
panic("invalid node blob")
|
||||
}
|
||||
nodeset[string(path)] = common.CopyBytes(blob)
|
||||
})
|
||||
trieC = NewStackTrie(optionsC)
|
||||
checked int
|
||||
)
|
||||
for _, kv := range vals {
|
||||
trieC.MustUpdate(kv.k, kv.v)
|
||||
}
|
||||
rootC := trieC.Commit()
|
||||
if rootA != rootC {
|
||||
panic(fmt.Sprintf("roots differ: (trie) %x != %x (stacktrie)", rootA, rootC))
|
||||
}
|
||||
trieA, _ = New(TrieID(rootA), dbA)
|
||||
iterA := trieA.MustNodeIterator(nil)
|
||||
for iterA.Next(true) {
|
||||
if iterA.Hash() == (common.Hash{}) {
|
||||
if _, present := nodeset[string(iterA.Path())]; present {
|
||||
panic("unexpected tiny node")
|
||||
}
|
||||
continue
|
||||
}
|
||||
nodeBlob, present := nodeset[string(iterA.Path())]
|
||||
if !present {
|
||||
panic("missing node")
|
||||
}
|
||||
if !bytes.Equal(nodeBlob, iterA.NodeBlob()) {
|
||||
panic("node blob is not matched")
|
||||
}
|
||||
checked += 1
|
||||
}
|
||||
if checked != len(nodeset) {
|
||||
panic("node number is not matched")
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
342
trie/utils/verkle.go
Normal file
342
trie/utils/verkle.go
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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<codeStorageDelta.
|
||||
key.Add(headerStorageOffset, &key)
|
||||
|
||||
// In this branch, the tree-index is zero since we're in the account header,
|
||||
// and the sub-index is the LSB of the modified storage key.
|
||||
return zero, byte(key[0] & 0xFF)
|
||||
}
|
||||
// We first divide by VerkleNodeWidth to create room to avoid an overflow next.
|
||||
key.Rsh(&key, uint(verkleNodeWidthLog2))
|
||||
|
||||
// We add mainStorageOffset/VerkleNodeWidth which can't overflow.
|
||||
key.Add(&key, mainStorageOffsetLshVerkleNodeWidth)
|
||||
|
||||
// The sub-index is the LSB of the original storage key, since mainStorageOffset
|
||||
// doesn't affect this byte, so we can avoid masks or shifts.
|
||||
return &key, byte(key[0] & 0xFF)
|
||||
}
|
||||
|
||||
// StorageSlotKey returns the verkle tree key of the storage slot for the
|
||||
// specified account.
|
||||
func StorageSlotKey(address []byte, storageKey []byte) []byte {
|
||||
treeIndex, subIndex := storageIndex(storageKey)
|
||||
return GetTreeKey(address, treeIndex, subIndex)
|
||||
}
|
||||
|
||||
// VersionKeyWithEvaluatedAddress returns the verkle tree key of the version
|
||||
// field for the specified account. The difference between VersionKey is the
|
||||
// address evaluation is already computed to minimize the computational overhead.
|
||||
func VersionKeyWithEvaluatedAddress(evaluated *verkle.Point) []byte {
|
||||
return GetTreeKeyWithEvaluatedAddress(evaluated, zero, VersionLeafKey)
|
||||
}
|
||||
|
||||
// BalanceKeyWithEvaluatedAddress returns the verkle tree key of the balance
|
||||
// field for the specified account. The difference between BalanceKey is the
|
||||
// address evaluation is already computed to minimize the computational overhead.
|
||||
func BalanceKeyWithEvaluatedAddress(evaluated *verkle.Point) []byte {
|
||||
return GetTreeKeyWithEvaluatedAddress(evaluated, zero, BalanceLeafKey)
|
||||
}
|
||||
|
||||
// NonceKeyWithEvaluatedAddress returns the verkle tree key of the nonce
|
||||
// field for the specified account. The difference between NonceKey is the
|
||||
// address evaluation is already computed to minimize the computational overhead.
|
||||
func NonceKeyWithEvaluatedAddress(evaluated *verkle.Point) []byte {
|
||||
return GetTreeKeyWithEvaluatedAddress(evaluated, zero, NonceLeafKey)
|
||||
}
|
||||
|
||||
// CodeKeccakKeyWithEvaluatedAddress returns the verkle tree key of the code
|
||||
// keccak for the specified account. The difference between CodeKeccakKey is the
|
||||
// address evaluation is already computed to minimize the computational overhead.
|
||||
func CodeKeccakKeyWithEvaluatedAddress(evaluated *verkle.Point) []byte {
|
||||
return GetTreeKeyWithEvaluatedAddress(evaluated, zero, CodeKeccakLeafKey)
|
||||
}
|
||||
|
||||
// CodeSizeKeyWithEvaluatedAddress returns the verkle tree key of the code
|
||||
// size for the specified account. The difference between CodeSizeKey is the
|
||||
// address evaluation is already computed to minimize the computational overhead.
|
||||
func CodeSizeKeyWithEvaluatedAddress(evaluated *verkle.Point) []byte {
|
||||
return GetTreeKeyWithEvaluatedAddress(evaluated, zero, CodeSizeLeafKey)
|
||||
}
|
||||
|
||||
// CodeChunkKeyWithEvaluatedAddress returns the verkle tree key of the code
|
||||
// chunk for the specified account. The difference between CodeChunkKey is the
|
||||
// address evaluation is already computed to minimize the computational overhead.
|
||||
func CodeChunkKeyWithEvaluatedAddress(addressPoint *verkle.Point, chunk *uint256.Int) []byte {
|
||||
treeIndex, subIndex := codeChunkIndex(chunk)
|
||||
return GetTreeKeyWithEvaluatedAddress(addressPoint, treeIndex, subIndex)
|
||||
}
|
||||
|
||||
// StorageSlotKeyWithEvaluatedAddress returns the verkle tree key of the storage
|
||||
// slot for the specified account. The difference between StorageSlotKey is the
|
||||
// address evaluation is already computed to minimize the computational overhead.
|
||||
func StorageSlotKeyWithEvaluatedAddress(evaluated *verkle.Point, storageKey []byte) []byte {
|
||||
treeIndex, subIndex := storageIndex(storageKey)
|
||||
return GetTreeKeyWithEvaluatedAddress(evaluated, treeIndex, subIndex)
|
||||
}
|
||||
|
||||
func pointToHash(evaluated *verkle.Point, suffix byte) []byte {
|
||||
// The output of Byte() is big endian for banderwagon. This
|
||||
// introduces an imbalance in the tree, because hashes are
|
||||
// elements of a 253-bit field. This means more than half the
|
||||
// tree would be empty. To avoid this problem, use a little
|
||||
// endian commitment and chop the MSB.
|
||||
bytes := evaluated.Bytes()
|
||||
for i := 0; i < 16; i++ {
|
||||
bytes[31-i], bytes[i] = bytes[i], bytes[31-i]
|
||||
}
|
||||
bytes[31] = suffix
|
||||
return bytes[:]
|
||||
}
|
||||
|
||||
func evaluateAddressPoint(address []byte) *verkle.Point {
|
||||
if len(address) < 32 {
|
||||
var aligned [32]byte
|
||||
address = append(aligned[:32-len(address)], address...)
|
||||
}
|
||||
var poly [3]fr.Element
|
||||
|
||||
poly[0].SetZero()
|
||||
|
||||
// 32-byte address, interpreted as two little endian
|
||||
// 16-byte numbers.
|
||||
verkle.FromLEBytes(&poly[1], address[:16])
|
||||
verkle.FromLEBytes(&poly[2], address[16:])
|
||||
|
||||
cfg := verkle.GetConfig()
|
||||
ret := cfg.CommitToPoly(poly[:], 0)
|
||||
|
||||
// add a constant point
|
||||
ret.Add(ret, index0Point)
|
||||
return ret
|
||||
}
|
||||
139
trie/utils/verkle_test.go
Normal file
139
trie/utils/verkle_test.go
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
// 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 <http://www.gnu.org/licenses/>
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
375
trie/verkle.go
Normal file
375
trie/verkle.go
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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{})
|
||||
}
|
||||
97
trie/verkle_test.go
Normal file
97
trie/verkle_test.go
Normal file
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue