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 eof opcodes #30511

Open
wants to merge 36 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
44125c2
core/vm: add analysis tests
MariusVanDerWijden Sep 26, 2024
d744516
core/vm: parse containers on call
MariusVanDerWijden Sep 26, 2024
d23c5e5
core: special case on creation for eof code
MariusVanDerWijden Sep 26, 2024
2297ef6
core/vm: add ReturnStack etc to ScopeContext
MariusVanDerWijden Sep 26, 2024
8374793
core/vm: add new eof-behavior to opDelegateCall
MariusVanDerWijden Sep 26, 2024
3012db5
core/vm: added new behavior to legacy opcodes
MariusVanDerWijden Sep 26, 2024
5bd220b
core/vm: added opSwapN,Exchange,DupN opcodes
MariusVanDerWijden Sep 26, 2024
866ad95
core/vm: added RJUMP,RJUMPI,RJUMPV opcodes
MariusVanDerWijden Sep 26, 2024
0263062
core/vm: added CALLF,RETF opcodes
MariusVanDerWijden Sep 26, 2024
a58ba40
core/vm: added JUMPF opcodes
MariusVanDerWijden Sep 26, 2024
9fa14a1
core/vm: added EOFCREATE opcode
MariusVanDerWijden Sep 26, 2024
5992baf
core/vm: added RETURNCONTRACT opcode
MariusVanDerWijden Sep 26, 2024
bb1885c
core/vm: added DATALOAD,DATALOADN,DATASIZE,DATACOPY opcode
MariusVanDerWijden Sep 26, 2024
c7db447
core/vm: added RETURNDATALOAD opcode
MariusVanDerWijden Sep 26, 2024
d206fba
core/vm: added EXTCALL,EXTDELEGATECALL,EXTSTATICCALL opcode
MariusVanDerWijden Sep 26, 2024
3d82fb1
core/vm: add gas computation for EOFCREATE, EXT{CALL,DELEGATECALL,STA…
MariusVanDerWijden Sep 26, 2024
ce4a1c5
core: modify eoa check in statetransition
MariusVanDerWijden Sep 26, 2024
55e2e27
core: fix a bug in state transition
MariusVanDerWijden Sep 26, 2024
1d51444
core/vm: fix create rules
MariusVanDerWijden Sep 26, 2024
3e56fde
tests: changes needed to pass tests, drop before merging
MariusVanDerWijden Sep 26, 2024
d0dfef2
core/vm: fix eofcreate auxdata
MariusVanDerWijden Sep 26, 2024
1b300b2
core/vm: fix rebasing issue
MariusVanDerWijden Oct 4, 2024
d2af47a
core/vm: fix rebasing issue
MariusVanDerWijden Oct 4, 2024
a73a8e7
happy lint, happy life
MariusVanDerWijden Oct 5, 2024
e027042
core: addressed review comments
MariusVanDerWijden Oct 11, 2024
8b40d4e
core: fix new error string
MariusVanDerWijden Oct 11, 2024
1b99404
Restore state test transction context
shemnon Oct 15, 2024
ff7ac51
limit scope to state tests
shemnon Oct 15, 2024
56ff85a
Merge pull request #58 from shemnon/state-tests-origin
MariusVanDerWijden Oct 16, 2024
1156ca4
Limit EXTCODECOPY to eof bytes
shemnon Oct 25, 2024
f0dc433
Merge pull request #60 from shemnon/eof/extcodecopy-oob
MariusVanDerWijden Feb 4, 2025
dbb4ef5
Merge branch 'master' into eof-opcodes
MariusVanDerWijden Feb 4, 2025
6fe254e
all: fix merging issues
MariusVanDerWijden Feb 4, 2025
1c931f0
core/vm: revert merge errors
MariusVanDerWijden Feb 4, 2025
10cd083
core/vm: revert merge errors
MariusVanDerWijden Feb 4, 2025
30b642b
applied suggestion
MariusVanDerWijden Feb 4, 2025
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
27 changes: 27 additions & 0 deletions cmd/evm/internal/t8ntool/transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,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
}

