core/vm: fix opEOFCreate, correct initcode stuff

This commit is contained in:
Marius van der Wijden 2024-07-02 11:37:00 +02:00
parent abe9f28845
commit e9ad878b90
5 changed files with 45 additions and 24 deletions

View file

@ -72,6 +72,7 @@ type RefTests struct {
} }
type EOFTest struct { type EOFTest struct {
IsInitCode bool `json:"isInitCode"`
Code string `json:"code"` Code string `json:"code"`
Results map[string]etResult `json:"results"` Results map[string]etResult `json:"results"`
} }
@ -84,7 +85,7 @@ type etResult struct {
func eofParser(ctx *cli.Context) error { func eofParser(ctx *cli.Context) error {
// If `--hex` is set, parse and validate the hex string argument. // If `--hex` is set, parse and validate the hex string argument.
if ctx.IsSet(HexFlag.Name) { if ctx.IsSet(HexFlag.Name) {
if _, err := parseAndValidate(ctx.String(HexFlag.Name)); err != nil { if _, err := parseAndValidate(ctx.String(HexFlag.Name), false); err != nil {
if err2 := errors.Unwrap(err); err2 != nil { if err2 := errors.Unwrap(err); err2 != nil {
err = err2 err = err2
} }
@ -143,7 +144,7 @@ func eofParser(ctx *cli.Context) error {
if len(t) == 0 || t[0] == '#' { if len(t) == 0 || t[0] == '#' {
continue continue
} }
if _, err := parseAndValidate(t); err != nil { if _, err := parseAndValidate(t, false); err != nil {
if err2 := errors.Unwrap(err); err2 != nil { if err2 := errors.Unwrap(err); err2 != nil {
err = err2 err = err2
} }
@ -166,7 +167,7 @@ func ExecuteTest(src []byte) (int, int, error) {
total++ total++
// TODO(matt): all tests currently run against // TODO(matt): all tests currently run against
// shanghai EOF, add support for custom forks. // shanghai EOF, add support for custom forks.
_, err := parseAndValidate(tt.Code) _, err := parseAndValidate(tt.Code, tt.IsInitCode)
if err2 := errors.Unwrap(err); err2 != nil { if err2 := errors.Unwrap(err); err2 != nil {
err = err2 err = err2
} }
@ -193,7 +194,7 @@ func ExecuteTest(src []byte) (int, int, error) {
return passed, total, nil return passed, total, nil
} }
func parseAndValidate(s string) (*vm.Container, error) { func parseAndValidate(s string, isInitCode bool) (*vm.Container, error) {
if len(s) >= 2 && strings.HasPrefix(s, "0x") { if len(s) >= 2 && strings.HasPrefix(s, "0x") {
s = s[2:] s = s[2:]
} }
@ -202,7 +203,7 @@ func parseAndValidate(s string) (*vm.Container, error) {
return nil, fmt.Errorf("unable to decode data: %w", err) return nil, fmt.Errorf("unable to decode data: %w", err)
} }
var c vm.Container var c vm.Container
if err := c.UnmarshalBinary(b); err != nil { if err := c.UnmarshalBinary(b, isInitCode); err != nil {
return nil, err return nil, err
} }
if err := c.ValidateCode(&jt); err != nil { if err := c.ValidateCode(&jt); err != nil {
@ -223,7 +224,7 @@ func eofDump(ctx *cli.Context) error {
return fmt.Errorf("unable to decode data: %w", err) return fmt.Errorf("unable to decode data: %w", err)
} }
var c vm.Container var c vm.Container
if err := c.UnmarshalBinary(b); err != nil { if err := c.UnmarshalBinary(b, false); err != nil {
return err return err
} }
fmt.Print(c.String()) fmt.Print(c.String())

View file

@ -297,7 +297,7 @@ func applyEOFChecks(prestate *Prestate, chainConfig *params.ChainConfig) error {
c vm.Container c vm.Container
err error err error
) )
err = c.UnmarshalBinary(acc.Code) err = c.UnmarshalBinary(acc.Code, false)
if err == nil { if err == nil {
jt := vm.NewPragueEOFInstructionSetForTesting() jt := vm.NewPragueEOFInstructionSetForTesting()
err = c.ValidateCode(&jt) err = c.ValidateCode(&jt)

View file

@ -882,7 +882,6 @@ func opEOFCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
salt = scope.Stack.pop() salt = scope.Stack.pop()
offset, size = scope.Stack.pop(), scope.Stack.pop() offset, size = scope.Stack.pop(), scope.Stack.pop()
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64())) input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
gas = scope.Contract.Gas
) )
if int(idx) >= len(scope.Contract.Container.ContainerSections) { if int(idx) >= len(scope.Contract.Container.ContainerSections) {
return nil, fmt.Errorf("invalid subcontainer") return nil, fmt.Errorf("invalid subcontainer")
@ -895,6 +894,12 @@ func opEOFCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
if ok := scope.Contract.UseGas(hashingCharge, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified); !ok { if ok := scope.Contract.UseGas(hashingCharge, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified); !ok {
return nil, ErrGasUintOverflow return nil, ErrGasUintOverflow
} }
if interpreter.evm.Config.Tracer != nil {
if interpreter.evm.Config.Tracer != nil {
interpreter.evm.Config.Tracer.OnOpcode(*pc, byte(EOFCREATE), 0, hashingCharge, scope, interpreter.returnData, interpreter.evm.depth, nil)
}
}
gas := scope.Contract.Gas
// Reuse last popped value from stack // Reuse last popped value from stack
stackvalue := size stackvalue := size
// Apply EIP150 // Apply EIP150
@ -996,6 +1001,18 @@ func opReturnContract(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
if len(deployedCode) == 0 { if len(deployedCode) == 0 {
return nil, errors.New("nonexistant subcontainer") return nil, errors.New("nonexistant subcontainer")
} }
// Validate the subcontainer
var c Container
if err := c.UnmarshalBinary(deployedCode, true); err != nil {
panic(fmt.Sprintf("%x", deployedCode))
}
if err := c.ValidateCode(interpreter.tableEOF); err != nil {
return nil, err
}
if len(c.Data) < c.DataSize {
return nil, errors.New("invalid subcontainer")
}
c.DataSize = len(c.Data)
// Restore context // Restore context
var ( var (
last = len(scope.ReturnStack) - 1 last = len(scope.ReturnStack) - 1
@ -1004,7 +1021,9 @@ func opReturnContract(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
scope.ReturnStack = scope.ReturnStack[:last] scope.ReturnStack = scope.ReturnStack[:last]
scope.CodeSection = retCtx.Section scope.CodeSection = retCtx.Section
*pc = retCtx.Pc - 1 *pc = retCtx.Pc - 1
return deployedCode, errStopToken fmt.Printf("%v", c.MarshalBinary())
fmt.Printf("%v", deployedCode)
return c.MarshalBinary(), errStopToken
} }
func opDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {

View file

@ -142,7 +142,7 @@ func (c *Container) MarshalBinary() []byte {
} }
// UnmarshalBinary decodes an EOF container. // UnmarshalBinary decodes an EOF container.
func (c *Container) UnmarshalBinary(b []byte) error { func (c *Container) UnmarshalBinary(b []byte, isInitcode bool) error {
if !hasEOFMagic(b) { if !hasEOFMagic(b) {
return fmt.Errorf("%w: want %x", ErrInvalidMagic, eofMagic) return fmt.Errorf("%w: want %x", ErrInvalidMagic, eofMagic)
} }
@ -224,7 +224,11 @@ func (c *Container) UnmarshalBinary(b []byte) error {
if len(containerSizes) != 0 { if len(containerSizes) != 0 {
expectedSize += sum(containerSizes) expectedSize += sum(containerSizes)
} }
if len(b) < expectedSize-dataSize || len(b) > expectedSize { if len(b) < expectedSize-dataSize {
return fmt.Errorf("%w: have %d, want %d", ErrInvalidContainerSize, len(b), expectedSize)
}
// Only check that the expected size is not exceed on non-initcode
if !isInitcode && len(b) > expectedSize {
return fmt.Errorf("%w: have %d, want %d", ErrInvalidContainerSize, len(b), expectedSize) return fmt.Errorf("%w: have %d, want %d", ErrInvalidContainerSize, len(b), expectedSize)
} }
@ -278,7 +282,7 @@ func (c *Container) UnmarshalBinary(b []byte) error {
} }
c := new(Container) c := new(Container)
end := min(idx+size, len(b)) end := min(idx+size, len(b))
if err := c.UnmarshalBinary(b[idx:end]); err != nil { if err := c.UnmarshalBinary(b[idx:end], isInitcode); err != nil {
return fmt.Errorf("%w for section %d", err, i) return fmt.Errorf("%w for section %d", err, i)
} }
container = append(container, c) container = append(container, c)
@ -291,7 +295,10 @@ func (c *Container) UnmarshalBinary(b []byte) error {
} }
// Parse data section. // Parse data section.
end := min(idx+dataSize, len(b)) end := len(b)
if !isInitcode {
end = min(idx+dataSize, len(b))
}
c.Data = b[idx:end] c.Data = b[idx:end]
return nil return nil

View file

@ -487,7 +487,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
if isInitcodeEOF { if isInitcodeEOF {
// If the initcode is EOF, verify it is well-formed. // If the initcode is EOF, verify it is well-formed.
var c Container var c Container
if err := c.UnmarshalBinary(codeAndHash.code); err != nil { if err := c.UnmarshalBinary(codeAndHash.code, isInitcodeEOF); err != nil {
return nil, common.Address{}, gas, fmt.Errorf("%w: %v", ErrInvalidEOFInitcode, err) return nil, common.Address{}, gas, fmt.Errorf("%w: %v", ErrInvalidEOFInitcode, err)
} }
if err := c.ValidateCode(evm.interpreter.tableEOF); err != nil { if err := c.ValidateCode(evm.interpreter.tableEOF); err != nil {
@ -568,13 +568,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// Reject code starting with 0xEF if EIP-3541 is enabled. // Reject code starting with 0xEF if EIP-3541 is enabled.
if err == nil && len(ret) >= 1 && HasEOFByte(ret) { if err == nil && len(ret) >= 1 && HasEOFByte(ret) {
if evm.chainRules.IsShanghai { if evm.chainRules.IsShanghai {
var c Container // Don't reject EOF contracts after Shanghai
if err = c.UnmarshalBinary(ret); err == nil {
err = c.ValidateCode(evm.interpreter.tableEOF)
}
if err != nil {
err = fmt.Errorf("%w: %v", ErrInvalidEOF, err)
}
} else if evm.chainRules.IsLondon { } else if evm.chainRules.IsLondon {
err = ErrInvalidCode err = ErrInvalidCode
} }
@ -691,7 +685,7 @@ func (evm *EVM) GetVMContext() *tracing.VMContext {
func (evm *EVM) parseContainer(b []byte) *Container { func (evm *EVM) parseContainer(b []byte) *Container {
if evm.chainRules.IsPrague { if evm.chainRules.IsPrague {
var c Container var c Container
if err := c.UnmarshalBinary(b); err != nil && strings.HasPrefix(err.Error(), "invalid magic") { if err := c.UnmarshalBinary(b, false); err != nil && strings.HasPrefix(err.Error(), "invalid magic") {
return nil return nil
} else if err != nil { } else if err != nil {
// Code was already validated, so no other errors should be possible. // Code was already validated, so no other errors should be possible.