Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP mega eof #29518

Draft
wants to merge 54 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
c23f8af
core/vm: add eof container
lightclient Dec 6, 2022
e8953bd
happy lint, happy life
MariusVanDerWijden Apr 12, 2024
9aa46f3
core/vm: add optional containerSections
MariusVanDerWijden Apr 12, 2024
9ca1532
core/vm: add optional containerSections
MariusVanDerWijden Apr 12, 2024
e5724b2
core/types: implement InitCodeTransaction for EOF
MariusVanDerWijden Apr 12, 2024
4723974
core/vm: implement JUMPF, added disallowed opcodes
MariusVanDerWijden Apr 15, 2024
1e381a3
core/vm: started work on eofcreate, safety push
MariusVanDerWijden Apr 15, 2024
319d022
core/vm: started work on eofcreate
MariusVanDerWijden Apr 16, 2024
ed5b362
core/vm: implement dataload opcodes
MariusVanDerWijden Apr 16, 2024
61aa4c1
core/vm: implement dupN, swapN
MariusVanDerWijden Apr 16, 2024
1762ea3
core/vm: refactor using min
MariusVanDerWijden Apr 16, 2024
e1e0a7a
core/vm: implement opReturnDataLoad
MariusVanDerWijden Apr 16, 2024
6e16cf5
core/vm: implement opExt{Call,StaticCall,DelegateCall}
MariusVanDerWijden Apr 16, 2024
5eac46f
core/vm: added opcode names
MariusVanDerWijden Apr 17, 2024
9640e5f
core/vm: add opcodes to the jumptable
MariusVanDerWijden Apr 17, 2024
7e7517d
cmd/evm: implement evm eofdump
MariusVanDerWijden Apr 17, 2024
22f49b5
cmd/evm: implement evm eofdump
MariusVanDerWijden Apr 17, 2024
b537b7d
cmd/evm: fix tests
MariusVanDerWijden Apr 17, 2024
fc4f265
cmd/eofdump: move eofdump to own subcommand
MariusVanDerWijden Apr 17, 2024
e677308
core/vm: fixed bug in marshaling
MariusVanDerWijden Apr 17, 2024
9443916
core/vm: better validation
MariusVanDerWijden Apr 18, 2024
f5ecd89
core/vm: fixed pc skip
MariusVanDerWijden Apr 18, 2024
ee16514
cmd/eofdump: add recursive mode for tests
MariusVanDerWijden Apr 19, 2024
a033d6b
core/vm: fix a few bugs, prettier print
MariusVanDerWijden Apr 22, 2024
8835928
core/vm: fix bugs in DATALOADN, Validation, PUSH9 etc
MariusVanDerWijden Apr 22, 2024
f669cfc
core/vm: fix bugs in RETURNCONTRACT validation
MariusVanDerWijden Apr 22, 2024
06872b2
core/vm: fix bugs in EOFCREATE validation and others
MariusVanDerWijden Apr 23, 2024
96fd50a
core/vm: make sure every section is reachable
MariusVanDerWijden Apr 23, 2024
046f287
core/vm: fix subcontainer validation
MariusVanDerWijden Apr 24, 2024
efb9807
core/vm: fix max-stack-height validation
MariusVanDerWijden Apr 24, 2024
44d876c
core/vm: fix RJUMPV to use max_size + 1, fix tests
MariusVanDerWijden Apr 24, 2024
56a0ca8
core/vm: fix CALLF stack height validation
MariusVanDerWijden Apr 24, 2024
104df43
core/vm: fix DUPN, SWAPN, EXCHANGE stack height validation
MariusVanDerWijden Apr 24, 2024
ab6003b
core/vm: fix DUPN, SWAPN, EXCHANGE stack height validation
MariusVanDerWijden Apr 24, 2024
4add7c2
core/vm: fix JUMPF stack height validation
MariusVanDerWijden Apr 24, 2024
f762178
core/vm: fix backward jump validation
MariusVanDerWijden May 6, 2024
3929447
core/vm: fix ext stack heights, fix jumpdest validation bitmap
MariusVanDerWijden May 6, 2024
2f91f0b
core/vm: started work on linear validation
MariusVanDerWijden May 21, 2024
8c7de99
core/vm: more fixes for validate_linear
MariusVanDerWijden May 22, 2024
65a20c4
core/vm: more fixes for validate_linear
MariusVanDerWijden May 23, 2024
ab1c88c
core/vm: fix stack limit
MariusVanDerWijden May 24, 2024
076a587
core/vm: fix rjumpv offsets
MariusVanDerWijden May 24, 2024
a643fe9
core/vm: fix test
MariusVanDerWijden May 24, 2024
4d29707
core/vm: fix underflow checks
MariusVanDerWijden May 24, 2024
49086d5
core/vm: fix a bunch of validation bugs
MariusVanDerWijden Jun 4, 2024
38d86bd
core/vm: fixed RJUMP bugs
MariusVanDerWijden Jun 6, 2024
345b79f
core/vm: correctly set bounds for underflow
MariusVanDerWijden Jun 6, 2024
d1051a5
core/vm: fix off-by-one error in exchange validation
MariusVanDerWijden Jun 6, 2024
1da9562
core/vm: fix error in DUPN/SWAPN
MariusVanDerWijden Jun 6, 2024
81cf881
core/vm: fixed copy paste error
MariusVanDerWijden Jun 6, 2024
2fde71e
core/vm: fix off-by-one in rjumpv
MariusVanDerWijden Jun 6, 2024
c3c3a28
core/vm: reordered logic
MariusVanDerWijden Jun 7, 2024
b789efe
core/vm: simplify
MariusVanDerWijden Jun 7, 2024
19d492e
core/vm: validate rjump destination
MariusVanDerWijden Jun 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
220 changes: 220 additions & 0 deletions cmd/eofdump/eofparser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
// Copyright 2023 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.

