core/vm: parse containers on call

This commit is contained in:
Marius van der Wijden 2024-09-26 12:58:22 +02:00
parent 44125c2e45
commit d744516b74
2 changed files with 47 additions and 12 deletions

View File

@ -53,6 +53,7 @@ type Contract struct {
analysis bitvec // Locally cached result of JUMPDEST analysis analysis bitvec // Locally cached result of JUMPDEST analysis
Code []byte Code []byte
Container *Container
CodeHash common.Hash CodeHash common.Hash
CodeAddr *common.Address CodeAddr *common.Address
Input []byte Input []byte
@ -144,11 +145,12 @@ func (c *Contract) AsDelegate() *Contract {
} }
// GetOp returns the n'th element in the contract's byte array // GetOp returns the n'th element in the contract's byte array
func (c *Contract) GetOp(n uint64) OpCode { func (c *Contract) GetOp(n uint64, s uint64) OpCode {
if n < uint64(len(c.Code)) { 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 OpCode(c.Code[n])
} }
return STOP return STOP
} }
@ -193,10 +195,23 @@ func (c *Contract) Value() *uint256.Int {
return c.value 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 // SetCallCode sets the code of the contract and address of the backing data
// object // 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.Code = code
c.Container = container
c.CodeHash = hash c.CodeHash = hash
c.CodeAddr = addr c.CodeAddr = addr
} }

View File

@ -18,7 +18,9 @@ package vm
import ( import (
"errors" "errors"
"fmt"
"math/big" "math/big"
"strings"
"sync/atomic" "sync/atomic"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
@ -223,7 +225,7 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
// If the account has no code, we can abort here // If the account has no code, we can abort here
// The depth-check is already done, and precompiles handled above // The depth-check is already done, and precompiles handled above
contract := NewContract(caller, AccountRef(addrCopy), value, gas) contract := NewContract(caller, AccountRef(addrCopy), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code) contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code, evm.parseContainer(code))
ret, err = evm.interpreter.Run(contract, input, false) ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas gas = contract.Gas
} }
@ -286,7 +288,8 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
if witness := evm.StateDB.Witness(); witness != nil { if witness := evm.StateDB.Witness(); witness != nil {
witness.AddCode(evm.StateDB.GetCode(addrCopy)) witness.AddCode(evm.StateDB.GetCode(addrCopy))
} }
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) code := evm.StateDB.GetCode(addrCopy)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code, evm.parseContainer(code))
ret, err = evm.interpreter.Run(contract, input, false) ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas gas = contract.Gas
} }
@ -336,7 +339,8 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
if witness := evm.StateDB.Witness(); witness != nil { if witness := evm.StateDB.Witness(); witness != nil {
witness.AddCode(evm.StateDB.GetCode(addrCopy)) witness.AddCode(evm.StateDB.GetCode(addrCopy))
} }
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) code := evm.StateDB.GetCode(addrCopy)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code, evm.parseContainer(code))
ret, err = evm.interpreter.Run(contract, input, false) ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas gas = contract.Gas
} }
@ -394,7 +398,8 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
if witness := evm.StateDB.Witness(); witness != nil { if witness := evm.StateDB.Witness(); witness != nil {
witness.AddCode(evm.StateDB.GetCode(addrCopy)) witness.AddCode(evm.StateDB.GetCode(addrCopy))
} }
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy)) code := evm.StateDB.GetCode(addrCopy)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code, evm.parseContainer(code))
// When an error was returned by the EVM or when setting the creation code // When an error was returned by the EVM or when setting the creation code
// above we revert to the snapshot and consume any gas remaining. Additionally // above we revert to the snapshot and consume any gas remaining. Additionally
// when we're in Homestead this also counts for code storage gas errors. // when we're in Homestead this also counts for code storage gas errors.
@ -617,3 +622,18 @@ func (evm *EVM) GetVMContext() *tracing.VMContext {
StateDB: evm.StateDB, StateDB: evm.StateDB,
} }
} }
// parseContainer tries to parse an EOF container if the Shanghai fork is active. It expects the code to already be validated.
func (evm *EVM) parseContainer(b []byte) *Container {
if evm.chainRules.IsPrague {
var c Container
if err := c.UnmarshalBinary(b, false); err != nil && strings.HasPrefix(err.Error(), "invalid magic") {
return nil
} else if err != nil {
// Code was already validated, so no other errors should be possible.
panic(fmt.Sprintf("unexpected error: %v\ncode: %s\n", err, common.Bytes2Hex(b)))
}
return &c
}
return nil
}