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

@ -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
@ -144,11 +145,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
}
@ -193,10 +195,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
}

View File

@ -18,7 +18,9 @@ package vm
import (
"errors"
"fmt"
"math/big"
"strings"
"sync/atomic"
"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
// The depth-check is already done, and precompiles handled above
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)
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 {
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)
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 {
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)
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 {
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
// 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.
@ -617,3 +622,18 @@ func (evm *EVM) GetVMContext() *tracing.VMContext {
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
}