package main

import (
"bufio"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"io/fs"
"os"
"path/filepath"
"strings"

"github.com/ethereum/go-ethereum/core/vm"
"github.com/urfave/cli/v2"
)

func init() {
jt = vm.NewPragueEOFInstructionSetForTesting()
}

var (
jt vm.JumpTable
errorMap = map[string]int{
io.ErrUnexpectedEOF.Error(): 1,
vm.ErrInvalidMagic.Error(): 2,
vm.ErrInvalidVersion.Error(): 3,
vm.ErrMissingTypeHeader.Error(): 4,
vm.ErrInvalidTypeSize.Error(): 5,
vm.ErrMissingCodeHeader.Error(): 6,
vm.ErrInvalidCodeHeader.Error(): 7,
vm.ErrMissingDataHeader.Error(): 8,
vm.ErrMissingTerminator.Error(): 9,
vm.ErrTooManyInputs.Error(): 10,
vm.ErrTooManyOutputs.Error(): 11,
vm.ErrTooLargeMaxStackHeight.Error(): 12,
vm.ErrInvalidCodeSize.Error(): 13,
vm.ErrInvalidContainerSize.Error(): 14,
vm.ErrUndefinedInstruction.Error(): 15,
vm.ErrTruncatedImmediate.Error(): 16,
vm.ErrInvalidSectionArgument.Error(): 17,
vm.ErrInvalidJumpDest.Error(): 18,
vm.ErrConflictingStack.Error(): 19,
vm.ErrInvalidBranchCount.Error(): 20,
vm.ErrInvalidOutputs.Error(): 21,
vm.ErrInvalidMaxStackHeight.Error(): 22,
vm.ErrInvalidCodeTermination.Error(): 23,
vm.ErrUnreachableCode.Error(): 24,
}
)

type RefTests struct {
Vectors map[string]EOFTest `json:"vectors"`
}

type EOFTest struct {
Code string `json:"code"`
Results map[string]etResult `json:"results"`
}

type etResult struct {
Result bool `json:"result"`
Exception string `json:"exception,omitempty"`
}

