Merge pull request #61 from MariusVanDerWijden/shemnon-legacy-pc

Move EOF interpreter to use same PC semantics as legacy EVM
This commit is contained in:
Marius van der Wijden 2025-02-13 10:43:08 +01:00 committed by GitHub
commit b78b30717c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 229 additions and 165 deletions

View file

@ -146,10 +146,8 @@ 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, s uint64) OpCode { func (c *Contract) GetOp(n uint64) OpCode {
if c.IsEOF() && n < uint64(len(c.Container.codeSections[s])) { if n < uint64(len(c.Code)) {
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
@ -201,13 +199,6 @@ func (c *Contract) IsEOF() bool {
return c.Container != nil 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, container *Container) { func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte, container *Container) {

View file

@ -66,12 +66,12 @@ func isEOFVersion1(code []byte) bool {
// Container is an EOF container object. // Container is an EOF container object.
type Container struct { type Container struct {
types []*functionMetadata types []*functionMetadata
codeSections [][]byte codeSectionOffsets []int // contains all the offsets of the codeSections. The last item marks the end
subContainers []*Container subContainers []*Container
subContainerCodes [][]byte subContainerOffsets []int // contains all the offsets of the subContainers. The last item marks the end
data []byte dataSize int // might be more than len(data)
dataSize int // might be more than len(data) rawContainer []byte
} }
// functionMetadata is an EOF function signature. // functionMetadata is an EOF function signature.
@ -105,8 +105,44 @@ func (meta *functionMetadata) checkStackMax(stackMax int) error {
return nil return nil
} }
// codeSectionAt returns the code section at index.
// returns an empty slice if the index is out of bounds.
func (c *Container) codeSectionAt(index int) []byte {
if index >= len(c.codeSectionOffsets)-1 || index < 0 {
return c.rawContainer[0:0]
}
return c.rawContainer[c.codeSectionOffsets[index]:c.codeSectionOffsets[index+1]]
}
// subContainerAt returns the sub container at index.
// returns an empty slice if the index is out of bounds.
func (c *Container) subContainerAt(index int) []byte {
if index >= len(c.subContainerOffsets)-1 || index < 0 {
return c.rawContainer[0:0]
}
return c.rawContainer[c.subContainerOffsets[index]:c.subContainerOffsets[index+1]]
}
func (c *Container) dataOffset() int {
if len(c.subContainerOffsets) > 0 {
return c.subContainerOffsets[len(c.subContainerOffsets)-1]
}
return c.codeSectionOffsets[len(c.codeSectionOffsets)-1]
}
func (c *Container) dataLen() int {
return len(c.rawContainer) - c.dataOffset()
}
func (c *Container) getDataAt(offset, length uint64) []byte {
return getData(c.rawContainer, uint64(c.dataOffset())+offset, length)
}
// MarshalBinary encodes an EOF container into binary format. // MarshalBinary encodes an EOF container into binary format.
func (c *Container) MarshalBinary() []byte { func (c *Container) MarshalBinary() []byte {
// Drop the end markers
codeSectionOffsets := c.codeSectionOffsets[:len(c.codeSectionOffsets)-1]
// Build EOF prefix. // Build EOF prefix.
b := make([]byte, 2) b := make([]byte, 2)
copy(b, eofMagic) copy(b, eofMagic)
@ -116,9 +152,9 @@ func (c *Container) MarshalBinary() []byte {
b = append(b, kindTypes) b = append(b, kindTypes)
b = binary.BigEndian.AppendUint16(b, uint16(len(c.types)*4)) b = binary.BigEndian.AppendUint16(b, uint16(len(c.types)*4))
b = append(b, kindCode) b = append(b, kindCode)
b = binary.BigEndian.AppendUint16(b, uint16(len(c.codeSections))) b = binary.BigEndian.AppendUint16(b, uint16(len(codeSectionOffsets)))
for _, codeSection := range c.codeSections { for s := range codeSectionOffsets {
b = binary.BigEndian.AppendUint16(b, uint16(len(codeSection))) b = binary.BigEndian.AppendUint16(b, uint16(len(c.codeSectionAt(s))))
} }
var encodedContainer [][]byte var encodedContainer [][]byte
if len(c.subContainers) != 0 { if len(c.subContainers) != 0 {
@ -138,13 +174,13 @@ func (c *Container) MarshalBinary() []byte {
for _, ty := range c.types { for _, ty := range c.types {
b = append(b, []byte{ty.inputs, ty.outputs, byte(ty.maxStackHeight >> 8), byte(ty.maxStackHeight & 0x00ff)}...) b = append(b, []byte{ty.inputs, ty.outputs, byte(ty.maxStackHeight >> 8), byte(ty.maxStackHeight & 0x00ff)}...)
} }
for _, code := range c.codeSections { for s := range codeSectionOffsets {
b = append(b, code...) b = append(b, c.codeSectionAt(s)...)
} }
for _, section := range encodedContainer { for _, section := range encodedContainer {
b = append(b, section...) b = append(b, section...)
} }
b = append(b, c.data...) b = append(b, c.rawContainer[c.dataOffset():]...)
return b return b
} }
@ -282,21 +318,22 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool)
// Parse code sections. // Parse code sections.
idx += typesSize idx += typesSize
codeSections := make([][]byte, len(codeSizes)) codeSectionOffsets := make([]int, len(codeSizes))
for i, size := range codeSizes { for i, size := range codeSizes {
if size == 0 { if size == 0 {
return fmt.Errorf("%w for section %d: size must not be 0", errInvalidCodeSize, i) return fmt.Errorf("%w for section %d: size must not be 0", errInvalidCodeSize, i)
} }
codeSections[i] = b[idx : idx+size] codeSectionOffsets[i] = idx
idx += size idx += size
} }
c.codeSections = codeSections // add the end marker to the codeSection offsets
c.codeSectionOffsets = append(codeSectionOffsets, idx)
// Parse the optional container sizes. // Parse the optional container sizes.
if len(containerSizes) != 0 { if len(containerSizes) != 0 {
if len(containerSizes) > maxContainerSections { if len(containerSizes) > maxContainerSections {
return fmt.Errorf("%w number of container section exceed: %v: have %v", errInvalidContainerSectionSize, maxContainerSections, len(containerSizes)) return fmt.Errorf("%w number of container section exceed: %v: have %v", errInvalidContainerSectionSize, maxContainerSections, len(containerSizes))
} }
subContainerCodes := make([][]byte, 0, len(containerSizes)) subContainerOffsets := make([]int, 0, len(containerSizes))
subContainers := make([]*Container, 0, len(containerSizes)) subContainers := make([]*Container, 0, len(containerSizes))
for i, size := range containerSizes { for i, size := range containerSizes {
if size == 0 || idx+size > len(b) { if size == 0 || idx+size > len(b) {
@ -311,23 +348,21 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool)
return err return err
} }
subContainers = append(subContainers, subC) subContainers = append(subContainers, subC)
subContainerCodes = append(subContainerCodes, b[idx:end]) subContainerOffsets = append(subContainerOffsets, idx)
idx += size idx += size
} }
c.subContainers = subContainers c.subContainers = subContainers
c.subContainerCodes = subContainerCodes // add the end marker to the subContainer offsets
c.subContainerOffsets = append(subContainerOffsets, idx)
} }
//Parse data section. //Parse data section.
end := len(b)
if !isInitcode {
end = min(idx+dataSize, len(b))
}
if topLevel && len(b) != idx+dataSize { if topLevel && len(b) != idx+dataSize {
return errTruncatedTopLevelContainer return errTruncatedTopLevelContainer
} }
c.data = b[idx:end]
c.rawContainer = b
return nil return nil
} }
@ -355,7 +390,7 @@ func (c *Container) validateSubContainer(jt *JumpTable, refBy int) error {
// should not mean 2 and 3 should be visited twice // should not mean 2 and 3 should be visited twice
var ( var (
index = toVisit[0] index = toVisit[0]
code = c.codeSections[index] code = c.codeSectionAt(index)
) )
if _, ok := visited[index]; !ok { if _, ok := visited[index]; !ok {
res, err := validateCode(code, index, c, jt, refBy == refByEOFCreate) res, err := validateCode(code, index, c, jt, refBy == refByEOFCreate)
@ -387,7 +422,7 @@ func (c *Container) validateSubContainer(jt *JumpTable, refBy int) error {
toVisit = toVisit[1:] toVisit = toVisit[1:]
} }
// Make sure every code section is visited at least once. // Make sure every code section is visited at least once.
if len(visited) != len(c.codeSections) { if len(visited) != len(c.codeSectionOffsets)-1 {
return errUnreachableCode return errUnreachableCode
} }
for idx, container := range c.subContainers { for idx, container := range c.subContainers {
@ -464,6 +499,8 @@ func sum(list []int) (s int) {
} }
func (c *Container) String() string { func (c *Container) String() string {
// Drop the end markers
codeSectionOffsets := c.codeSectionOffsets[:len(c.codeSectionOffsets)-1]
var output = []string{ var output = []string{
"Header", "Header",
fmt.Sprintf(" - EOFMagic: %02x", eofMagic), fmt.Sprintf(" - EOFMagic: %02x", eofMagic),
@ -471,14 +508,13 @@ func (c *Container) String() string {
fmt.Sprintf(" - KindType: %02x", kindTypes), fmt.Sprintf(" - KindType: %02x", kindTypes),
fmt.Sprintf(" - TypesSize: %04x", len(c.types)*4), fmt.Sprintf(" - TypesSize: %04x", len(c.types)*4),
fmt.Sprintf(" - KindCode: %02x", kindCode), fmt.Sprintf(" - KindCode: %02x", kindCode),
fmt.Sprintf(" - Number of code sections: %d", len(codeSectionOffsets)),
fmt.Sprintf(" - KindData: %02x", kindData), fmt.Sprintf(" - KindData: %02x", kindData),
fmt.Sprintf(" - DataSize: %04x", len(c.data)), fmt.Sprintf(" - DataSize: %04x", c.dataSize),
fmt.Sprintf(" - Number of code sections: %d", len(c.codeSections)),
} }
for i, code := range c.codeSections { for i := range codeSectionOffsets {
output = append(output, fmt.Sprintf(" - Code section %d length: %04x", i, len(code))) output = append(output, fmt.Sprintf(" - Code section %d length: %04x", i, len(c.codeSectionAt(i))))
} }
output = append(output, fmt.Sprintf(" - Number of subcontainers: %d", len(c.subContainers))) output = append(output, fmt.Sprintf(" - Number of subcontainers: %d", len(c.subContainers)))
if len(c.subContainers) > 0 { if len(c.subContainers) > 0 {
for i, section := range c.subContainers { for i, section := range c.subContainers {
@ -490,12 +526,12 @@ func (c *Container) String() string {
output = append(output, fmt.Sprintf(" - Type %v: %x", i, output = append(output, fmt.Sprintf(" - Type %v: %x", i,
[]byte{typ.inputs, typ.outputs, byte(typ.maxStackHeight >> 8), byte(typ.maxStackHeight & 0x00ff)})) []byte{typ.inputs, typ.outputs, byte(typ.maxStackHeight >> 8), byte(typ.maxStackHeight & 0x00ff)}))
} }
for i, code := range c.codeSections { for i := range codeSectionOffsets {
output = append(output, fmt.Sprintf(" - Code section %d: %#x", i, code)) output = append(output, fmt.Sprintf(" - Code section %d: %#x", i, c.codeSectionAt(i)))
} }
for i, section := range c.subContainers { for i, section := range c.subContainers {
output = append(output, fmt.Sprintf(" - Subcontainer %d: %x", i, section.MarshalBinary())) output = append(output, fmt.Sprintf(" - Subcontainer %d: %x", i, section.MarshalBinary()))
} }
output = append(output, fmt.Sprintf(" - Data: %#x", c.data)) output = append(output, fmt.Sprintf(" - Data: %#x", c.rawContainer[c.dataOffset():]))
return strings.Join(output, "\n") return strings.Join(output, "\n")
} }

View file

@ -31,8 +31,7 @@ import (
// opRjump implements the RJUMP opcode. // opRjump implements the RJUMP opcode.
func opRjump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opRjump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
code = scope.Contract.CodeAt(scope.CodeSection) offset = parseInt16(scope.Contract.Code[*pc+1:])
offset = parseInt16(code[*pc+1:])
) )
// move pc past op and operand (+3), add relative offset, subtract 1 to // move pc past op and operand (+3), add relative offset, subtract 1 to
// account for interpreter loop. // account for interpreter loop.
@ -54,8 +53,7 @@ func opRjumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
// opRjumpv implements the RJUMPV opcode // opRjumpv implements the RJUMPV opcode
func opRjumpv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opRjumpv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
code = scope.Contract.CodeAt(scope.CodeSection) maxIndex = uint64(scope.Contract.Code[*pc+1]) + 1
maxIndex = uint64(code[*pc+1]) + 1
idx = scope.Stack.pop() idx = scope.Stack.pop()
) )
if idx, overflow := idx.Uint64WithOverflow(); overflow || idx >= maxIndex { if idx, overflow := idx.Uint64WithOverflow(); overflow || idx >= maxIndex {
@ -64,7 +62,7 @@ func opRjumpv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
*pc += 1 + maxIndex*2 *pc += 1 + maxIndex*2
return nil, nil return nil, nil
} }
offset := parseInt16(code[*pc+2+2*idx.Uint64():]) offset := parseInt16(scope.Contract.Code[*pc+2+2*idx.Uint64():])
// move pc past op and count byte (2), move past count number of 16bit offsets (count*2), add relative offset, subtract 1 to // move pc past op and count byte (2), move past count number of 16bit offsets (count*2), add relative offset, subtract 1 to
// account for interpreter loop. // account for interpreter loop.
*pc = uint64(int64(*pc+2+maxIndex*2) + int64(offset) - 1) *pc = uint64(int64(*pc+2+maxIndex*2) + int64(offset) - 1)
@ -74,9 +72,8 @@ func opRjumpv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
// opCallf implements the CALLF opcode // opCallf implements the CALLF opcode
func opCallf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opCallf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
code = scope.Contract.CodeAt(scope.CodeSection) idx = binary.BigEndian.Uint16(scope.Contract.Code[*pc+1:])
idx = binary.BigEndian.Uint16(code[*pc+1:]) typ = scope.Contract.Container.types[idx]
typ = scope.Contract.Container.types[idx]
) )
if scope.Stack.len()+int(typ.maxStackHeight)-int(typ.inputs) > 1024 { if scope.Stack.len()+int(typ.maxStackHeight)-int(typ.inputs) > 1024 {
return nil, fmt.Errorf("stack overflow") return nil, fmt.Errorf("stack overflow")
@ -91,7 +88,7 @@ func opCallf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
} }
scope.ReturnStack = append(scope.ReturnStack, retCtx) scope.ReturnStack = append(scope.ReturnStack, retCtx)
scope.CodeSection = uint64(idx) scope.CodeSection = uint64(idx)
*pc = uint64(math.MaxUint64) *pc = uint64(scope.Contract.Container.codeSectionOffsets[idx]) - 1
return nil, nil return nil, nil
} }
@ -111,15 +108,14 @@ func opRetf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
// opJumpf implements the JUMPF opcode // opJumpf implements the JUMPF opcode
func opJumpf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opJumpf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
code = scope.Contract.CodeAt(scope.CodeSection) idx = binary.BigEndian.Uint16(scope.Contract.Code[*pc+1:])
idx = binary.BigEndian.Uint16(code[*pc+1:]) typ = scope.Contract.Container.types[idx]
typ = scope.Contract.Container.types[idx]
) )
if scope.Stack.len()+int(typ.maxStackHeight)-int(typ.inputs) > 1024 { if scope.Stack.len()+int(typ.maxStackHeight)-int(typ.inputs) > 1024 {
return nil, fmt.Errorf("stack overflow") return nil, fmt.Errorf("stack overflow")
} }
scope.CodeSection = uint64(idx) scope.CodeSection = uint64(idx)
*pc = uint64(math.MaxUint64) *pc = uint64(scope.Contract.Container.codeSectionOffsets[idx]) - 1
return nil, nil return nil, nil
} }
@ -129,20 +125,20 @@ func opEOFCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
return nil, ErrWriteProtection return nil, ErrWriteProtection
} }
var ( var (
code = scope.Contract.CodeAt(scope.CodeSection) idx = scope.Contract.Code[*pc+1]
idx = code[*pc+1]
value = scope.Stack.pop() value = scope.Stack.pop()
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(offset.Uint64(), size.Uint64()) input = scope.Memory.GetCopy(offset.Uint64(), size.Uint64())
) )
if int(idx) >= len(scope.Contract.Container.subContainerCodes) { if int(idx) >= len(scope.Contract.Container.subContainerOffsets)-1 {
return nil, fmt.Errorf("invalid subcontainer") return nil, fmt.Errorf("invalid subcontainer")
} }
subContainer := scope.Contract.Container.subContainerAt(int(idx))
// Deduct hashing charge // Deduct hashing charge
// Since size <= params.MaxInitCodeSize, these multiplication cannot overflow // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
hashingCharge := (params.Keccak256WordGas) * ((uint64(len(scope.Contract.Container.subContainerCodes[idx])) + 31) / 32) hashingCharge := (params.Keccak256WordGas) * ((uint64(len(subContainer)) + 31) / 32)
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
} }
@ -159,7 +155,7 @@ func opEOFCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation2) scope.Contract.UseGas(gas, interpreter.evm.Config.Tracer, tracing.GasChangeCallContractCreation2)
// Skip the immediate // Skip the immediate
*pc += 1 *pc += 1
res, addr, returnGas, suberr := interpreter.evm.EOFCreate(scope.Contract, input, scope.Contract.Container.subContainerCodes[idx], gas, &value, &salt) res, addr, returnGas, suberr := interpreter.evm.EOFCreate(scope.Contract, input, subContainer, gas, &value, &salt)
if suberr != nil { if suberr != nil {
stackvalue.Clear() stackvalue.Clear()
} else { } else {
@ -182,16 +178,15 @@ func opReturnContract(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
return nil, errors.New("returncontract in non-initcode mode") return nil, errors.New("returncontract in non-initcode mode")
} }
var ( var (
code = scope.Contract.CodeAt(scope.CodeSection) idx = scope.Contract.Code[*pc+1]
idx = code[*pc+1]
offset = scope.Stack.pop() offset = scope.Stack.pop()
size = scope.Stack.pop() size = scope.Stack.pop()
) )
if int(idx) >= len(scope.Contract.Container.subContainerCodes) { if int(idx) >= len(scope.Contract.Container.subContainerOffsets)-1 {
return nil, fmt.Errorf("invalid subcontainer") return nil, fmt.Errorf("invalid subcontainer")
} }
ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64())
containerCode := scope.Contract.Container.subContainerCodes[idx] containerCode := scope.Contract.Container.subContainerAt(int(idx))
if len(containerCode) == 0 { if len(containerCode) == 0 {
return nil, errors.New("nonexistant subcontainer") return nil, errors.New("nonexistant subcontainer")
} }
@ -202,11 +197,12 @@ func opReturnContract(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
} }
// append the auxdata // append the auxdata
c.data = append(c.data, ret...) c.rawContainer = append(c.rawContainer, ret...)
if len(c.data) < c.dataSize { newDataSize := c.dataLen()
if newDataSize < c.dataSize {
return nil, errors.New("incomplete aux data") return nil, errors.New("incomplete aux data")
} }
c.dataSize = len(c.data) c.dataSize = newDataSize
// probably unneeded as subcontainers are deeply validated // probably unneeded as subcontainers are deeply validated
if err := c.ValidateCode(interpreter.tableEOF, false); err != nil { if err := c.ValidateCode(interpreter.tableEOF, false); err != nil {
@ -230,7 +226,7 @@ func opDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
stackItem.Clear() stackItem.Clear()
scope.Stack.push(&stackItem) scope.Stack.push(&stackItem)
} else { } else {
data := getData(scope.Contract.Container.data, offset, 32) data := scope.Contract.Container.getDataAt(offset, 32)
scope.Stack.push(stackItem.SetBytes(data)) scope.Stack.push(stackItem.SetBytes(data))
} }
return nil, nil return nil, nil
@ -239,10 +235,9 @@ func opDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
// opDataLoadN implements the DATALOADN opcode // opDataLoadN implements the DATALOADN opcode
func opDataLoadN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opDataLoadN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
code = scope.Contract.CodeAt(scope.CodeSection) offset = uint64(binary.BigEndian.Uint16(scope.Contract.Code[*pc+1:]))
offset = uint64(binary.BigEndian.Uint16(code[*pc+1:]))
) )
data := getData(scope.Contract.Container.data, offset, 32) data := scope.Contract.Container.getDataAt(offset, 32)
scope.Stack.push(new(uint256.Int).SetBytes(data)) scope.Stack.push(new(uint256.Int).SetBytes(data))
*pc += 2 // move past 2 byte immediate *pc += 2 // move past 2 byte immediate
return nil, nil return nil, nil
@ -250,8 +245,7 @@ func opDataLoadN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
// opDataSize implements the DATASIZE opcode // opDataSize implements the DATASIZE opcode
func opDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
length := len(scope.Contract.Container.data) item := uint256.NewInt(uint64(scope.Contract.Container.dataLen()))
item := uint256.NewInt(uint64(length))
scope.Stack.push(item) scope.Stack.push(item)
return nil, nil return nil, nil
} }
@ -265,7 +259,7 @@ func opDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
) )
// These values are checked for overflow during memory expansion calculation // These values are checked for overflow during memory expansion calculation
// (the memorySize function on the opcode). // (the memorySize function on the opcode).
data := getData(scope.Contract.Container.data, offset.Uint64(), size.Uint64()) data := scope.Contract.Container.getDataAt(offset.Uint64(), size.Uint64())
scope.Memory.Set(memOffset.Uint64(), size.Uint64(), data) scope.Memory.Set(memOffset.Uint64(), size.Uint64(), data)
return nil, nil return nil, nil
} }
@ -273,8 +267,7 @@ func opDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
// opDupN implements the DUPN opcode // opDupN implements the DUPN opcode
func opDupN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opDupN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
code = scope.Contract.CodeAt(scope.CodeSection) index = int(scope.Contract.Code[*pc+1]) + 1
index = int(code[*pc+1]) + 1
) )
scope.Stack.dup(index) scope.Stack.dup(index)
*pc += 1 // move past immediate *pc += 1 // move past immediate
@ -284,8 +277,7 @@ func opDupN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
// opSwapN implements the SWAPN opcode // opSwapN implements the SWAPN opcode
func opSwapN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opSwapN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
code = scope.Contract.CodeAt(scope.CodeSection) index = int(scope.Contract.Code[*pc+1]) + 1
index = int(code[*pc+1]) + 1
) )
scope.Stack.swap(index + 1) scope.Stack.swap(index + 1)
*pc += 1 // move past immediate *pc += 1 // move past immediate
@ -295,8 +287,7 @@ func opSwapN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
// opExchange implements the EXCHANGE opcode // opExchange implements the EXCHANGE opcode
func opExchange(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opExchange(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
code = scope.Contract.CodeAt(scope.CodeSection) index = int(scope.Contract.Code[*pc+1])
index = int(code[*pc+1])
n = (index >> 4) + 1 n = (index >> 4) + 1
m = (index & 0x0F) + 1 m = (index & 0x0F) + 1
) )

