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

core/vm: implement full EOF suite #26133

Closed
wants to merge 53 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
492843d
core/vm: add eof container
lightclient Dec 6, 2022
f256678
core/vm: add eof execution semantics
lightclient Dec 8, 2022
2579e9e
tests: add shanghai testing defn
lightclient Dec 22, 2022
07da06d
core/vm: add eof code validation
lightclient Dec 22, 2022
c90f24e
core/vm: properly validated deployed code if eof
lightclient Dec 29, 2022
0684011
core/vm: fix relative jump destination validation
lightclient Dec 29, 2022
3906b64
core/vm: do code validation on eof
lightclient Dec 29, 2022
05cf350
core/vm: fix linter and account for overflowing rjumpv case
lightclient Dec 29, 2022
d75812c
core/vm: add tests for deploying eof containers
lightclient Dec 29, 2022
4d9179d
core/vm: fix lints
lightclient Dec 29, 2022
2411f4d
core/vm: disallow eof deploying legacy code
lightclient Dec 29, 2022
c1eae0e
core/vm: parse relative args as ints in validation
lightclient Dec 30, 2022
836a7f8
core/vm: add invalid to jump table
lightclient Dec 30, 2022
afc52bf
core/vm: reorder stack height checks in eof validation
lightclient Dec 30, 2022
2cd5977
core/vm: follow rjumpv branches correctly
lightclient Dec 30, 2022
944ba9f
core/vm: make eof parser more paranoid
holiman Dec 30, 2022
3df0d42
core/vm: fix on eof
holiman Dec 30, 2022
723e363
core/vm: fix uint16 overflow
holiman Dec 30, 2022
f4188b2
core/vm: tests for codeBitmap/eofCodeBitmap
holiman Dec 30, 2022
b4c0136
core/vm: bound check rjumpv before code analysis
lightclient Dec 31, 2022
092750e
core/vm: fix fuzzer crashes
lightclient Dec 31, 2022
4d9b08f
core/vm: more fixes
lightclient Dec 31, 2022
e84268b
core/vm: more fixes
lightclient Dec 31, 2022
6a6af81
core/vm: bound type section inputs and outputs to 127
lightclient Jan 1, 2023
3492758
core/vm: exit stack validation on terminal op
lightclient Jan 2, 2023
60f1ed3
core/vm: reduce max_stack_height limit to 1023
lightclient Jan 2, 2023
c4f8993
core/vm: validate retf outputs
lightclient Jan 2, 2023
2c8a9ab
core/vm: account for input stack height in max stack height calculation
lightclient Jan 2, 2023
ae91086
core/vm: increment pos after callf in stack validation
lightclient Jan 2, 2023
96e435d
core/vm: flip comparison operator in stack validation for callf
lightclient Jan 2, 2023
3ab52d5
core/vm: clean up eof unmarshaling errors plus a few other fixes
lightclient Jan 2, 2023
c115b1a
core/vm: account for callf return value in stack analysis and some ot…
lightclient Jan 3, 2023
713eb5f
core/vm: fix lints
lightclient Jan 3, 2023
cd29313
core/vm: fix incorrect type
lightclient Jan 3, 2023
dff09a9
core/vm: clean up eof validation and remove jumpf
lightclient Jan 3, 2023
167e3b9
core/vm: account for callf inputs and add some comments
lightclient Jan 3, 2023
505db9d
cmd/evm: validate pre-allocated eof code in t8n
lightclient Jan 4, 2023
f7efe96
core/vm: ban eof code from running legacy initcode
lightclient Jan 4, 2023
bd082f5
core/vm: uncomment analysis tests
lightclient Jan 4, 2023
630be09
core/vm: don't consume all gas if eof initcode validation fails
lightclient Jan 4, 2023
2382f83
core/vm: check for full magic in initcode
lightclient Jan 4, 2023
f4c5cd7
core/vm: clean stop after retf
lightclient Jan 6, 2023
41cd50b
core/vm: add custom eof errors
lightclient Jan 6, 2023
50110b5
cmd/evm: add eofparser test tool to evm binary
lightclient Jan 9, 2023
db10933
core: fix eof blockchain test and reorder magic check
lightclient Jan 10, 2023
a229ad6
core/vm: don't update caller nonce on creation if eof validation fails
lightclient Jan 11, 2023
d410d2a
core/vm: clean up eof bitmap analysis
lightclient Jan 11, 2023
8fb979f
core/vm: remove custom parser error
lightclient Jan 11, 2023
e60f7ec
core/vm: don't use iota to figure out eip-4750 opcodes
lightclient Jan 13, 2023
55bff2b
core/vm: remove unused function
lightclient Jan 13, 2023
a1f2a17
core/vm: change formatter in error msg for invalid magic
lightclient Jan 17, 2023
e20edd6
core/vm: bump nonce for create tx with invalid eof
lightclient Jan 17, 2023
df23888
core/vm: don't double bump nonce when deployed eof is invalid in crea…
lightclient Jan 19, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
168 changes: 168 additions & 0 deletions cmd/evm/eofparser.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
// 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"
"os"
"strings"

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

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

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 EOFTest struct {
Code string `json:"code"`
Results map[string]etResult `json:"results"`
}

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