// Configure tracer
if ctx.IsSet(TraceTracerFlag.Name) { // Custom tracing
Expand Down Expand Up @@ -265,6 +268,30 @@ func applyCancunChecks(env *stEnv, chainConfig *params.ChainConfig) error {
return nil
}

// applyEOFChecks does a sanity check of the prestate EOF code validity, to avoid panic during state transition.
func applyEOFChecks(prestate *Prestate, chainConfig *params.ChainConfig) error {
if !chainConfig.IsShanghai(big.NewInt(int64(prestate.Env.Number)), prestate.Env.Timestamp) {
return nil
}
for addr, acc := range prestate.Pre {
if vm.HasEOFByte(acc.Code) {
var (
c vm.Container
err error
)
err = c.UnmarshalBinary(acc.Code, false)
if err == nil {
jt := vm.NewEOFInstructionSetForTesting()
err = c.ValidateCode(&jt, false)
}
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
10 changes: 9 additions & 1 deletion core/state_transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package core

import (
"bytes"
"errors"
"fmt"
"math"
"math/big"
Expand Down Expand Up @@ -484,7 +485,14 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
vmerr error // vm errors do not effect consensus and are therefore not assigned to err
)
if contractCreation {
ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value)
ret, _, st.gasRemaining, vmerr = st.evm.Create(sender, msg.Data, st.gasRemaining, value, rules.IsPrague)
// Special case for EOF, if the initcode or deployed code is
// invalid, the tx is considered valid (so update nonce), but
// gas for initcode execution is not consumed.
// Only intrinsic creation transaction costs are charged.
if errors.Is(vmerr, vm.ErrInvalidEOFInitcode) {
st.state.SetNonce(msg.From, st.state.GetNonce(sender.Address())+1)
}
} else {
// Increment the nonce for the next transaction.
st.state.SetNonce(msg.From, st.state.GetNonce(msg.From)+1)
Expand Down
76 changes: 76 additions & 0 deletions core/vm/analysis_eof_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Copyright 2024 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 vm

import (
"testing"

"github.com/ethereum/go-ethereum/common"
)

func TestEOFAnalysis(t *testing.T) {
tests := []struct {
code []byte
exp byte
which int
}{
{[]byte{byte(RJUMP), 0x01, 0x01, 0x01}, 0b0000_0110, 0},
{[]byte{byte(RJUMPI), byte(RJUMP), byte(RJUMP), byte(RJUMPI)}, 0b0011_0110, 0},
{[]byte{byte(RJUMPV), 0x02, byte(RJUMP), 0x00, byte(RJUMPI), 0x00}, 0b0011_1110, 0},
}
for i, test := range tests {
ret := eofCodeBitmap(test.code)
if ret[test.which] != test.exp {
t.Fatalf("test %d: expected %x, got %02x", i, test.exp, ret[test.which])
}
}
}

func BenchmarkJumpdestOpEOFAnalysis(bench *testing.B) {
var op OpCode
bencher := func(b *testing.B) {
code := make([]byte, analysisCodeSize)
b.SetBytes(analysisCodeSize)
for i := range code {
code[i] = byte(op)
}
bits := make(bitvec, len(code)/8+1+4)
b.ResetTimer()
for i := 0; i < b.N; i++ {
clear(bits)
eofCodeBitmapInternal(code, bits)
}
}
for op = PUSH1; op <= PUSH32; op++ {
bench.Run(op.String(), bencher)
}
op = JUMPDEST
bench.Run(op.String(), bencher)
op = STOP
bench.Run(op.String(), bencher)
op = RJUMPV
bench.Run(op.String(), bencher)
op = EOFCREATE
bench.Run(op.String(), bencher)
}

func FuzzCodeAnalysis(f *testing.F) {
f.Add(common.FromHex("5e30303030"))
f.Fuzz(func(t *testing.T, data []byte) {
eofCodeBitmap(data)
})
}
28 changes: 0 additions & 28 deletions core/vm/analysis_legacy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,31 +105,3 @@ func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
op = STOP
bench.Run(op.String(), bencher)
}

func BenchmarkJumpdestOpEOFAnalysis(bench *testing.B) {
var op OpCode
bencher := func(b *testing.B) {
code := make([]byte, analysisCodeSize)
b.SetBytes(analysisCodeSize)
for i := range code {
code[i] = byte(op)
}
bits := make(bitvec, len(code)/8+1+4)
b.ResetTimer()
for i := 0; i < b.N; i++ {
clear(bits)
eofCodeBitmapInternal(code, bits)
}
}
for op = PUSH1; op <= PUSH32; op++ {
bench.Run(op.String(), bencher)
}
op = JUMPDEST
bench.Run(op.String(), bencher)
op = STOP
bench.Run(op.String(), bencher)
op = RJUMPV
bench.Run(op.String(), bencher)
op = EOFCREATE
bench.Run(op.String(), bencher)
}
31 changes: 23 additions & 8 deletions core/vm/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,11 @@ type Contract struct {
jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis.
analysis bitvec // Locally cached result of JUMPDEST analysis

Code []byte
CodeHash common.Hash
CodeAddr *common.Address
Input []byte
Code []byte
Container *Container
CodeHash common.Hash
CodeAddr *common.Address
Input []byte

// is the execution frame represented by this object a contract deployment
IsDeployment bool
Expand Down Expand Up @@ -145,11 +146,12 @@ func (c *Contract) AsDelegate() *Contract {
}

// GetOp returns the n'th element in the contract's byte array
func (c *Contract) GetOp(n uint64) OpCode {
if n < uint64(len(c.Code)) {
func (c *Contract) GetOp(n uint64, s uint64) OpCode {
if c.IsEOF() && n < uint64(len(c.Container.codeSections[s])) {
return OpCode(c.Container.codeSections[s][n])
} else if n < uint64(len(c.Code)) {
return OpCode(c.Code[n])
}

return STOP
}

Expand Down Expand Up @@ -194,10 +196,23 @@ func (c *Contract) Value() *uint256.Int {
return c.value
}

// IsEOF returns whether the contract is EOF.
func (c *Contract) IsEOF() bool {
return c.Container != nil
}

func (c *Contract) CodeAt(section uint64) []byte {
if c.Container == nil {
return c.Code
}
return c.Container.codeSections[section]
}

// SetCallCode sets the code of the contract and address of the backing data
// object
func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte, container *Container) {
c.Code = code
c.Container = container
c.CodeHash = hash
c.CodeAddr = addr
}
Expand Down
Loading