View file

@ -24,6 +24,49 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
) )
func MakeTestContainer(
types []*functionMetadata,
codeSections [][]byte,
subContainers []*Container,
data []byte,
dataSize int) Container {
testBytes := make([]byte, 0, 16*1024)
codeSectionOffsets := make([]int, 0, len(codeSections))
idx := 0
for _, code := range codeSections {
codeSectionOffsets = append(codeSectionOffsets, idx)
idx += len(code)
testBytes = append(testBytes, code...)
}
codeSectionOffsets = append(codeSectionOffsets, idx)
var subContainerOffsets []int
if len(subContainers) > 0 {
subContainerOffsets = make([]int, len(subContainers))
for _, subContainer := range subContainers {
containerBytes := subContainer.MarshalBinary()
subContainerOffsets = append(subContainerOffsets, idx)
idx += len(containerBytes)
testBytes = append(testBytes, containerBytes...)
}
// set the subContainer end marker
subContainerOffsets = append(subContainerOffsets, idx)
}
testBytes = append(testBytes, data...)
return Container{
types: types,
codeSectionOffsets: codeSectionOffsets,
subContainers: subContainers,
subContainerOffsets: subContainerOffsets,
dataSize: dataSize,
rawContainer: testBytes,
}
}
func TestEOFMarshaling(t *testing.T) { func TestEOFMarshaling(t *testing.T) {
for i, test := range []struct { for i, test := range []struct {
want Container want Container
@ -31,18 +74,10 @@ func TestEOFMarshaling(t *testing.T) {
}{ }{
{ {
want: Container{ want: Container{
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
codeSections: [][]byte{common.Hex2Bytes("604200")}, codeSectionOffsets: []int{19, 22}, // 604200, endMarker
data: []byte{0x01, 0x02, 0x03}, dataSize: 3,
dataSize: 3, rawContainer: common.Hex2Bytes("ef000101000402000100030400030000800001604200010203"),
},
},
{
want: Container{
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
codeSections: [][]byte{common.Hex2Bytes("604200")},
data: []byte{0x01, 0x02, 0x03},
dataSize: 3,
}, },
}, },
{ {
@ -52,12 +87,8 @@ func TestEOFMarshaling(t *testing.T) {
{inputs: 2, outputs: 3, maxStackHeight: 4}, {inputs: 2, outputs: 3, maxStackHeight: 4},
{inputs: 1, outputs: 1, maxStackHeight: 1}, {inputs: 1, outputs: 1, maxStackHeight: 1},
}, },
codeSections: [][]byte{ codeSectionOffsets: []int{31, 34, 39, 40}, // 604200, 6042604200, 00, endMarker
common.Hex2Bytes("604200"), rawContainer: common.Hex2Bytes("ef000101000c02000300030005000104000000008000010203000401010001604200604260420000"),
common.Hex2Bytes("6042604200"),
common.Hex2Bytes("00"),
},
data: []byte{},
}, },
}, },
} { } {
@ -80,13 +111,13 @@ func TestEOFSubcontainer(t *testing.T) {
if err := subcontainer.UnmarshalBinary(common.Hex2Bytes("ef000101000402000100010400000000800000fe"), true); err != nil { if err := subcontainer.UnmarshalBinary(common.Hex2Bytes("ef000101000402000100010400000000800000fe"), true); err != nil {
t.Fatal(err) t.Fatal(err)
} }
container := Container{ container := MakeTestContainer(
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
codeSections: [][]byte{common.Hex2Bytes("604200")}, [][]byte{common.Hex2Bytes("604200")},
subContainers: []*Container{subcontainer}, []*Container{subcontainer},
data: []byte{0x01, 0x02, 0x03}, []byte{0x01, 0x02, 0x03},
dataSize: 3, 3,
} )
var ( var (
b = container.MarshalBinary() b = container.MarshalBinary()
got Container got Container

View file

@ -148,8 +148,8 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable,
case DATALOADN: case DATALOADN:
arg, _ := parseUint16(code[i+1:]) arg, _ := parseUint16(code[i+1:])
// TODO why are we checking this? We should just pad // TODO why are we checking this? We should just pad
if arg+32 > len(container.data) { if arg+32 > container.dataLen() {
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidDataloadNArgument, arg, len(container.data), i) return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidDataloadNArgument, arg, container.dataLen(), i)
} }
case RETURNCONTRACT: case RETURNCONTRACT:
if !isInitCode { if !isInitCode {
@ -176,8 +176,8 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable,
if arg >= len(container.subContainers) { if arg >= len(container.subContainers) {
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errUnreachableCode, arg, len(container.subContainers), i) return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errUnreachableCode, arg, len(container.subContainers), i)
} }
if ct := container.subContainers[arg]; len(ct.data) != ct.dataSize { if ct := container.subContainers[arg]; ct.dataLen() != ct.dataSize {
return nil, fmt.Errorf("%w: container %d, have %d, claimed %d, pos %d", errEOFCreateWithTruncatedSection, arg, len(ct.data), ct.dataSize, i) return nil, fmt.Errorf("%w: container %d, have %d, claimed %d, pos %d", errEOFCreateWithTruncatedSection, arg, ct.dataLen(), ct.dataSize, i)
} }
if visitedSubcontainers == nil { if visitedSubcontainers == nil {
visitedSubcontainers = make(map[int]int) visitedSubcontainers = make(map[int]int)

View file

@ -246,12 +246,14 @@ func TestValidateCode(t *testing.T) {
metadata: []*functionMetadata{{inputs: 0, outputs: 0, maxStackHeight: 2}, {inputs: 2, outputs: 1, maxStackHeight: 2}}, metadata: []*functionMetadata{{inputs: 0, outputs: 0, maxStackHeight: 2}, {inputs: 2, outputs: 1, maxStackHeight: 2}},
}, },
} { } {
container := &Container{ container := MakeTestContainer(
types: test.metadata, test.metadata,
data: make([]byte, 0), [][]byte{test.code},
subContainers: make([]*Container, 0), []*Container{},
} []byte{},
_, err := validateCode(test.code, test.section, container, &eofInstructionSet, false) 0,
)
_, err := validateCode(test.code, test.section, &container, &eofInstructionSet, false)
if !errors.Is(err, test.err) { if !errors.Is(err, test.err) {
t.Errorf("test %d (%s): unexpected error (want: %v, got: %v)", i, common.Bytes2Hex(test.code), test.err, err) t.Errorf("test %d (%s): unexpected error (want: %v, got: %v)", i, common.Bytes2Hex(test.code), test.err, err)
} }
@ -270,14 +272,16 @@ func BenchmarkRJUMPI(b *testing.B) {
code = append(code, snippet...) code = append(code, snippet...)
} }
code = append(code, byte(STOP)) code = append(code, byte(STOP))
container := &Container{ container := MakeTestContainer(
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
data: make([]byte, 0), [][]byte{code},
subContainers: make([]*Container, 0), []*Container{},
} []byte{},
0,
)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_, err := validateCode(code, 0, container, &eofInstructionSet, false) _, err := validateCode(code, 0, &container, &eofInstructionSet, false)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
@ -302,14 +306,16 @@ func BenchmarkRJUMPV(b *testing.B) {
} }
code = append(code, byte(PUSH0)) code = append(code, byte(PUSH0))
code = append(code, byte(STOP)) code = append(code, byte(STOP))
container := &Container{ container := MakeTestContainer(
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
data: make([]byte, 0), [][]byte{code},
subContainers: make([]*Container, 0), []*Container{},
} []byte{},
0,
)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_, err := validateCode(code, 0, container, &eofInstructionSet, false) _, err := validateCode(code, 0, &container, &eofInstructionSet, false)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }
@ -322,30 +328,32 @@ func BenchmarkRJUMPV(b *testing.B) {
// - or code to again call into 1024 code sections. // - or code to again call into 1024 code sections.
// We can't have all code sections calling each other, otherwise we would exceed 48KB. // We can't have all code sections calling each other, otherwise we would exceed 48KB.
func BenchmarkEOFValidation(b *testing.B) { func BenchmarkEOFValidation(b *testing.B) {
var container Container
var code []byte
maxSections := 1024 maxSections := 1024
types := make([]*functionMetadata, maxSections)
codeSections := make([][]byte, maxSections)
var code []byte
for i := 0; i < maxSections; i++ { for i := 0; i < maxSections; i++ {
code = append(code, byte(CALLF)) code = append(code, byte(CALLF))
code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1) code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1)
} }
// First container // First container
container.codeSections = append(container.codeSections, append(code, byte(STOP))) codeSections = append(codeSections, append(code, byte(STOP)))
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0}) types = append(types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0})
inner := []byte{ inner := []byte{
byte(RETF), byte(RETF),
} }
for i := 0; i < 1023; i++ { for i := 0; i < 1023; i++ {
container.codeSections = append(container.codeSections, inner) codeSections = append(codeSections, inner)
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0}) types = append(types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
} }
for i := 0; i < 12; i++ { for i := 0; i < 12; i++ {
container.codeSections[i+1] = append(code, byte(RETF)) codeSections[i+1] = append(code, byte(RETF))
} }
container := MakeTestContainer(types, codeSections, []*Container{}, []byte{}, 0)
bin := container.MarshalBinary() bin := container.MarshalBinary()
if len(bin) > 48*1024 { if len(bin) > 48*1024 {
b.Fatal("Exceeds 48Kb") b.Fatal("Exceeds 48Kb")
@ -368,17 +376,18 @@ func BenchmarkEOFValidation(b *testing.B) {
// - contain calls to some other code sections. // - contain calls to some other code sections.
// We can't have all code sections calling each other, otherwise we would exceed 48KB. // We can't have all code sections calling each other, otherwise we would exceed 48KB.
func BenchmarkEOFValidation2(b *testing.B) { func BenchmarkEOFValidation2(b *testing.B) {
var container Container
var code []byte
maxSections := 1024 maxSections := 1024
types := make([]*functionMetadata, maxSections)
codeSections := make([][]byte, maxSections)
var code []byte
for i := 0; i < maxSections; i++ { for i := 0; i < maxSections; i++ {
code = append(code, byte(CALLF)) code = append(code, byte(CALLF))
code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1) code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1)
} }
code = append(code, byte(STOP)) code = append(code, byte(STOP))
// First container // First container
container.codeSections = append(container.codeSections, code) codeSections = append(codeSections, code)
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0}) types = append(types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0})
inner := []byte{ inner := []byte{
byte(CALLF), 0x03, 0xE8, byte(CALLF), 0x03, 0xE8,
@ -397,10 +406,11 @@ func BenchmarkEOFValidation2(b *testing.B) {
} }
for i := 0; i < 1023; i++ { for i := 0; i < 1023; i++ {
container.codeSections = append(container.codeSections, inner) codeSections = append(codeSections, inner)
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0}) types = append(types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
} }
container := MakeTestContainer(types, codeSections, []*Container{}, []byte{}, 0)
bin := container.MarshalBinary() bin := container.MarshalBinary()
if len(bin) > 48*1024 { if len(bin) > 48*1024 {
b.Fatal("Exceeds 48Kb") b.Fatal("Exceeds 48Kb")
@ -424,7 +434,9 @@ func BenchmarkEOFValidation2(b *testing.B) {
// - contain calls to other code sections // - contain calls to other code sections
// We can't have all code sections calling each other, otherwise we would exceed 48KB. // We can't have all code sections calling each other, otherwise we would exceed 48KB.
func BenchmarkEOFValidation3(b *testing.B) { func BenchmarkEOFValidation3(b *testing.B) {
var container Container maxSections := 1024
types := make([]*functionMetadata, maxSections)
codeSections := make([][]byte, maxSections)
var code []byte var code []byte
snippet := []byte{ snippet := []byte{
byte(PUSH0), byte(PUSH0),
@ -437,25 +449,25 @@ func BenchmarkEOFValidation3(b *testing.B) {
} }
code = append(code, snippet...) code = append(code, snippet...)
// First container, calls into all other containers // First container, calls into all other containers
maxSections := 1024
for i := 0; i < maxSections; i++ { for i := 0; i < maxSections; i++ {
code = append(code, byte(CALLF)) code = append(code, byte(CALLF))
code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1) code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1)
} }
code = append(code, byte(STOP)) code = append(code, byte(STOP))
container.codeSections = append(container.codeSections, code) codeSections = append(codeSections, code)
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 1}) types = append(types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 1})
// Other containers // Other containers
for i := 0; i < 1023; i++ { for i := 0; i < 1023; i++ {
container.codeSections = append(container.codeSections, []byte{byte(RJUMP), 0x00, 0x00, byte(RETF)}) codeSections = append(codeSections, []byte{byte(RJUMP), 0x00, 0x00, byte(RETF)})
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0}) types = append(types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
} }
// Other containers // Other containers
for i := 0; i < 68; i++ { for i := 0; i < 68; i++ {
container.codeSections[i+1] = append(snippet, byte(RETF)) codeSections[i+1] = append(snippet, byte(RETF))
container.types[i+1] = &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 1} types[i+1] = &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 1}
} }
container := MakeTestContainer(types, codeSections, []*Container{}, []byte{}, 0)
bin := container.MarshalBinary() bin := container.MarshalBinary()
if len(bin) > 48*1024 { if len(bin) > 48*1024 {
b.Fatal("Exceeds 48Kb") b.Fatal("Exceeds 48Kb")
@ -487,14 +499,16 @@ func BenchmarkRJUMPI_2(b *testing.B) {
code = binary.BigEndian.AppendUint16(code, uint16(x)) code = binary.BigEndian.AppendUint16(code, uint16(x))
} }
code = append(code, byte(STOP)) code = append(code, byte(STOP))
container := &Container{ container := MakeTestContainer(
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
data: make([]byte, 0), [][]byte{code},
subContainers: make([]*Container, 0), []*Container{},
} []byte{},
0,
)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_, err := validateCode(code, 0, container, &eofInstructionSet, false) _, err := validateCode(code, 0, &container, &eofInstructionSet, false)
if err != nil { if err != nil {
b.Fatal(err) b.Fatal(err)
} }

View file

@ -363,7 +363,7 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
} }
func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.CodeAt(scope.CodeSection))))) scope.Stack.push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Code))))
return nil, nil return nil, nil
} }
@ -378,7 +378,7 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
uint64CodeOffset = math.MaxUint64 uint64CodeOffset = math.MaxUint64
} }
codeCopy := getData(scope.Contract.CodeAt(scope.CodeSection), uint64CodeOffset, length.Uint64()) codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64())
scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
return nil, nil return nil, nil
} }
@ -909,7 +909,7 @@ func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
} }
func opUndefined(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opUndefined(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return nil, &ErrInvalidOpCode{opcode: OpCode(scope.Contract.CodeAt(scope.CodeSection)[*pc])} return nil, &ErrInvalidOpCode{opcode: OpCode(scope.Contract.Code[*pc])}
} }
func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
@ -988,13 +988,12 @@ func makeLog(size int) executionFunc {
// opPush1 is a specialized version of pushN // opPush1 is a specialized version of pushN
func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
code = scope.Contract.CodeAt(scope.CodeSection) codeLen = uint64(len(scope.Contract.Code))
codeLen = uint64(len(code))
integer = new(uint256.Int) integer = new(uint256.Int)
) )
*pc += 1 *pc += 1
if *pc < codeLen { if *pc < codeLen {
scope.Stack.push(integer.SetUint64(uint64(code[*pc]))) scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
} else { } else {
scope.Stack.push(integer.Clear()) scope.Stack.push(integer.Clear())
} }
@ -1005,8 +1004,7 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
func makePush(size uint64, pushByteSize int) executionFunc { func makePush(size uint64, pushByteSize int) executionFunc {
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) { return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var ( var (
code = scope.Contract.CodeAt(scope.CodeSection) codeLen = len(scope.Contract.Code)
codeLen = len(code)
start = min(codeLen, int(*pc+1)) start = min(codeLen, int(*pc+1))
end = min(codeLen, start+pushByteSize) end = min(codeLen, start+pushByteSize)
) )

View file

@ -232,6 +232,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool, i
res []byte // result of the opcode execution function res []byte // result of the opcode execution function
debug = in.evm.Config.Tracer != nil debug = in.evm.Config.Tracer != nil
) )
// Don't move this deferred function, it's placed before the OnOpcode-deferred method, // Don't move this deferred function, it's placed before the OnOpcode-deferred method,
// so that it gets executed _after_: the OnOpcode needs the stacks before // so that it gets executed _after_: the OnOpcode needs the stacks before
// they are returned to the pools // they are returned to the pools
@ -243,6 +244,8 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool, i
if contract.IsEOF() { if contract.IsEOF() {
jt = in.tableEOF jt = in.tableEOF
// Set EOF entrypoint
pc = uint64(contract.Container.codeSectionOffsets[0])
} else { } else {
jt = in.table jt = in.table
} }
@ -279,7 +282,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool, i
// Get the operation from the jump table and validate the stack to ensure there are // Get the operation from the jump table and validate the stack to ensure there are
// enough stack items available to perform the operation. // enough stack items available to perform the operation.
op = contract.GetOp(pc, callContext.CodeSection) op = contract.GetOp(pc)
operation := jt[op] operation := jt[op]
cost = operation.constantGas // For tracing cost = operation.constantGas // For tracing
// Validate stack // Validate stack