func eofParser(ctx *cli.Context) error {
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
log.Root().SetHandler(glogger)

// 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) {
src, err := os.ReadFile(ctx.String(RefTestFlag.Name))
if err != nil {
return err
}
var tests map[string]EOFTest
if err = json.Unmarshal(src, &tests); err != nil {
return err
}
passed, total := 0, 0
for name, tt := range tests {
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
}
if !r.Result && err != nil && r.Exception != errorMap[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
}

// 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 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
}
19 changes: 19 additions & 0 deletions cmd/evm/internal/t8ntool/transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,25 @@ func Transition(ctx *cli.Context) error {
return NewError(ErrorConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
}
}
// Sanity check pre-allocated EOF code to not panic in state transition.
if chainConfig.IsShanghai(big.NewInt(int64(prestate.Env.Number))) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think t8n tool should also check that before the EOF fork, code in prestate doesn't start with EF00

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.NewShanghaiEOFInstructionSetForTesting()
err = c.ValidateCode(&jt)
}
if err != nil {
return NewError(ErrorConfig, fmt.Errorf("code at %s considered invalid: %v", addr, err))
}
}
}
}
isMerged := chainConfig.TerminalTotalDifficulty != nil && chainConfig.TerminalTotalDifficulty.BitLen() == 0
env := prestate.Env
if isMerged {
Expand Down
36 changes: 36 additions & 0 deletions cmd/evm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ 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/flags"
"github.com/ethereum/go-ethereum/tests"
"github.com/urfave/cli/v2"
)

Expand Down Expand Up @@ -125,6 +128,26 @@ var (
Value: true,
Usage: "enable return data output",
}
HexFlag = &cli.StringFlag{
Name: "hex",
Usage: "single container data parse and validation",
}
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",
}
RefTestFlag = &cli.StringFlag{
Name: "test",
Usage: "Path to EOF validation reference test.",
}
)

var stateTransitionCommand = &cli.Command{
Expand Down Expand Up @@ -185,6 +208,18 @@ var blockBuilderCommand = &cli.Command{
},
}

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{
VerbosityFlag,
HexFlag,
RefTestFlag,
},
}

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

func init() {
Expand Down Expand Up @@ -221,6 +256,7 @@ func init() {
stateTransitionCommand,
transactionCommand,
blockBuilderCommand,
eofParserCommand,
}
}

Expand Down
8 changes: 8 additions & 0 deletions cmd/evm/t8n_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,14 @@ func TestT8n(t *testing.T) {
output: t8nOutput{alloc: true, result: true},
expOut: "exp.json",
},
{ // Validate pre-allocated EOF code
base: "./testdata/26",
input: t8nInput{
"alloc.json", "txs.json", "env.json", "Shanghai", "",
},
output: t8nOutput{alloc: true, result: false},
expExitCode: 3,
},
} {
args := []string{"t8n"}
args = append(args, tc.output.get()...)
Expand Down
14 changes: 14 additions & 0 deletions cmd/evm/testdata/26/alloc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "0x0",
"code": "0xef01",
"nonce": "0x1",
"storage": {}
},
"a94f5374fce5edbc8e2a8697c15331677e6ebf0c": {
"balance": "0x0",
"code": "0xef0001010008020002000700020300000000000002020100025959b0000250b101b1",
"nonce": "0x1",
"storage": {}
}
}
11 changes: 11 additions & 0 deletions cmd/evm/testdata/26/env.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"currentDifficulty": null,
"currentRandom": "0xdeadc0de",
"currentGasLimit": "0x750a163df65e8a",
"parentBaseFee": "0x500",
"parentGasUsed": "0x0",
"parentGasLimit": "0x750a163df65e8a",
"currentNumber": "1",
"currentTimestamp": "1000"
}
1 change: 1 addition & 0 deletions cmd/evm/testdata/26/txs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
Loading