func eofParser(ctx *cli.Context) error {
// If `--hex` is set, parse and validate the hex string argument.
if ctx.IsSet(HexFlag.Name) {
if _, err := parseAndValidate(ctx.String(HexFlag.Name)); err != nil {
if err2 := errors.Unwrap(err); err2 != nil {
err = err2
}
return fmt.Errorf("err(%d): %w", errorMap[err.Error()], err)
}
fmt.Println("ok.")
return nil
}

// If `--test` is set, parse and validate the reference test at the provided path.
if ctx.IsSet(RefTestFlag.Name) {
file := ctx.String(RefTestFlag.Name)
if info, err := os.Stat(file); err != nil {
return err
} else if !info.IsDir() {
src, err := os.ReadFile(file)
if err != nil {
return err
}
return ExecuteTest(src)
} else {
return filepath.Walk(file, func(path string, info fs.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
fmt.Printf("Executing Tests: %v\n", info.Name())
src, err := os.ReadFile(path)
if err != nil {
return err
}
return ExecuteTest(src)
})
}

}

// If neither are passed in, read input from stdin.
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
t := strings.TrimSpace(scanner.Text())
if len(t) == 0 || t[0] == '#' {
continue
}
if _, err := parseAndValidate(t); err != nil {
if err2 := errors.Unwrap(err); err2 != nil {
err = err2
}
fmt.Fprintf(os.Stderr, "err(%d): %v\n", errorMap[err.Error()], err)
}
}

return nil
}

func ExecuteTest(src []byte) error {
var testsByName map[string]RefTests
if err := json.Unmarshal(src, &testsByName); err != nil {
return err
}
passed, total := 0, 0
for _, tests := range testsByName {
for name, tt := range tests.Vectors {
for fork, r := range tt.Results {
total++
// TODO(matt): all tests currently run against
// shanghai EOF, add support for custom forks.
_, err := parseAndValidate(tt.Code)
if err2 := errors.Unwrap(err); err2 != nil {
err = err2
}
if r.Result && err != nil {
fmt.Fprintf(os.Stderr, "%s, %s: expected success, got %v\n", name, fork, err)
continue
}
if !r.Result && err == nil {
fmt.Fprintf(os.Stderr, "%s, %s: expected error %d, got %v\n", name, fork, r.Exception, err)
continue
}
/*
// TODO (MariusVanDerWijden) reenable once tests have a decent error format
if !r.Result && err != nil && r.Exception != err.Error() {
fmt.Fprintf(os.Stderr, "%s, %s: expected error %d, got: err(%d): %v\n", name, fork, r.Exception, errorMap[err.Error()], err)
continue
}
*/
passed++
}
}
}
fmt.Printf("%d/%d tests passed.\n", passed, total)
return nil
}

func parseAndValidate(s string) (*vm.Container, error) {
if len(s) >= 2 && strings.HasPrefix(s, "0x") {
s = s[2:]
}
b, err := hex.DecodeString(s)
if err != nil {
return nil, fmt.Errorf("unable to decode data: %w", err)
}
var c vm.Container
if err := c.UnmarshalBinary(b); err != nil {
return nil, err
}
if err := c.ValidateCode(&jt); err != nil {
return nil, err
}
return &c, nil
}

func eofDump(ctx *cli.Context) error {
// If `--hex` is set, parse and validate the hex string argument.
if ctx.IsSet(HexFlag.Name) {
s := ctx.String(HexFlag.Name)
if len(s) >= 2 && strings.HasPrefix(s, "0x") {
s = s[2:]
}
b, err := hex.DecodeString(s)
if err != nil {
return fmt.Errorf("unable to decode data: %w", err)
}
var c vm.Container
if err := c.UnmarshalBinary(b); err != nil {
return err
}
fmt.Print(c.String())
return nil
}
return nil
}
65 changes: 65 additions & 0 deletions cmd/eofdump/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"fmt"
"os"

"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/urfave/cli/v2"
)

var app = flags.NewApp("the evm command line interface")

