Move EOF interpreter to use same PC semantics as legacy EVM

Update the EOF container parsing to allow the PC to use the same semantics as the legacy EVM.
 Also, a new test container maker is necessary to handle the particulars of the unit tests, mostly unrelated to
This commit is contained in:
Danno Ferrin 2024-10-14 16:38:35 -06:00 committed by Marius van der Wijden
parent b0dedd720b
commit 5bfacf4ba2
9 changed files with 241 additions and 164 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,15 @@ 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
subContainers []*Container codeSectionEnd int
subContainerCodes [][]byte subContainers []*Container
data []byte subContainerOffsets []int
dataSize int // might be more than len(data) subContainerEnd int
dataOffest int
dataSize int // might be more than len(data)
rawContainer []byte
} }
// functionMetadata is an EOF function signature. // functionMetadata is an EOF function signature.
@ -105,6 +108,46 @@ func (meta *functionMetadata) checkStackMax(stackMax int) error {
return nil return nil
} }
func (c *Container) codeSectionSize(s int) int {
if s >= len(c.codeSectionOffsets) || s < 0 {
return 0
} else if s == len(c.codeSectionOffsets)-1 {
return c.codeSectionEnd - c.codeSectionOffsets[s]
}
return c.codeSectionOffsets[s+1] - c.codeSectionOffsets[s]
}
func (c *Container) codeSectionBytes(s int) []byte {
if s >= len(c.codeSectionOffsets) || s < 0 {
return c.rawContainer[0:0]
} else if s == len(c.codeSectionOffsets)-1 {
return c.rawContainer[c.codeSectionOffsets[s]:c.codeSectionEnd]
}
return c.rawContainer[c.codeSectionOffsets[s]:c.codeSectionOffsets[s+1]]
}
func (c *Container) subContainerSize(s int) int {
if s >= len(c.subContainerOffsets) || s < 0 {
return 0
} else if s == len(c.subContainerOffsets)-1 {
return c.subContainerEnd - c.subContainerOffsets[s]
}
return c.subContainerOffsets[s+1] - c.subContainerOffsets[s]
}
func (c *Container) subContainerBytes(s int) []byte {
if s >= len(c.subContainerOffsets) || s < 0 {
return c.rawContainer[0:0]
} else if s == len(c.subContainerOffsets)-1 {
return c.rawContainer[c.subContainerOffsets[s]:c.subContainerEnd]
}
return c.rawContainer[c.subContainerOffsets[s]:c.subContainerOffsets[s+1]]
}
func (c *Container) dataLen() int {
return len(c.rawContainer) - c.dataOffest
}
// 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 {
// Build EOF prefix. // Build EOF prefix.
@ -116,9 +159,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(c.codeSectionOffsets)))
for _, codeSection := range c.codeSections { for s := range c.codeSectionOffsets {
b = binary.BigEndian.AppendUint16(b, uint16(len(codeSection))) b = binary.BigEndian.AppendUint16(b, uint16(c.codeSectionSize(s)))
} }
var encodedContainer [][]byte var encodedContainer [][]byte
if len(c.subContainers) != 0 { if len(c.subContainers) != 0 {
@ -138,13 +181,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 c.codeSectionOffsets {
b = append(b, code...) b = append(b, c.codeSectionBytes(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.dataOffest:]...)
return b return b
} }
@ -282,21 +325,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 c.codeSectionOffsets = codeSectionOffsets
c.codeSectionEnd = 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 +355,22 @@ 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 c.subContainerEnd = idx
c.subContainerOffsets = subContainerOffsets
} }
//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.dataOffest = idx
c.rawContainer = b
return nil return nil
} }
@ -355,7 +398,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.codeSectionBytes(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 +430,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) {
return errUnreachableCode return errUnreachableCode
} }
for idx, container := range c.subContainers { for idx, container := range c.subContainers {
@ -472,11 +515,11 @@ func (c *Container) String() string {
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(" - KindData: %02x", kindData), fmt.Sprintf(" - KindData: %02x", kindData),
fmt.Sprintf(" - DataSize: %04x", len(c.data)), fmt.Sprintf(" - DataSize: %04x", c.dataLen()),
fmt.Sprintf(" - Number of code sections: %d", len(c.codeSections)), fmt.Sprintf(" - Number of code sections: %d", len(c.codeSectionOffsets)),
} }
for i, code := range c.codeSections { for i := range c.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, c.codeSectionSize(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)))
@ -490,12 +533,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 c.codeSectionOffsets {
output = append(output, fmt.Sprintf(" - Code section %d: %#x", i, code)) output = append(output, fmt.Sprintf(" - Code section %d: %#x", i, c.codeSectionBytes(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.dataOffest:]))
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,19 @@ 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) {
return nil, fmt.Errorf("invalid subcontainer") return nil, fmt.Errorf("invalid subcontainer")
} }
// 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(scope.Contract.Container.subContainerSize(int(idx))) + 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 +154,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, scope.Contract.Container.subContainerBytes(int(idx)), gas, &value, &salt)
if suberr != nil { if suberr != nil {
stackvalue.Clear() stackvalue.Clear()
} else { } else {
@ -182,16 +177,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) {
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.subContainerBytes(int(idx))
if len(containerCode) == 0 { if len(containerCode) == 0 {
return nil, errors.New("nonexistant subcontainer") return nil, errors.New("nonexistant subcontainer")
} }
@ -202,11 +196,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 +225,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 := getData(scope.Contract.Container.rawContainer, uint64(scope.Contract.Container.dataOffest)+offset, 32)
scope.Stack.push(stackItem.SetBytes(data)) scope.Stack.push(stackItem.SetBytes(data))
} }
return nil, nil return nil, nil
@ -239,10 +234,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 := getData(scope.Contract.Container.rawContainer, uint64(scope.Contract.Container.dataOffest)+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 +244,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 +258,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 := getData(scope.Contract.Container.rawContainer, uint64(scope.Contract.Container.dataOffest)+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 +266,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 +276,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 +286,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,52 @@ 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...)
}
codeSectionEnd := idx
var subContainerOffsets []int
subContainerEnd := 0
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...)
}
subContainerEnd = idx
}
testBytes = append(testBytes, data...)
return Container{
types: types,
codeSectionOffsets: codeSectionOffsets,
codeSectionEnd: codeSectionEnd,
subContainers: subContainers,
subContainerOffsets: subContainerOffsets,
subContainerEnd: subContainerEnd,
dataOffest: subContainerEnd,
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 +77,12 @@ 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}, // 604200
data: []byte{0x01, 0x02, 0x03}, codeSectionEnd: 22,
dataSize: 3, dataOffest: 22,
}, 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 +92,10 @@ 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}, // 604200, 6042604200, 00
common.Hex2Bytes("604200"), codeSectionEnd: 40,
common.Hex2Bytes("6042604200"), dataOffest: 40,
common.Hex2Bytes("00"), rawContainer: common.Hex2Bytes("ef000101000c02000300030005000104000000008000010203000401010001604200604260420000"),
},
data: []byte{},
}, },
}, },
} { } {
@ -80,13 +118,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

BIN
statetest Normal file

Binary file not shown.