diff --git a/core/vm/contract.go b/core/vm/contract.go index cfda75b27e..fb5b12c2a9 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -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 } diff --git a/core/vm/evm.go b/core/vm/evm.go index 616668d565..c717f55826 100644 --- a/core/vm/evm.go +++ b/core/vm/evm.go @@ -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 +}