var (
RefTestFlag = &cli.StringFlag{
Name: "test",
Usage: "Path to EOF validation reference test.",
}
HexFlag = &cli.StringFlag{
Name: "hex",
Usage: "single container data parse and validation",
}
)

var eofParserCommand = &cli.Command{
Name: "eofparser",
Aliases: []string{"eof"},
Usage: "parses hex eof container and returns validation errors (if any)",
Action: eofParser,
Flags: []cli.Flag{
HexFlag,
RefTestFlag,
},
}

var eofDumpCommand = &cli.Command{
Name: "eofdump",
Usage: "parses hex eof container",
Action: eofDump,
Flags: []cli.Flag{
HexFlag,
},
}

func init() {
app.Commands = []*cli.Command{
eofParserCommand,
eofDumpCommand,
}
app.Before = func(ctx *cli.Context) error {
flags.MigrateGlobalFlags(ctx)
return debug.Setup(ctx)
}
app.After = func(ctx *cli.Context) error {
debug.Exit()
return nil
}
}

func main() {
if err := app.Run(os.Args); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
26 changes: 26 additions & 0 deletions cmd/evm/internal/t8ntool/transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ func Transition(ctx *cli.Context) error {
if err := applyCancunChecks(&prestate.Env, chainConfig); err != nil {
return err
}
if err := applyEOFChecks(&prestate, chainConfig); err != nil {
return err
}
// Run the test and aggregate the result
s, result, body, err := prestate.Apply(vmConfig, chainConfig, txIt, ctx.Int64(RewardFlag.Name), getTracer)
if err != nil {
Expand Down Expand Up @@ -285,6 +288,29 @@ func applyCancunChecks(env *stEnv, chainConfig *params.ChainConfig) error {
return nil
}

func applyEOFChecks(prestate *Prestate, chainConfig *params.ChainConfig) error {
// Sanity check pre-allocated EOF code to not panic in state transition.
if chainConfig.IsShanghai(big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp) {
for addr, acc := range prestate.Pre {
if vm.HasEOFByte(acc.Code) {
var (
c vm.Container
err error
)
err = c.UnmarshalBinary(acc.Code)
if err == nil {
jt := vm.NewPragueEOFInstructionSetForTesting()
err = c.ValidateCode(&jt)
}
if err != nil {
return NewError(ErrorConfig, fmt.Errorf("code at %s considered invalid: %v", addr, err))
}
}
}
}
return nil
}

type Alloc map[common.Address]types.Account

func (g Alloc) OnRoot(common.Hash) {}
Expand Down
15 changes: 15 additions & 0 deletions cmd/evm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,13 @@ import (
"fmt"
"math/big"
"os"
"strings"

"github.com/ethereum/go-ethereum/cmd/evm/internal/t8ntool"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/internal/debug"
"github.com/ethereum/go-ethereum/internal/flags"
"github.com/ethereum/go-ethereum/tests"
"github.com/urfave/cli/v2"

// Force-load the tracer engines to trigger registration
Expand Down Expand Up @@ -138,6 +141,18 @@ var (
Usage: "enable return data output",
Category: flags.VMCategory,
}
ForknameFlag = &cli.StringFlag{
Name: "state.fork",
Usage: fmt.Sprintf("Name of ruleset to use."+
"\n\tAvailable forknames:"+
"\n\t %v"+
"\n\tAvailable extra eips:"+
"\n\t %v"+
"\n\tSyntax <forkname>(+ExtraEip)",
strings.Join(tests.AvailableForks(), "\n\t "),
strings.Join(vm.ActivateableEips(), ", ")),
Value: "Shanghai",
}
)

var stateTransitionCommand = &cli.Command{
Expand Down
2 changes: 0 additions & 2 deletions cmd/evm/t8n_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -284,8 +284,6 @@ func TestT8n(t *testing.T) {
input: t8nInput{
"alloc.json", "txs_more.rlp", "env.json", "Cancun", "",
},
output: t8nOutput{alloc: true, result: true},
expOut: "exp.json",
},
} {
args := []string{"t8n"}
Expand Down