mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-11 06:23:47 +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"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
|
||||||
fuzz "github.com/google/gofuzz"
|
fuzz "github.com/google/gofuzz"
|
||||||
)
|
)
|
||||||
|
|
||||||
// TestReplicate can be used to replicate crashers from the fuzzing tests.
|
// TestReplicate can be used to replicate crashers from the fuzzing tests.
|
||||||
// Just replace testString with the data in .quoted
|
// Just replace testString with the data in .quoted
|
||||||
func TestReplicate(t *testing.T) {
|
func TestReplicate(t *testing.T) {
|
||||||
testString := "\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00"
|
//t.Skip("Test only useful for reproducing issues")
|
||||||
data := []byte(testString)
|
fuzzAbi([]byte("\x20\x20\x20\x20\x20\x20\x20\x20\x80\x00\x00\x00\x20\x20\x20\x20\x00"))
|
||||||
fuzzAbi(data)
|
//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) {
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
fuzzAbi(data)
|
fuzzAbi(data)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
names = []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"}
|
names = []string{"_name", "name", "NAME", "name_", "__", "_name_", "n"}
|
||||||
stateMut = []string{"", "pure", "view", "payable"}
|
stateMut = []string{"pure", "view", "payable"}
|
||||||
stateMutabilites = []*string{&stateMut[0], &stateMut[1], &stateMut[2], &stateMut[3]}
|
pays = []string{"true", "false"}
|
||||||
pays = []string{"", "true", "false"}
|
vNames = []string{"a", "b", "c", "d", "e", "f", "g"}
|
||||||
payables = []*string{&pays[0], &pays[1]}
|
varNames = append(vNames, names...)
|
||||||
vNames = []string{"a", "b", "c", "d", "e", "f", "g"}
|
varTypes = []string{"bool", "address", "bytes", "string",
|
||||||
varNames = append(vNames, names...)
|
|
||||||
varTypes = []string{"bool", "address", "bytes", "string",
|
|
||||||
"uint8", "int8", "uint8", "int8", "uint16", "int16",
|
"uint8", "int8", "uint8", "int8", "uint16", "int16",
|
||||||
"uint24", "int24", "uint32", "int32", "uint40", "int40", "uint48", "int48", "uint56", "int56",
|
"uint24", "int24", "uint32", "int32", "uint40", "int40", "uint48", "int48", "uint56", "int56",
|
||||||
"uint64", "int64", "uint72", "int72", "uint80", "int80", "uint88", "int88", "uint96", "int96",
|
"uint64", "int64", "uint72", "int72", "uint80", "int80", "uint88", "int88", "uint96", "int96",
|
||||||
|
|
@ -62,7 +60,7 @@ var (
|
||||||
"bytes32", "bytes"}
|
"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 {
|
if out, err := abi.Unpack(method, input); err == nil {
|
||||||
_, err := abi.Pack(method, out...)
|
_, err := abi.Pack(method, out...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -78,7 +76,7 @@ func unpackPack(abi abi.ABI, method string, input []byte) ([]interface{}, bool)
|
||||||
return nil, false
|
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 {
|
if packed, err := abi.Pack(method, input); err == nil {
|
||||||
outptr := reflect.New(reflect.TypeOf(input))
|
outptr := reflect.New(reflect.TypeOf(input))
|
||||||
err := abi.UnpackIntoInterface(outptr.Interface(), method, packed)
|
err := abi.UnpackIntoInterface(outptr.Interface(), method, packed)
|
||||||
|
|
@ -94,12 +92,12 @@ func packUnpack(abi abi.ABI, method string, input *[]interface{}) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
type args struct {
|
type arg struct {
|
||||||
name string
|
name string
|
||||||
typ 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)
|
sig := fmt.Sprintf(`[{ "type" : "function", "name" : "%v" `, name)
|
||||||
if stateMutability != nil {
|
if stateMutability != nil {
|
||||||
sig += fmt.Sprintf(`, "stateMutability": "%v" `, *stateMutability)
|
sig += fmt.Sprintf(`, "stateMutability": "%v" `, *stateMutability)
|
||||||
|
|
@ -126,56 +124,55 @@ func createABI(name string, stateMutability, payable *string, inputs []args) (ab
|
||||||
sig += "} ]"
|
sig += "} ]"
|
||||||
}
|
}
|
||||||
sig += `}]`
|
sig += `}]`
|
||||||
|
//fmt.Printf("sig: %s\n", sig)
|
||||||
return abi.JSON(strings.NewReader(sig))
|
return JSON(strings.NewReader(sig))
|
||||||
}
|
}
|
||||||
|
|
||||||
func fuzzAbi(input []byte) int {
|
func fuzzAbi(input []byte) {
|
||||||
good := false
|
var (
|
||||||
fuzzer := fuzz.NewFromGoFuzz(input)
|
fuzzer = fuzz.NewFromGoFuzz(input)
|
||||||
|
name = oneOf(fuzzer, names)
|
||||||
name := names[getUInt(fuzzer)%len(names)]
|
stateM = oneOfOrNil(fuzzer, stateMut)
|
||||||
stateM := stateMutabilites[getUInt(fuzzer)%len(stateMutabilites)]
|
payable = oneOfOrNil(fuzzer, pays)
|
||||||
payable := payables[getUInt(fuzzer)%len(payables)]
|
arguments []arg
|
||||||
maxLen := 5
|
)
|
||||||
for k := 1; k < maxLen; k++ {
|
for i := 0; i < upTo(fuzzer, 10); i++ {
|
||||||
var arg []args
|
argName := oneOf(fuzzer, varNames)
|
||||||
for i := k; i > 0; i-- {
|
argTyp := oneOf(fuzzer, varTypes)
|
||||||
argName := varNames[i]
|
switch upTo(fuzzer, 10) {
|
||||||
argTyp := varTypes[getUInt(fuzzer)%len(varTypes)]
|
case 0: // 10% chance to make it a slice
|
||||||
if getUInt(fuzzer)%10 == 0 {
|
argTyp += "[]"
|
||||||
argTyp += "[]"
|
case 1: // 10% chance to make it an array
|
||||||
} else if getUInt(fuzzer)%10 == 0 {
|
argTyp += fmt.Sprintf("[%d]", 1+upTo(fuzzer, 30))
|
||||||
arrayArgs := getUInt(fuzzer)%30 + 1
|
default:
|
||||||
argTyp += fmt.Sprintf("[%d]", arrayArgs)
|
|
||||||
}
|
|
||||||
arg = append(arg, args{
|
|
||||||
name: argName,
|
|
||||||
typ: argTyp,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
abi, err := createABI(name, stateM, payable, arg)
|
arguments = append(arguments, arg{name: argName, typ: argTyp})
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
structs, b := unpackPack(abi, name, input)
|
|
||||||
c := packUnpack(abi, name, &structs)
|
|
||||||
good = good || b || c
|
|
||||||
}
|
}
|
||||||
if good {
|
abi, err := createABI(name, stateM, payable, arguments)
|
||||||
return 1
|
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
|
var i int
|
||||||
fuzzer.Fuzz(&i)
|
fuzzer.Fuzz(&i)
|
||||||
if i < 0 {
|
if i < 0 {
|
||||||
i = -i
|
return (-1 - i) % max
|
||||||
if i < 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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()
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
if len(b.pendingBlock.Transactions()) != 0 {
|
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
|
// Get the last block
|
||||||
block := b.blockchain.GetBlockByHash(b.pendingBlock.ParentHash())
|
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, ","))
|
sig = fmt.Sprintf("%v(%v)", rawName, strings.Join(types, ","))
|
||||||
id = crypto.Keccak256([]byte(sig))[:4]
|
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)
|
identity := fmt.Sprintf("function %v", rawName)
|
||||||
switch funType {
|
switch funType {
|
||||||
case Fallback:
|
case Fallback:
|
||||||
|
|
@ -135,7 +126,14 @@ func NewMethod(name string, rawName string, funType FunctionType, mutability str
|
||||||
case Constructor:
|
case Constructor:
|
||||||
identity = "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{
|
return Method{
|
||||||
Name: name,
|
Name: name,
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ func packElement(t Type, reflectValue reflect.Value) ([]byte, error) {
|
||||||
reflectValue = mustArrayToByteSlice(reflectValue)
|
reflectValue = mustArrayToByteSlice(reflectValue)
|
||||||
}
|
}
|
||||||
if reflectValue.Type() != reflect.TypeOf([]byte{}) {
|
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
|
return packBytesSlice(reflectValue.Bytes(), reflectValue.Len()), nil
|
||||||
case FixedBytesTy, FunctionTy:
|
case FixedBytesTy, FunctionTy:
|
||||||
|
|
@ -66,7 +66,7 @@ func packElement(t Type, reflectValue reflect.Value) ([]byte, error) {
|
||||||
}
|
}
|
||||||
return common.RightPadBytes(reflectValue.Bytes(), 32), nil
|
return common.RightPadBytes(reflectValue.Bytes(), 32), nil
|
||||||
default:
|
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)
|
dst.Set(slice)
|
||||||
return nil
|
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 {
|
func setArray(dst, src reflect.Value) error {
|
||||||
|
|
@ -155,7 +155,7 @@ func setArray(dst, src reflect.Value) error {
|
||||||
dst.Set(array)
|
dst.Set(array)
|
||||||
return nil
|
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 {
|
func setStruct(dst, src reflect.Value) error {
|
||||||
|
|
@ -163,7 +163,7 @@ func setStruct(dst, src reflect.Value) error {
|
||||||
srcField := src.Field(i)
|
srcField := src.Field(i)
|
||||||
dstField := dst.Field(i)
|
dstField := dst.Field(i)
|
||||||
if !dstField.IsValid() || !srcField.IsValid() {
|
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 {
|
if err := set(dstField, srcField); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,19 @@
|
||||||
|
|
||||||
package keystore
|
package keystore
|
||||||
|
|
||||||
import "testing"
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
func Fuzz(f *testing.F) {
|
func FuzzPassword(f *testing.F) {
|
||||||
f.Fuzz(func(t *testing.T, data []byte) {
|
f.Fuzz(func(t *testing.T, password string) {
|
||||||
fuzz(data)
|
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)
|
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)
|
url, path, found := strings.Cut(account.URL.Path, "/")
|
||||||
if len(parts) != 2 {
|
if !found {
|
||||||
return nil, fmt.Errorf("invalid URL format: %s", account.URL)
|
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 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.
|
// Session represents a secured communication session with the wallet.
|
||||||
|
|
|
||||||
|
|
@ -581,6 +581,7 @@ func accountImport(c *cli.Context) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if first != second {
|
if first != second {
|
||||||
|
//lint:ignore ST1005 This is a message for the user
|
||||||
return errors.New("Passwords do not match")
|
return errors.New("Passwords do not match")
|
||||||
}
|
}
|
||||||
acc, err := internalApi.ImportRawKey(hex.EncodeToString(crypto.FromECDSA(pKey)), first)
|
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 {
|
func discv4Test(ctx *cli.Context) error {
|
||||||
// Configure test package globals.
|
// Configure test package globals.
|
||||||
if !ctx.IsSet(remoteEnodeFlag.Name) {
|
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.Remote = ctx.String(remoteEnodeFlag.Name)
|
||||||
v4test.Listen1 = ctx.String(testListen1Flag.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)
|
hash := make([]byte, 32)
|
||||||
trienodes := res.Nodes
|
trienodes := res.Nodes
|
||||||
if got, want := len(trienodes), len(tc.expHashes); got != want {
|
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 {
|
for i, trienode := range trienodes {
|
||||||
hasher.Reset()
|
hasher.Reset()
|
||||||
|
|
|
||||||
|
|
@ -248,7 +248,7 @@ func newFaucet(genesis *core.Genesis, port int, enodes []*enode.Node, network ui
|
||||||
|
|
||||||
lesBackend, err := les.New(stack, &cfg)
|
lesBackend, err := les.New(stack, &cfg)
|
||||||
if err != nil {
|
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'
|
// Assemble the ethstats monitoring and reporting service'
|
||||||
|
|
|
||||||
|
|
@ -211,7 +211,7 @@ func initGenesis(ctx *cli.Context) error {
|
||||||
}
|
}
|
||||||
defer chaindb.Close()
|
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()
|
defer triedb.Close()
|
||||||
|
|
||||||
_, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
_, hash, err := core.SetupGenesisBlockWithOverride(chaindb, triedb, genesis, &overrides)
|
||||||
|
|
@ -485,7 +485,7 @@ func dump(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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()
|
defer triedb.Close()
|
||||||
|
|
||||||
state, err := state.New(root, state.NewDatabaseWithNodeDB(db, triedb), nil)
|
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)
|
db := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
triedb := utils.MakeTrieDatabase(ctx, db, false, true)
|
triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
|
@ -39,6 +40,12 @@ var logTestCommand = &cli.Command{
|
||||||
This command is only meant for testing.
|
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
|
// logTest is an entry point which spits out some logs. This is used by testing
|
||||||
// to verify expected outputs
|
// to verify expected outputs
|
||||||
func logTest(ctx *cli.Context) error {
|
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))
|
log.Info("uint64", "18,446,744,073,709,551,615", uint64(math.MaxUint64))
|
||||||
}
|
}
|
||||||
{ // Special characters
|
{ // Special characters
|
||||||
|
|
||||||
|
|
||||||
log.Info("Special chars in value", "key", "special \r\n\t chars")
|
log.Info("Special chars in value", "key", "special \r\n\t chars")
|
||||||
log.Info("Special chars in key", "special \n\t chars", "value")
|
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)
|
colored := fmt.Sprintf("\u001B[%dmColored\u001B[0m[", 35)
|
||||||
log.Info(colored, colored, colored)
|
log.Info(colored, colored, colored)
|
||||||
|
err := errors.New("this is an 'error'")
|
||||||
|
log.Info("an error message with quotes", "error", err)
|
||||||
}
|
}
|
||||||
{ // Custom Stringer() - type
|
{ // Custom Stringer() - type
|
||||||
log.Info("Custom Stringer value", "2562047h47m16.854s", common.PrettyDuration(time.Duration(9223372036854775807)))
|
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
|
{ // Lazy eval
|
||||||
log.Info("Lazy evaluation of value", "key", log.Lazy{Fn: func() interface{} { return "lazy value" }})
|
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")
|
log.Error("Failed to load head block")
|
||||||
return errors.New("no 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()
|
defer triedb.Close()
|
||||||
|
|
||||||
snapConfig := snapshot.Config{
|
snapConfig := snapshot.Config{
|
||||||
|
|
@ -260,7 +260,7 @@ func traverseState(ctx *cli.Context) error {
|
||||||
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
defer chaindb.Close()
|
defer chaindb.Close()
|
||||||
|
|
||||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
|
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
headBlock := rawdb.ReadHeadBlock(chaindb)
|
headBlock := rawdb.ReadHeadBlock(chaindb)
|
||||||
|
|
@ -369,7 +369,7 @@ func traverseRawState(ctx *cli.Context) error {
|
||||||
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
chaindb := utils.MakeChainDatabase(ctx, stack, true)
|
||||||
defer chaindb.Close()
|
defer chaindb.Close()
|
||||||
|
|
||||||
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true)
|
triedb := utils.MakeTrieDatabase(ctx, chaindb, false, true, false)
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
headBlock := rawdb.ReadHeadBlock(chaindb)
|
headBlock := rawdb.ReadHeadBlock(chaindb)
|
||||||
|
|
@ -533,7 +533,7 @@ func dumpState(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
triedb := utils.MakeTrieDatabase(ctx, db, false, true)
|
triedb := utils.MakeTrieDatabase(ctx, db, false, true, false)
|
||||||
defer triedb.Close()
|
defer triedb.Close()
|
||||||
|
|
||||||
snapConfig := snapshot.Config{
|
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 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="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="\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="an error message with quotes" error="this is an 'error'"
|
||||||
t=xxxxxxxxxxxxxxxxxxxxxxxx lvl=info msg="Lazy evaluation of value" key="lazy value"
|
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 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 \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"
|
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 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] "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] "\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] 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] 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 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 \nINFO [10-18|14:11:31.106] with wonky characters 💩"
|
||||||
INFO [XX-XX|XX:XX:XX.XXX] A multiline message
|
INFO [XX-XX|XX:XX:XX.XXX] A multiline message
|
||||||
LALA [XXZXXZXXZXXZXXZXXX] Actually part of message above
|
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] 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 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] repeated-key 2 xx=short xx=longer
|
||||||
INFO [XX-XX|XX:XX:XX.XXX] log at level info
|
INFO [XX-XX|XX:XX:XX.XXX] log at level info
|
||||||
WARN [XX-XX|XX:XX:XX.XXX] log at level warn
|
WARN [XX-XX|XX:XX:XX.XXX] log at level warn
|
||||||
ERROR[XX-XX|XX:XX:XX.XXX] log at level error
|
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=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="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] 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 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 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
|
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)
|
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
|
// 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 {
|
if err != nil {
|
||||||
return fmt.Errorf("decode error child %x in db: %w", child.Commitment().Bytes(), err)
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
|
root, err := verkle.ParseNode(serializedRoot, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
@ -195,7 +195,7 @@ func expandVerkle(ctx *cli.Context) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
root, err := verkle.ParseNode(serializedRoot, 0, rootC[:])
|
root, err := verkle.ParseNode(serializedRoot, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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 {
|
func rpcSubscribe(client *rpc.Client, out io.Writer, method string, args ...string) error {
|
||||||
parts := strings.SplitN(method, "_", 2)
|
namespace, method, _ := strings.Cut(method, "_")
|
||||||
namespace := parts[0]
|
|
||||||
method = parts[1]
|
|
||||||
ch := make(chan interface{})
|
ch := make(chan interface{})
|
||||||
subArgs := make([]interface{}, len(args)+1)
|
subArgs := make([]interface{}, len(args)+1)
|
||||||
subArgs[0] = method
|
subArgs[0] = method
|
||||||
|
|
|
||||||
|
|
@ -460,7 +460,7 @@ func ImportLDBData(db ethdb.Database, f string, startIndex int64, interrupt chan
|
||||||
case OpBatchAdd:
|
case OpBatchAdd:
|
||||||
batch.Put(key, val)
|
batch.Put(key, val)
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unknown op %d\n", op)
|
return fmt.Errorf("unknown op %d", op)
|
||||||
}
|
}
|
||||||
if batch.ValueSize() > ethdb.IdealBatchSize {
|
if batch.ValueSize() > ethdb.IdealBatchSize {
|
||||||
if err := batch.Write(); err != nil {
|
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.
|
// 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{
|
config := &trie.Config{
|
||||||
Preimages: preimage,
|
Preimages: preimage,
|
||||||
|
IsVerkle: isVerkle,
|
||||||
}
|
}
|
||||||
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), disk)
|
scheme, err := rawdb.ParseStateScheme(ctx.String(StateSchemeFlag.Name), disk)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package bitutil
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
|
@ -48,19 +49,23 @@ func TestEncodingCycle(t *testing.T) {
|
||||||
"0xdf7070533534333636313639343638373532313536346c1bc333393438373130707063363430353639343638373532313536346c1bc333393438336336346c65fe",
|
"0xdf7070533534333636313639343638373532313536346c1bc333393438373130707063363430353639343638373532313536346c1bc333393438336336346c65fe",
|
||||||
}
|
}
|
||||||
for i, tt := range tests {
|
for i, tt := range tests {
|
||||||
data := hexutil.MustDecode(tt)
|
if err := testEncodingCycle(hexutil.MustDecode(tt)); err != nil {
|
||||||
|
t.Errorf("test %d: %v", i, err)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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.
|
// Tests that data bitset decoding and rencoding works and is bijective.
|
||||||
func TestDecodingCycle(t *testing.T) {
|
func TestDecodingCycle(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
|
|
@ -179,3 +184,40 @@ func benchmarkEncoding(b *testing.B, bytes int, fill float64) {
|
||||||
bitsetDecodeBytes(bitsetEncodeBytes(data), len(data))
|
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
|
return nil, err
|
||||||
}
|
}
|
||||||
if password != confirm {
|
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
|
// A single string password was specified, use that
|
||||||
case len(call.Arguments) == 1 && call.Argument(0).ToString() != nil:
|
case len(call.Arguments) == 1 && call.Argument(0).ToString() != nil:
|
||||||
|
|
|
||||||
|
|
@ -22,53 +22,37 @@ import (
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Tests disassembling the instructions for valid evm code
|
// Tests disassembling instructions
|
||||||
func TestInstructionIteratorValid(t *testing.T) {
|
func TestInstructionIterator(t *testing.T) {
|
||||||
cnt := 0
|
for i, tc := range []struct {
|
||||||
script, _ := hex.DecodeString("61000000")
|
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() {
|
var (
|
||||||
cnt++
|
have int
|
||||||
}
|
code, _ = hex.DecodeString(tc.code)
|
||||||
|
it = NewInstructionIterator(code)
|
||||||
if err := it.Error(); err != nil {
|
)
|
||||||
t.Errorf("Expected 2, but encountered error %v instead.", err)
|
for it.Next() {
|
||||||
}
|
have++
|
||||||
if cnt != 2 {
|
}
|
||||||
t.Errorf("Expected 2, but got %v instead.", cnt)
|
var haveErr = ""
|
||||||
}
|
if it.Error() != nil {
|
||||||
}
|
haveErr = it.Error().Error()
|
||||||
|
}
|
||||||
// Tests disassembling the instructions for invalid evm code
|
if haveErr != tc.wantErr {
|
||||||
func TestInstructionIteratorInvalid(t *testing.T) {
|
t.Errorf("test %d: encountered error: %q want %q", i, haveErr, tc.wantErr)
|
||||||
cnt := 0
|
continue
|
||||||
script, _ := hex.DecodeString("6100")
|
}
|
||||||
|
if have != tc.want {
|
||||||
it := NewInstructionIterator(script)
|
t.Errorf("wrong instruction count, have %d want %d", have, tc.want)
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"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
|
//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.
|
// 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,
|
// Create an ephemeral in-memory database for computing hash,
|
||||||
// all the derived states will be discarded to not pollute disk.
|
// 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)
|
statedb, err := state.New(types.EmptyRootHash, db, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return common.Hash{}, err
|
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.
|
// ToBlock returns the genesis block according to genesis specification.
|
||||||
func (g *Genesis) ToBlock() *types.Block {
|
func (g *Genesis) ToBlock() *types.Block {
|
||||||
root, err := g.Alloc.hash()
|
root, err := g.Alloc.hash(g.IsVerkle())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"math/big"
|
"math/big"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
@ -231,7 +232,7 @@ func TestReadWriteGenesisAlloc(t *testing.T) {
|
||||||
{1}: {Balance: big.NewInt(1), Storage: map[common.Hash]common.Hash{{1}: {1}}},
|
{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}}},
|
{2}: {Balance: big.NewInt(2), Storage: map[common.Hash]common.Hash{{2}: {2}}},
|
||||||
}
|
}
|
||||||
hash, _ = alloc.hash()
|
hash, _ = alloc.hash(false)
|
||||||
)
|
)
|
||||||
blob, _ := json.Marshal(alloc)
|
blob, _ := json.Marshal(alloc)
|
||||||
rawdb.WriteGenesisStateSpec(db, hash, blob)
|
rawdb.WriteGenesisStateSpec(db, hash, blob)
|
||||||
|
|
@ -261,3 +262,66 @@ func newDbConfig(scheme string) *trie.Config {
|
||||||
}
|
}
|
||||||
return &trie.Config{PathDB: pathdb.Defaults}
|
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"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/crate-crypto/go-ipa/banderwagon"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/common/lru"
|
"github.com/ethereum/go-ethereum/common/lru"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
|
|
@ -28,6 +29,7 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
"github.com/ethereum/go-ethereum/trie"
|
"github.com/ethereum/go-ethereum/trie"
|
||||||
"github.com/ethereum/go-ethereum/trie/trienode"
|
"github.com/ethereum/go-ethereum/trie/trienode"
|
||||||
|
"github.com/ethereum/go-ethereum/trie/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
@ -36,6 +38,12 @@ const (
|
||||||
|
|
||||||
// Cache size granted for caching clean code.
|
// Cache size granted for caching clean code.
|
||||||
codeCacheSize = 64 * 1024 * 1024
|
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.
|
// Database wraps access to tries and contract code.
|
||||||
|
|
@ -44,7 +52,7 @@ type Database interface {
|
||||||
OpenTrie(root common.Hash) (Trie, error)
|
OpenTrie(root common.Hash) (Trie, error)
|
||||||
|
|
||||||
// OpenStorageTrie opens the storage trie of an account.
|
// 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 returns an independent copy of the given trie.
|
||||||
CopyTrie(Trie) Trie
|
CopyTrie(Trie) Trie
|
||||||
|
|
@ -70,11 +78,6 @@ type Trie interface {
|
||||||
// TODO(fjl): remove this when StateTrie is removed
|
// TODO(fjl): remove this when StateTrie is removed
|
||||||
GetKey([]byte) []byte
|
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
|
// GetAccount abstracts an account read from the trie. It retrieves the
|
||||||
// account blob from the trie with provided account address and decodes it
|
// account blob from the trie with provided account address and decodes it
|
||||||
// with associated decoding algorithm. If the specified account is not in
|
// with associated decoding algorithm. If the specified account is not in
|
||||||
|
|
@ -83,27 +86,32 @@ type Trie interface {
|
||||||
// be returned.
|
// be returned.
|
||||||
GetAccount(address common.Address) (*types.StateAccount, error)
|
GetAccount(address common.Address) (*types.StateAccount, error)
|
||||||
|
|
||||||
// UpdateStorage associates key with value in the trie. If value has length zero,
|
// GetStorage returns the value for key stored in the trie. The value bytes
|
||||||
// any existing value is deleted from the trie. The value bytes must not be modified
|
// must not be modified by the caller. If a node was not found in the database,
|
||||||
// by the caller while they are stored in the trie. If a node was not found in the
|
// a trie.MissingNodeError is returned.
|
||||||
// database, a trie.MissingNodeError is returned.
|
GetStorage(addr common.Address, key []byte) ([]byte, error)
|
||||||
UpdateStorage(addr common.Address, key, value []byte) error
|
|
||||||
|
|
||||||
// UpdateAccount abstracts an account write to the trie. It encodes the
|
// UpdateAccount abstracts an account write to the trie. It encodes the
|
||||||
// provided account object with associated algorithm and then updates it
|
// provided account object with associated algorithm and then updates it
|
||||||
// in the trie with provided address.
|
// in the trie with provided address.
|
||||||
UpdateAccount(address common.Address, account *types.StateAccount) error
|
UpdateAccount(address common.Address, account *types.StateAccount) error
|
||||||
|
|
||||||
// UpdateContractCode abstracts code write to the trie. It is expected
|
// UpdateStorage associates key with value in the trie. If value has length zero,
|
||||||
// to be moved to the stateWriter interface when the latter is ready.
|
// any existing value is deleted from the trie. The value bytes must not be modified
|
||||||
UpdateContractCode(address common.Address, codeHash common.Hash, code []byte) error
|
// 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
|
// DeleteStorage removes any existing value for key from the trie. If a node
|
||||||
// was not found in the database, a trie.MissingNodeError is returned.
|
// was not found in the database, a trie.MissingNodeError is returned.
|
||||||
DeleteStorage(addr common.Address, key []byte) error
|
DeleteStorage(addr common.Address, key []byte) error
|
||||||
|
|
||||||
// DeleteAccount abstracts an account deletion from the trie.
|
// UpdateContractCode abstracts code write to the trie. It is expected
|
||||||
DeleteAccount(address common.Address) error
|
// 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
|
// 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.
|
// 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.
|
// OpenTrie opens the main account trie at a specific root hash.
|
||||||
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
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)
|
tr, err := trie.NewStateTrie(trie.StateTrieID(root), db.triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
@ -178,7 +189,13 @@ func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OpenStorageTrie opens the storage trie of an account.
|
// 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)
|
tr, err := trie.NewStateTrie(trie.StorageTrieID(stateRoot, crypto.Keccak256Hash(address.Bytes()), root), db.triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
||||||
|
|
@ -123,7 +123,7 @@ func (it *nodeIterator) step() error {
|
||||||
address := common.BytesToAddress(preimage)
|
address := common.BytesToAddress(preimage)
|
||||||
|
|
||||||
// Traverse the storage slots belong to the account
|
// 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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -145,7 +145,7 @@ func (s *stateObject) getTrie() (Trie, error) {
|
||||||
s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root)
|
s.trie = s.db.prefetcher.trie(s.addrHash, s.data.Root)
|
||||||
}
|
}
|
||||||
if s.trie == nil {
|
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 {
|
if err != nil {
|
||||||
return nil, err
|
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
|
// employed when the associated state snapshot is not available. It iterates the
|
||||||
// storage slots along with all internal trie nodes via trie directly.
|
// 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) {
|
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 {
|
if err != nil {
|
||||||
return false, 0, nil, nil, fmt.Errorf("failed to open storage trie, err: %w", err)
|
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
|
sf.trie = trie
|
||||||
} else {
|
} 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 {
|
if err != nil {
|
||||||
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
|
log.Warn("Trie prefetcher failed opening trie", "root", sf.root, "err", err)
|
||||||
return
|
return
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
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")
|
EmptyRootHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
||||||
|
|
||||||
// EmptyUncleHash is the known hash of the empty uncle set.
|
// 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 is the known hash of the empty withdrawal set.
|
||||||
EmptyWithdrawalsHash = common.HexToHash("56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421")
|
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
|
// 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
|
// 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/>.
|
// 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) {
|
"github.com/ethereum/go-ethereum/common"
|
||||||
f.Fuzz(func(t *testing.T, data []byte) {
|
)
|
||||||
fuzz(data)
|
|
||||||
|
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.
|
// IsPush specifies if an opcode is a PUSH opcode.
|
||||||
func (op OpCode) IsPush() bool {
|
func (op OpCode) IsPush() bool {
|
||||||
return PUSH1 <= op && op <= PUSH32
|
return PUSH0 <= op && op <= PUSH32
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0x0 range - arithmetic ops.
|
// 0x0 range - arithmetic ops.
|
||||||
|
|
|
||||||
|
|
@ -18,13 +18,11 @@ package runtime
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"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) {
|
f.Fuzz(func(t *testing.T, code, input []byte) {
|
||||||
runtime.Execute(code, input, &runtime.Config{
|
Execute(code, input, &Config{
|
||||||
GasLimit: 12000000,
|
GasLimit: 12000000,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -28,7 +29,6 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"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"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
|
@ -36,6 +36,56 @@ import (
|
||||||
fuzz "github.com/google/gofuzz"
|
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
|
var trieRoot common.Hash
|
||||||
|
|
||||||
func getChain() *core.BlockChain {
|
func getChain() *core.BlockChain {
|
||||||
|
|
@ -86,10 +136,10 @@ type dummyBackend struct {
|
||||||
chain *core.BlockChain
|
chain *core.BlockChain
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *dummyBackend) Chain() *core.BlockChain { return d.chain }
|
func (d *dummyBackend) Chain() *core.BlockChain { return d.chain }
|
||||||
func (d *dummyBackend) RunPeer(*snap.Peer, snap.Handler) error { return nil }
|
func (d *dummyBackend) RunPeer(*Peer, Handler) error { return nil }
|
||||||
func (d *dummyBackend) PeerInfo(enode.ID) interface{} { return "Foo" }
|
func (d *dummyBackend) PeerInfo(enode.ID) interface{} { return "Foo" }
|
||||||
func (d *dummyBackend) Handle(*snap.Peer, snap.Packet) error { return nil }
|
func (d *dummyBackend) Handle(*Peer, Packet) error { return nil }
|
||||||
|
|
||||||
type dummyRW struct {
|
type dummyRW struct {
|
||||||
code uint64
|
code uint64
|
||||||
|
|
@ -110,34 +160,3 @@ func (d *dummyRW) WriteMsg(msg p2p.Msg) error {
|
||||||
d.writeCount++
|
d.writeCount++
|
||||||
return nil
|
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
|
// keyvalue is a key-value tuple tagged with a deletion field to allow creating
|
||||||
// memory-database write batches.
|
// memory-database write batches.
|
||||||
type keyvalue struct {
|
type keyvalue struct {
|
||||||
key []byte
|
key string
|
||||||
value []byte
|
value []byte
|
||||||
delete bool
|
delete bool
|
||||||
}
|
}
|
||||||
|
|
@ -222,14 +222,14 @@ type batch struct {
|
||||||
|
|
||||||
// Put inserts the given value into the batch for later committing.
|
// Put inserts the given value into the batch for later committing.
|
||||||
func (b *batch) Put(key, value []byte) error {
|
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)
|
b.size += len(key) + len(value)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete inserts the a key removal into the batch for later committing.
|
// Delete inserts the a key removal into the batch for later committing.
|
||||||
func (b *batch) Delete(key []byte) error {
|
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)
|
b.size += len(key)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -249,10 +249,10 @@ func (b *batch) Write() error {
|
||||||
}
|
}
|
||||||
for _, keyvalue := range b.writes {
|
for _, keyvalue := range b.writes {
|
||||||
if keyvalue.delete {
|
if keyvalue.delete {
|
||||||
delete(b.db.db, string(keyvalue.key))
|
delete(b.db.db, keyvalue.key)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
b.db.db[string(keyvalue.key)] = keyvalue.value
|
b.db.db[keyvalue.key] = keyvalue.value
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
@ -267,12 +267,12 @@ func (b *batch) Reset() {
|
||||||
func (b *batch) Replay(w ethdb.KeyValueWriter) error {
|
func (b *batch) Replay(w ethdb.KeyValueWriter) error {
|
||||||
for _, keyvalue := range b.writes {
|
for _, keyvalue := range b.writes {
|
||||||
if keyvalue.delete {
|
if keyvalue.delete {
|
||||||
if err := w.Delete(keyvalue.key); err != nil {
|
if err := w.Delete([]byte(keyvalue.key)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if err := w.Put(keyvalue.key, keyvalue.value); err != nil {
|
if err := w.Put([]byte(keyvalue.key), keyvalue.value); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package memorydb
|
package memorydb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"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/errors v1.8.1
|
||||||
github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593
|
github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593
|
||||||
github.com/consensys/gnark-crypto v0.12.1
|
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/crate-crypto/go-kzg-4844 v0.7.0
|
||||||
github.com/davecgh/go-spew v1.1.1
|
github.com/davecgh/go-spew v1.1.1
|
||||||
github.com/deckarep/golang-set/v2 v2.1.0
|
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/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
|
||||||
github.com/fsnotify/fsnotify v1.6.0
|
github.com/fsnotify/fsnotify v1.6.0
|
||||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
|
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/go-stack/stack v1.8.1
|
||||||
github.com/gofrs/flock v0.8.1
|
github.com/gofrs/flock v0.8.1
|
||||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||||
github.com/golang/protobuf v1.5.3
|
github.com/golang/protobuf v1.5.3
|
||||||
github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb
|
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/google/uuid v1.3.0
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/graph-gophers/graphql-go v1.3.0
|
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/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect
|
||||||
github.com/consensys/bavard v0.1.13 // indirect
|
github.com/consensys/bavard v0.1.13 // indirect
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // 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/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
|
||||||
github.com/deepmap/oapi-codegen v1.6.0 // indirect
|
github.com/deepmap/oapi-codegen v1.6.0 // indirect
|
||||||
github.com/dlclark/regexp2 v1.7.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/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 h1:YjAGVd3XmtK9ktAbX8Zg2g2PwLIMjGREZJHlV4j7NEo=
|
||||||
github.com/bits-and-blooms/bitset v1.7.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
|
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 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
|
||||||
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
|
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=
|
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 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 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
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-20230914135612-d1b03fcb8e58 h1:PwUlswsGOrLB677lW4XrlWLeszY3BaDGbvZ6dYk28tQ=
|
||||||
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/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 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA=
|
||||||
github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc=
|
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=
|
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/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 h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI=
|
||||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
|
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.1.1-0.20231004173727-0a4e93ed640b h1:LHeiiSTL2FEGCP1ov6FqkikiViqygeVo1ZwJ1x3nYSE=
|
||||||
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/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/getkin/kin-openapi v0.53.0/go.mod h1:7Yn5whZr5kJi6t+kShccXS8ae1APpYTW6yheSwk8Yi4=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
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=
|
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 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
|
||||||
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
|
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.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.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||||
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
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 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/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=
|
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/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
|
||||||
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
|
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 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/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-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/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-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-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.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.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 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
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
|
* @method formatOutputUInt
|
||||||
* @param {SolidityParam}
|
* @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 formatOutputUInt = function (param) {
|
||||||
var value = param.staticPart() || "0";
|
var value = param.staticPart() || "0";
|
||||||
|
|
|
||||||
|
|
@ -338,7 +338,7 @@ func (h *serverHandler) measure(setup *benchmarkSetup, count int) error {
|
||||||
case <-h.closeCh:
|
case <-h.closeCh:
|
||||||
clientPipe.Close()
|
clientPipe.Close()
|
||||||
serverPipe.Close()
|
serverPipe.Close()
|
||||||
return errors.New("Benchmark cancelled")
|
return errors.New("benchmark cancelled")
|
||||||
}
|
}
|
||||||
|
|
||||||
setup.totalTime += time.Duration(mclock.Now() - start)
|
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 {
|
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.
|
// 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.
|
// 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
|
return err
|
||||||
}
|
}
|
||||||
if reqType != announceTypeSigned {
|
if reqType != announceTypeSigned {
|
||||||
return errors.New("Expected announceTypeSigned")
|
return errors.New("expected announceTypeSigned")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -430,7 +430,7 @@ func handleGetProofs(msg Decoder) (serveRequestFn, uint64, uint64, error) {
|
||||||
p.bumpInvalid()
|
p.bumpInvalid()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
trie, err = statedb.OpenStorageTrie(root, address, account.Root)
|
trie, err = statedb.OpenStorageTrie(root, address, account.Root, nil)
|
||||||
if trie == nil || err != 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)
|
p.Log().Warn("Failed to open storage trie for proof", "block", header.Number, "hash", header.Hash(), "account", address, "root", account.Root, "err", err)
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -257,7 +257,7 @@ func (vt *ValueTracker) loadFromDb(mapping []string) error {
|
||||||
}
|
}
|
||||||
if version != vtVersion {
|
if version != vtVersion {
|
||||||
log.Error("Unknown ValueTracker version", "stored", version, "current", nvtVersion)
|
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
|
var vte valueTrackerEncV1
|
||||||
if err := rlp.Decode(r, &vte); err != nil {
|
if err := rlp.Decode(r, &vte); err != nil {
|
||||||
|
|
@ -295,7 +295,7 @@ loop:
|
||||||
} else {
|
} else {
|
||||||
if vte.RefBasketMapping >= uint(len(vt.mappings)) {
|
if vte.RefBasketMapping >= uint(len(vt.mappings)) {
|
||||||
log.Error("Unknown request basket mapping", "stored", vte.RefBasketMapping, "current", vt.currentMapping)
|
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)
|
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
|
t state.Trie
|
||||||
)
|
)
|
||||||
if len(req.Id.AccountAddress) > 0 {
|
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 {
|
} else {
|
||||||
t, err = odr.serverState.OpenTrie(req.Id.Root)
|
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
|
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
|
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.
|
// ReadDiskStats retrieves the disk IO stats belonging to the current process.
|
||||||
func ReadDiskStats(stats *DiskStats) error {
|
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)
|
g.Update(47.0)
|
||||||
snapshot := g.Snapshot()
|
snapshot := g.Snapshot()
|
||||||
g.Update(float64(0))
|
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)
|
t.Errorf("g.Value(): 47.0 != %v\n", v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -45,7 +45,7 @@ func TestGetOrRegisterGaugeFloat64(t *testing.T) {
|
||||||
r := NewRegistry()
|
r := NewRegistry()
|
||||||
NewRegisteredGaugeFloat64("foo", r).Update(47.0)
|
NewRegisteredGaugeFloat64("foo", r).Update(47.0)
|
||||||
t.Logf("registry: %v", r)
|
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)
|
t.Fatal(g)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,7 @@ func (bc *testBlockChain) SubscribeChainHeadEvent(ch chan<- core.ChainHeadEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMiner(t *testing.T) {
|
func TestMiner(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
miner, mux, cleanup := createMiner(t)
|
miner, mux, cleanup := createMiner(t)
|
||||||
defer cleanup(false)
|
defer cleanup(false)
|
||||||
|
|
||||||
|
|
@ -128,6 +129,7 @@ func TestMiner(t *testing.T) {
|
||||||
// An initial FailedEvent should allow mining to stop on a subsequent
|
// An initial FailedEvent should allow mining to stop on a subsequent
|
||||||
// downloader StartEvent.
|
// downloader StartEvent.
|
||||||
func TestMinerDownloaderFirstFails(t *testing.T) {
|
func TestMinerDownloaderFirstFails(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
miner, mux, cleanup := createMiner(t)
|
miner, mux, cleanup := createMiner(t)
|
||||||
defer cleanup(false)
|
defer cleanup(false)
|
||||||
|
|
||||||
|
|
@ -161,6 +163,7 @@ func TestMinerDownloaderFirstFails(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMinerStartStopAfterDownloaderEvents(t *testing.T) {
|
func TestMinerStartStopAfterDownloaderEvents(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
miner, mux, cleanup := createMiner(t)
|
miner, mux, cleanup := createMiner(t)
|
||||||
defer cleanup(false)
|
defer cleanup(false)
|
||||||
|
|
||||||
|
|
@ -185,6 +188,7 @@ func TestMinerStartStopAfterDownloaderEvents(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStartWhileDownload(t *testing.T) {
|
func TestStartWhileDownload(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
miner, mux, cleanup := createMiner(t)
|
miner, mux, cleanup := createMiner(t)
|
||||||
defer cleanup(false)
|
defer cleanup(false)
|
||||||
waitForMiningState(t, miner, false)
|
waitForMiningState(t, miner, false)
|
||||||
|
|
@ -199,6 +203,7 @@ func TestStartWhileDownload(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStartStopMiner(t *testing.T) {
|
func TestStartStopMiner(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
miner, _, cleanup := createMiner(t)
|
miner, _, cleanup := createMiner(t)
|
||||||
defer cleanup(false)
|
defer cleanup(false)
|
||||||
waitForMiningState(t, miner, false)
|
waitForMiningState(t, miner, false)
|
||||||
|
|
@ -209,6 +214,7 @@ func TestStartStopMiner(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCloseMiner(t *testing.T) {
|
func TestCloseMiner(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
miner, _, cleanup := createMiner(t)
|
miner, _, cleanup := createMiner(t)
|
||||||
defer cleanup(true)
|
defer cleanup(true)
|
||||||
waitForMiningState(t, miner, false)
|
waitForMiningState(t, miner, false)
|
||||||
|
|
@ -222,6 +228,7 @@ func TestCloseMiner(t *testing.T) {
|
||||||
// TestMinerSetEtherbase checks that etherbase becomes set even if mining isn't
|
// TestMinerSetEtherbase checks that etherbase becomes set even if mining isn't
|
||||||
// possible at the moment
|
// possible at the moment
|
||||||
func TestMinerSetEtherbase(t *testing.T) {
|
func TestMinerSetEtherbase(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
miner, mux, cleanup := createMiner(t)
|
miner, mux, cleanup := createMiner(t)
|
||||||
defer cleanup(false)
|
defer cleanup(false)
|
||||||
miner.Start()
|
miner.Start()
|
||||||
|
|
|
||||||
|
|
@ -30,10 +30,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestTransactionPriceNonceSortLegacy(t *testing.T) {
|
func TestTransactionPriceNonceSortLegacy(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
testTransactionPriceNonceSort(t, nil)
|
testTransactionPriceNonceSort(t, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTransactionPriceNonceSort1559(t *testing.T) {
|
func TestTransactionPriceNonceSort1559(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
testTransactionPriceNonceSort(t, big.NewInt(0))
|
testTransactionPriceNonceSort(t, big.NewInt(0))
|
||||||
testTransactionPriceNonceSort(t, big.NewInt(5))
|
testTransactionPriceNonceSort(t, big.NewInt(5))
|
||||||
testTransactionPriceNonceSort(t, big.NewInt(50))
|
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
|
// 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.
|
// are prioritized to avoid network spam attacks aiming for a specific ordering.
|
||||||
func TestTransactionTimeSort(t *testing.T) {
|
func TestTransactionTimeSort(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
// Generate a batch of accounts to start with
|
// Generate a batch of accounts to start with
|
||||||
keys := make([]*ecdsa.PrivateKey, 5)
|
keys := make([]*ecdsa.PrivateKey, 5)
|
||||||
for i := 0; i < len(keys); i++ {
|
for i := 0; i < len(keys); i++ {
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestBuildPayload(t *testing.T) {
|
func TestBuildPayload(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
var (
|
var (
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
recipient = common.HexToAddress("0xdeadbeef")
|
recipient = common.HexToAddress("0xdeadbeef")
|
||||||
|
|
@ -82,6 +83,7 @@ func TestBuildPayload(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestPayloadId(t *testing.T) {
|
func TestPayloadId(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
ids := make(map[string]int)
|
ids := make(map[string]int)
|
||||||
for i, tt := range []*BuildPayloadArgs{
|
for i, tt := range []*BuildPayloadArgs{
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,7 @@ func newTestWorker(t *testing.T, chainConfig *params.ChainConfig, engine consens
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGenerateAndImportBlock(t *testing.T) {
|
func TestGenerateAndImportBlock(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
var (
|
var (
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
config = *params.AllCliqueProtocolChanges
|
config = *params.AllCliqueProtocolChanges
|
||||||
|
|
@ -210,9 +211,11 @@ func TestGenerateAndImportBlock(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestEmptyWorkEthash(t *testing.T) {
|
func TestEmptyWorkEthash(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
testEmptyWork(t, ethashChainConfig, ethash.NewFaker())
|
testEmptyWork(t, ethashChainConfig, ethash.NewFaker())
|
||||||
}
|
}
|
||||||
func TestEmptyWorkClique(t *testing.T) {
|
func TestEmptyWorkClique(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
testEmptyWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase()))
|
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) {
|
func TestAdjustIntervalEthash(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
testAdjustInterval(t, ethashChainConfig, ethash.NewFaker())
|
testAdjustInterval(t, ethashChainConfig, ethash.NewFaker())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAdjustIntervalClique(t *testing.T) {
|
func TestAdjustIntervalClique(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
testAdjustInterval(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase()))
|
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) {
|
func TestGetSealingWorkEthash(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
testGetSealingWork(t, ethashChainConfig, ethash.NewFaker())
|
testGetSealingWork(t, ethashChainConfig, ethash.NewFaker())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSealingWorkClique(t *testing.T) {
|
func TestGetSealingWorkClique(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
testGetSealingWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase()))
|
testGetSealingWork(t, cliqueChainConfig, clique.New(cliqueChainConfig.Clique, rawdb.NewMemoryDatabase()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSealingWorkPostMerge(t *testing.T) {
|
func TestGetSealingWorkPostMerge(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
local := new(params.ChainConfig)
|
local := new(params.ChainConfig)
|
||||||
*local = *ethashChainConfig
|
*local = *ethashChainConfig
|
||||||
local.TerminalTotalDifficulty = big.NewInt(0)
|
local.TerminalTotalDifficulty = big.NewInt(0)
|
||||||
|
|
|
||||||
204
oss-fuzz.sh
204
oss-fuzz.sh
|
|
@ -48,39 +48,27 @@ DOG
|
||||||
cd -
|
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() {
|
function compile_fuzzer() {
|
||||||
path=$GOPATH/src/github.com/ethereum/go-ethereum/$1
|
package=$1
|
||||||
function=$2
|
function=$2
|
||||||
fuzzer=$3
|
fuzzer=$3
|
||||||
|
file=$4
|
||||||
|
|
||||||
|
path=$GOPATH/src/$package
|
||||||
|
|
||||||
echo "Building $fuzzer"
|
echo "Building $fuzzer"
|
||||||
cd $path
|
cd $path
|
||||||
|
|
||||||
# Install build dependencies
|
# Install build dependencies
|
||||||
go install github.com/AdamKorcz/go-118-fuzz-build@latest
|
go mod tidy
|
||||||
go get github.com/AdamKorcz/go-118-fuzz-build/testing
|
go get github.com/holiman/gofuzz-shim/testing
|
||||||
|
|
||||||
# Test if file contains a line with "func $function(" and "testing.F".
|
if [[ $SANITIZER == *coverage* ]]; then
|
||||||
if [ $(grep -r "func $function(" $path | grep "testing.F" | wc -l) -eq 1 ]
|
coverbuild $path $function $fuzzer $coverpkg
|
||||||
then
|
else
|
||||||
build_native_go_fuzzer $fuzzer $function $path
|
gofuzz-shim --func $function --package $package -f $file -o $fuzzer.a
|
||||||
else
|
$CXX $CXXFLAGS $LIB_FUZZING_ENGINE $fuzzer.a -o $OUT/$fuzzer
|
||||||
echo "Could not find the function: func ${function}(f *testing.F)"
|
fi
|
||||||
fi
|
|
||||||
|
|
||||||
## Check if there exists a seed corpus file
|
## Check if there exists a seed corpus file
|
||||||
corpusfile="${path}/testdata/${fuzzer}_seed_corpus.zip"
|
corpusfile="${path}/testdata/${fuzzer}_seed_corpus.zip"
|
||||||
|
|
@ -92,42 +80,140 @@ function compile_fuzzer() {
|
||||||
cd -
|
cd -
|
||||||
}
|
}
|
||||||
|
|
||||||
compile_fuzzer tests/fuzzers/bitutil FuzzEncoder fuzzBitutilEncoder
|
go install github.com/holiman/gofuzz-shim@latest
|
||||||
compile_fuzzer tests/fuzzers/bitutil FuzzDecoder fuzzBitutilDecoder
|
repo=$GOPATH/src/github.com/ethereum/go-ethereum
|
||||||
compile_fuzzer tests/fuzzers/bn256 FuzzAdd fuzzBn256Add
|
compile_fuzzer github.com/ethereum/go-ethereum/accounts/abi \
|
||||||
compile_fuzzer tests/fuzzers/bn256 FuzzMul fuzzBn256Mul
|
FuzzABI fuzzAbi \
|
||||||
compile_fuzzer tests/fuzzers/bn256 FuzzPair fuzzBn256Pair
|
$repo/accounts/abi/abifuzzer_test.go
|
||||||
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
|
|
||||||
|
|
||||||
compile_fuzzer tests/fuzzers/bls12381 FuzzG1Add fuzz_g1_add
|
compile_fuzzer github.com/ethereum/go-ethereum/common/bitutil \
|
||||||
compile_fuzzer tests/fuzzers/bls12381 FuzzG1Mul fuzz_g1_mul
|
FuzzEncoder fuzzBitutilEncoder \
|
||||||
compile_fuzzer tests/fuzzers/bls12381 FuzzG1MultiExp fuzz_g1_multiexp
|
$repo/common/bitutil/compress_test.go
|
||||||
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 tests/fuzzers/bls12381 FuzzCrossG1Add fuzz_cross_g1_add
|
compile_fuzzer github.com/ethereum/go-ethereum/common/bitutil \
|
||||||
compile_fuzzer tests/fuzzers/bls12381 FuzzCrossG1MultiExp fuzz_cross_g1_multiexp
|
FuzzDecoder fuzzBitutilDecoder \
|
||||||
compile_fuzzer tests/fuzzers/bls12381 FuzzCrossG2Add fuzz_cross_g2_add
|
$repo/common/bitutil/compress_test.go
|
||||||
compile_fuzzer tests/fuzzers/bls12381 FuzzCrossPairing fuzz_cross_pairing
|
|
||||||
|
|
||||||
compile_fuzzer tests/fuzzers/snap FuzzARange fuzz_account_range
|
compile_fuzzer github.com/ethereum/go-ethereum/core/vm/runtime \
|
||||||
compile_fuzzer tests/fuzzers/snap FuzzSRange fuzz_storage_range
|
FuzzVmRuntime fuzzVmRuntime\
|
||||||
compile_fuzzer tests/fuzzers/snap FuzzByteCodes fuzz_byte_codes
|
$repo/core/vm/runtime/runtime_fuzz_test.go
|
||||||
compile_fuzzer tests/fuzzers/snap FuzzTrieNodes fuzz_trie_nodes
|
|
||||||
|
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
|
// "pmp:192.168.0.1" uses NAT-PMP with the given gateway address
|
||||||
func Parse(spec string) (Interface, error) {
|
func Parse(spec string) (Interface, error) {
|
||||||
var (
|
var (
|
||||||
parts = strings.SplitN(spec, ":", 2)
|
before, after, found = strings.Cut(spec, ":")
|
||||||
mech = strings.ToLower(parts[0])
|
mech = strings.ToLower(before)
|
||||||
ip net.IP
|
ip net.IP
|
||||||
)
|
)
|
||||||
if len(parts) > 1 {
|
if found {
|
||||||
ip = net.ParseIP(parts[1])
|
ip = net.ParseIP(after)
|
||||||
if ip == nil {
|
if ip == nil {
|
||||||
return nil, errors.New("invalid IP address")
|
return nil, errors.New("invalid IP address")
|
||||||
}
|
}
|
||||||
|
|
@ -86,7 +86,7 @@ func Parse(spec string) (Interface, error) {
|
||||||
case "pmp", "natpmp", "nat-pmp":
|
case "pmp", "natpmp", "nat-pmp":
|
||||||
return PMP(ip), nil
|
return PMP(ip), nil
|
||||||
default:
|
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) {
|
func NewMsgFilters(filterParam string) (MsgFilters, error) {
|
||||||
filters := make(MsgFilters)
|
filters := make(MsgFilters)
|
||||||
for _, filter := range strings.Split(filterParam, "-") {
|
for _, filter := range strings.Split(filterParam, "-") {
|
||||||
protoCodes := strings.SplitN(filter, ":", 2)
|
proto, codes, found := strings.Cut(filter, ":")
|
||||||
if len(protoCodes) != 2 || protoCodes[0] == "" || protoCodes[1] == "" {
|
if !found || proto == "" || codes == "" {
|
||||||
return nil, fmt.Errorf("invalid message filter: %s", filter)
|
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" {
|
if code == "*" || code == "-1" {
|
||||||
filters[MsgFilter{Proto: proto, Code: -1}] = struct{}{}
|
filters[MsgFilter{Proto: proto, Code: -1}] = struct{}{}
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,8 @@ func (msg *jsonrpcMessage) isUnsubscribe() bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *jsonrpcMessage) namespace() string {
|
func (msg *jsonrpcMessage) namespace() string {
|
||||||
elem := strings.SplitN(msg.Method, serviceMethodSeparator, 2)
|
before, _, _ := strings.Cut(msg.Method, serviceMethodSeparator)
|
||||||
return elem[0]
|
return before
|
||||||
}
|
}
|
||||||
|
|
||||||
func (msg *jsonrpcMessage) String() string {
|
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.
|
// callback returns the callback corresponding to the given RPC method name.
|
||||||
func (r *serviceRegistry) callback(method string) *callback {
|
func (r *serviceRegistry) callback(method string) *callback {
|
||||||
elem := strings.SplitN(method, serviceMethodSeparator, 2)
|
before, after, found := strings.Cut(method, serviceMethodSeparator)
|
||||||
if len(elem) != 2 {
|
if !found {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
defer r.mu.Unlock()
|
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.
|
// subscription returns a subscription callback in the given service.
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@
|
||||||
package tests
|
package tests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"math/rand"
|
||||||
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
|
@ -49,6 +51,9 @@ func TestBlockchain(t *testing.T) {
|
||||||
bt.skipLoad(`.*randomStatetest94.json.*`)
|
bt.skipLoad(`.*randomStatetest94.json.*`)
|
||||||
|
|
||||||
bt.walk(t, blockTestDir, func(t *testing.T, name string, test *BlockTest) {
|
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)
|
execBlockTest(t, bt, test)
|
||||||
})
|
})
|
||||||
// There is also a LegacyTests folder, containing blockchain tests generated
|
// 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 (
|
var (
|
||||||
curveA = secp256k1.S256()
|
curveA = secp256k1.S256()
|
||||||
curveB = btcec.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)
|
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))
|
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"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/big"
|
"math/big"
|
||||||
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
@ -76,6 +78,10 @@ func TestState(t *testing.T) {
|
||||||
benchmarksDir,
|
benchmarksDir,
|
||||||
} {
|
} {
|
||||||
st.walk(t, dir, func(t *testing.T, name string, test *StateTest) {
|
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() {
|
for _, subtest := range test.Subtests() {
|
||||||
subtest := subtest
|
subtest := subtest
|
||||||
key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index)
|
key := fmt.Sprintf("%s/%d", subtest.Fork, subtest.Index)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import (
|
||||||
// Config defines all necessary options for database.
|
// Config defines all necessary options for database.
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Preimages bool // Flag whether the preimage of node key is recorded
|
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
|
HashDB *hashdb.Config // Configs for hash-based scheme
|
||||||
PathDB *pathdb.Config // Configs for experimental path-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)
|
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
|
return hashed, cached
|
||||||
default:
|
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
|
return n, n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// hashShortNodeChildren collapses the short node. The returned collapsed node
|
// hashShortNodeChildren collapses the short node. The returned collapsed node
|
||||||
// holds a live reference to the Key, and must not be modified.
|
// holds a live reference to the Key, and must not be modified.
|
||||||
// The cached
|
|
||||||
func (h *hasher) hashShortNodeChildren(n *shortNode) (collapsed, cached *shortNode) {
|
func (h *hasher) hashShortNodeChildren(n *shortNode) (collapsed, cached *shortNode) {
|
||||||
// Hash the short node's child, caching the newly hashed subtree
|
// Hash the short node's child, caching the newly hashed subtree
|
||||||
collapsed, cached = n.copy(), n.copy()
|
collapsed, cached = n.copy(), n.copy()
|
||||||
// Previously, we did copy this one. We don't seem to need to actually
|
// Previously, we did copy this one. We don't seem to need to actually
|
||||||
// do that, since we don't overwrite/reuse keys
|
// 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)
|
collapsed.Key = hexToCompact(n.Key)
|
||||||
// Unless the child is a valuenode or hashnode, hash it
|
// Unless the child is a valuenode or hashnode, hash it
|
||||||
switch n.Val.(type) {
|
switch n.Val.(type) {
|
||||||
|
|
@ -153,7 +152,7 @@ func (h *hasher) shortnodeToHash(n *shortNode, force bool) node {
|
||||||
return h.hashData(enc)
|
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)
|
// may contain nil values)
|
||||||
func (h *hasher) fullnodeToHash(n *fullNode, force bool) node {
|
func (h *hasher) fullnodeToHash(n *fullNode, force bool) node {
|
||||||
n.encode(h.encbuf)
|
n.encode(h.encbuf)
|
||||||
|
|
@ -203,7 +202,7 @@ func (h *hasher) proofHash(original node) (collapsed, hashed node) {
|
||||||
fn, _ := h.hashFullNodeChildren(n)
|
fn, _ := h.hashFullNodeChildren(n)
|
||||||
return fn, h.fullnodeToHash(fn, false)
|
return fn, h.fullnodeToHash(fn, false)
|
||||||
default:
|
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
|
return n, n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -144,7 +144,8 @@ type nodeIterator struct {
|
||||||
path []byte // Path to the current node
|
path []byte // Path to the current node
|
||||||
err error // Failure set in case of an internal error in the iterator
|
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.
|
// errIteratorEnd is stored in nodeIterator.err when iteration is done.
|
||||||
|
|
@ -172,6 +173,24 @@ func newNodeIterator(trie *Trie, start []byte) NodeIterator {
|
||||||
return it
|
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) {
|
func (it *nodeIterator) AddResolver(resolver NodeResolver) {
|
||||||
it.resolver = resolver
|
it.resolver = resolver
|
||||||
}
|
}
|
||||||
|
|
@ -423,8 +442,9 @@ func (st *nodeIteratorState) resolve(it *nodeIterator, path []byte) error {
|
||||||
return nil
|
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 (
|
var (
|
||||||
|
path = it.path
|
||||||
child node
|
child node
|
||||||
state *nodeIteratorState
|
state *nodeIteratorState
|
||||||
childPath []byte
|
childPath []byte
|
||||||
|
|
@ -433,13 +453,12 @@ func findChild(n *fullNode, index int, path []byte, ancestor common.Hash) (node,
|
||||||
if n.Children[index] != nil {
|
if n.Children[index] != nil {
|
||||||
child = n.Children[index]
|
child = n.Children[index]
|
||||||
hash, _ := child.cache()
|
hash, _ := child.cache()
|
||||||
state = &nodeIteratorState{
|
state = it.getFromPool()
|
||||||
hash: common.BytesToHash(hash),
|
state.hash = common.BytesToHash(hash)
|
||||||
node: child,
|
state.node = child
|
||||||
parent: ancestor,
|
state.parent = ancestor
|
||||||
index: -1,
|
state.index = -1
|
||||||
pathlen: len(path),
|
state.pathlen = len(path)
|
||||||
}
|
|
||||||
childPath = append(childPath, path...)
|
childPath = append(childPath, path...)
|
||||||
childPath = append(childPath, byte(index))
|
childPath = append(childPath, byte(index))
|
||||||
return child, state, childPath, index
|
return child, state, childPath, index
|
||||||
|
|
@ -452,7 +471,7 @@ func (it *nodeIterator) nextChild(parent *nodeIteratorState, ancestor common.Has
|
||||||
switch node := parent.node.(type) {
|
switch node := parent.node.(type) {
|
||||||
case *fullNode:
|
case *fullNode:
|
||||||
// Full node, move to the first non-nil child.
|
// 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
|
parent.index = index - 1
|
||||||
return state, path, true
|
return state, path, true
|
||||||
}
|
}
|
||||||
|
|
@ -460,13 +479,12 @@ func (it *nodeIterator) nextChild(parent *nodeIteratorState, ancestor common.Has
|
||||||
// Short node, return the pointer singleton child
|
// Short node, return the pointer singleton child
|
||||||
if parent.index < 0 {
|
if parent.index < 0 {
|
||||||
hash, _ := node.Val.cache()
|
hash, _ := node.Val.cache()
|
||||||
state := &nodeIteratorState{
|
state := it.getFromPool()
|
||||||
hash: common.BytesToHash(hash),
|
state.hash = common.BytesToHash(hash)
|
||||||
node: node.Val,
|
state.node = node.Val
|
||||||
parent: ancestor,
|
state.parent = ancestor
|
||||||
index: -1,
|
state.index = -1
|
||||||
pathlen: len(it.path),
|
state.pathlen = len(it.path)
|
||||||
}
|
|
||||||
path := append(it.path, node.Key...)
|
path := append(it.path, node.Key...)
|
||||||
return state, path, true
|
return state, path, true
|
||||||
}
|
}
|
||||||
|
|
@ -480,7 +498,7 @@ func (it *nodeIterator) nextChildAt(parent *nodeIteratorState, ancestor common.H
|
||||||
switch n := parent.node.(type) {
|
switch n := parent.node.(type) {
|
||||||
case *fullNode:
|
case *fullNode:
|
||||||
// Full node, move to the first non-nil child before the desired key position
|
// 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 {
|
if child == nil {
|
||||||
// No more children in this fullnode
|
// No more children in this fullnode
|
||||||
return parent, it.path, false
|
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
|
// The child is before the seek position. Try advancing
|
||||||
for {
|
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
|
// If we run out of children, or skipped past the target, return the
|
||||||
// previous one
|
// previous one
|
||||||
if nextChild == nil || bytes.Compare(nextPath, key) >= 0 {
|
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
|
// Short node, return the pointer singleton child
|
||||||
if parent.index < 0 {
|
if parent.index < 0 {
|
||||||
hash, _ := n.Val.cache()
|
hash, _ := n.Val.cache()
|
||||||
state := &nodeIteratorState{
|
state := it.getFromPool()
|
||||||
hash: common.BytesToHash(hash),
|
state.hash = common.BytesToHash(hash)
|
||||||
node: n.Val,
|
state.node = n.Val
|
||||||
parent: ancestor,
|
state.parent = ancestor
|
||||||
index: -1,
|
state.index = -1
|
||||||
pathlen: len(it.path),
|
state.pathlen = len(it.path)
|
||||||
}
|
|
||||||
path := append(it.path, n.Key...)
|
path := append(it.path, n.Key...)
|
||||||
return state, path, true
|
return state, path, true
|
||||||
}
|
}
|
||||||
|
|
@ -533,6 +550,8 @@ func (it *nodeIterator) pop() {
|
||||||
it.path = it.path[:last.pathlen]
|
it.path = it.path[:last.pathlen]
|
||||||
it.stack[len(it.stack)-1] = nil
|
it.stack[len(it.stack)-1] = nil
|
||||||
it.stack = it.stack[:len(it.stack)-1]
|
it.stack = it.stack[:len(it.stack)-1]
|
||||||
|
// last is now unused
|
||||||
|
it.putInPool(last)
|
||||||
}
|
}
|
||||||
|
|
||||||
func compareNodes(a, b NodeIterator) int {
|
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
|
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)
|
hash := crypto.Keccak256Hash(result.Data)
|
||||||
if hash != root {
|
if hash != root {
|
||||||
addedKeys = append(addedKeys, result.Path)
|
addedKeys = append(addedKeys, result.Path)
|
||||||
addedHashes = append(addedHashes, crypto.Keccak256Hash(result.Data))
|
addedHashes = append(addedHashes, hash)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Fetch the next batch to retrieve
|
// Fetch the next batch to retrieve
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"hash"
|
"hash"
|
||||||
|
"io"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"reflect"
|
"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("980c393656413a15c8da01978ed9f89feb80b502f58f2d640e3a2f5f7a99a7018f1b573befd92053ac6f78fca4a87268"), value: common.Hex2Bytes("")}, // step 24
|
||||||
{op: 1, key: common.Hex2Bytes("fd"), value: common.Hex2Bytes("")}, // step 25
|
{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.
|
// randTest performs random trie operations.
|
||||||
|
|
@ -389,33 +392,45 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
func (randTest) Generate(r *rand.Rand, size int) reflect.Value {
|
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 allKeys [][]byte
|
||||||
|
var one = []byte{0}
|
||||||
genKey := func() []byte {
|
genKey := func() []byte {
|
||||||
if len(allKeys) < 2 || r.Intn(100) < 10 {
|
r.Read(one)
|
||||||
|
if len(allKeys) < 2 || one[0]%100 > 90 {
|
||||||
// new key
|
// new key
|
||||||
key := make([]byte, r.Intn(50))
|
size := one[0] % 50
|
||||||
|
key := make([]byte, size)
|
||||||
r.Read(key)
|
r.Read(key)
|
||||||
allKeys = append(allKeys, key)
|
allKeys = append(allKeys, key)
|
||||||
return key
|
return key
|
||||||
}
|
}
|
||||||
// use existing key
|
// use existing key
|
||||||
return allKeys[r.Intn(len(allKeys))]
|
idx := int(one[0]) % len(allKeys)
|
||||||
|
return allKeys[idx]
|
||||||
}
|
}
|
||||||
|
|
||||||
var steps randTest
|
var steps randTest
|
||||||
for i := 0; i < size; i++ {
|
for !finished() {
|
||||||
step := randTestStep{op: r.Intn(opMax)}
|
r.Read(one)
|
||||||
|
step := randTestStep{op: int(one[0]) % opMax}
|
||||||
switch step.op {
|
switch step.op {
|
||||||
case opUpdate:
|
case opUpdate:
|
||||||
step.key = genKey()
|
step.key = genKey()
|
||||||
step.value = make([]byte, 8)
|
step.value = make([]byte, 8)
|
||||||
binary.BigEndian.PutUint64(step.value, uint64(i))
|
binary.BigEndian.PutUint64(step.value, uint64(len(steps)))
|
||||||
case opGet, opDelete, opProve:
|
case opGet, opDelete, opProve:
|
||||||
step.key = genKey()
|
step.key = genKey()
|
||||||
}
|
}
|
||||||
steps = append(steps, step)
|
steps = append(steps, step)
|
||||||
}
|
}
|
||||||
return reflect.ValueOf(steps)
|
return steps
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyAccessList(old *Trie, new *Trie, set *trienode.NodeSet) error {
|
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
|
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
|
var scheme = rawdb.HashScheme
|
||||||
if rand.Intn(2) == 0 {
|
if rand.Intn(2) == 0 {
|
||||||
scheme = rawdb.PathScheme
|
scheme = rawdb.PathScheme
|
||||||
|
|
@ -513,12 +533,12 @@ func runRandTest(rt randTest) bool {
|
||||||
newtr, err := New(TrieID(root), triedb)
|
newtr, err := New(TrieID(root), triedb)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
rt[i].err = err
|
rt[i].err = err
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
if nodes != nil {
|
if nodes != nil {
|
||||||
if err := verifyAccessList(origTrie, newtr, nodes); err != nil {
|
if err := verifyAccessList(origTrie, newtr, nodes); err != nil {
|
||||||
rt[i].err = err
|
rt[i].err = err
|
||||||
return false
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tr = newtr
|
tr = newtr
|
||||||
|
|
@ -587,14 +607,14 @@ func runRandTest(rt randTest) bool {
|
||||||
}
|
}
|
||||||
// Abort the test on error.
|
// Abort the test on error.
|
||||||
if rt[i].err != nil {
|
if rt[i].err != nil {
|
||||||
return false
|
return rt[i].err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRandom(t *testing.T) {
|
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 {
|
if cerr, ok := err.(*quick.CheckError); ok {
|
||||||
t.Fatalf("random test iteration %d failed: %s", cerr.Count, spew.Sdump(cerr.In))
|
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)
|
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.
|
// IsDeleted returns the indicator if the node is marked as deleted.
|
||||||
func (n *Node) IsDeleted() bool {
|
func (n *Node) IsDeleted() bool {
|
||||||
return n.Hash == (common.Hash{})
|
return len(n.Blob) == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// New constructs a node with provided node information.
|
// 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