mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-02-28 00:27:26 +00:00
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:
parent
b0dedd720b
commit
5bfacf4ba2
9 changed files with 241 additions and 164 deletions
|
|
@ -146,10 +146,8 @@ func (c *Contract) AsDelegate() *Contract {
|
|||
}
|
||||
|
||||
// GetOp returns the n'th element in the contract's byte array
|
||||
func (c *Contract) GetOp(n uint64, s uint64) OpCode {
|
||||
if c.IsEOF() && n < uint64(len(c.Container.codeSections[s])) {
|
||||
return OpCode(c.Container.codeSections[s][n])
|
||||
} else if n < uint64(len(c.Code)) {
|
||||
func (c *Contract) GetOp(n uint64) OpCode {
|
||||
if n < uint64(len(c.Code)) {
|
||||
return OpCode(c.Code[n])
|
||||
}
|
||||
return STOP
|
||||
|
|
@ -201,13 +199,6 @@ func (c *Contract) IsEOF() bool {
|
|||
return c.Container != nil
|
||||
}
|
||||
|
||||
func (c *Contract) CodeAt(section uint64) []byte {
|
||||
if c.Container == nil {
|
||||
return c.Code
|
||||
}
|
||||
return c.Container.codeSections[section]
|
||||
}
|
||||
|
||||
// SetCallCode sets the code of the contract and address of the backing data
|
||||
// object
|
||||
func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte, container *Container) {
|
||||
|
|
|
|||
107
core/vm/eof.go
107
core/vm/eof.go
|
|
@ -66,12 +66,15 @@ func isEOFVersion1(code []byte) bool {
|
|||
|
||||
// Container is an EOF container object.
|
||||
type Container struct {
|
||||
types []*functionMetadata
|
||||
codeSections [][]byte
|
||||
subContainers []*Container
|
||||
subContainerCodes [][]byte
|
||||
data []byte
|
||||
dataSize int // might be more than len(data)
|
||||
types []*functionMetadata
|
||||
codeSectionOffsets []int
|
||||
codeSectionEnd int
|
||||
subContainers []*Container
|
||||
subContainerOffsets []int
|
||||
subContainerEnd int
|
||||
dataOffest int
|
||||
dataSize int // might be more than len(data)
|
||||
rawContainer []byte
|
||||
}
|
||||
|
||||
// functionMetadata is an EOF function signature.
|
||||
|
|
@ -105,6 +108,46 @@ func (meta *functionMetadata) checkStackMax(stackMax int) error {
|
|||
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.
|
||||
func (c *Container) MarshalBinary() []byte {
|
||||
// Build EOF prefix.
|
||||
|
|
@ -116,9 +159,9 @@ func (c *Container) MarshalBinary() []byte {
|
|||
b = append(b, kindTypes)
|
||||
b = binary.BigEndian.AppendUint16(b, uint16(len(c.types)*4))
|
||||
b = append(b, kindCode)
|
||||
b = binary.BigEndian.AppendUint16(b, uint16(len(c.codeSections)))
|
||||
for _, codeSection := range c.codeSections {
|
||||
b = binary.BigEndian.AppendUint16(b, uint16(len(codeSection)))
|
||||
b = binary.BigEndian.AppendUint16(b, uint16(len(c.codeSectionOffsets)))
|
||||
for s := range c.codeSectionOffsets {
|
||||
b = binary.BigEndian.AppendUint16(b, uint16(c.codeSectionSize(s)))
|
||||
}
|
||||
var encodedContainer [][]byte
|
||||
if len(c.subContainers) != 0 {
|
||||
|
|
@ -138,13 +181,13 @@ func (c *Container) MarshalBinary() []byte {
|
|||
for _, ty := range c.types {
|
||||
b = append(b, []byte{ty.inputs, ty.outputs, byte(ty.maxStackHeight >> 8), byte(ty.maxStackHeight & 0x00ff)}...)
|
||||
}
|
||||
for _, code := range c.codeSections {
|
||||
b = append(b, code...)
|
||||
for s := range c.codeSectionOffsets {
|
||||
b = append(b, c.codeSectionBytes(s)...)
|
||||
}
|
||||
for _, section := range encodedContainer {
|
||||
b = append(b, section...)
|
||||
}
|
||||
b = append(b, c.data...)
|
||||
b = append(b, c.rawContainer[c.dataOffest:]...)
|
||||
|
||||
return b
|
||||
}
|
||||
|
|
@ -282,21 +325,22 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool)
|
|||
|
||||
// Parse code sections.
|
||||
idx += typesSize
|
||||
codeSections := make([][]byte, len(codeSizes))
|
||||
codeSectionOffsets := make([]int, len(codeSizes))
|
||||
for i, size := range codeSizes {
|
||||
if size == 0 {
|
||||
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
|
||||
}
|
||||
c.codeSections = codeSections
|
||||
c.codeSectionOffsets = codeSectionOffsets
|
||||
c.codeSectionEnd = idx
|
||||
// Parse the optional container sizes.
|
||||
if len(containerSizes) != 0 {
|
||||
if len(containerSizes) > maxContainerSections {
|
||||
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))
|
||||
for i, size := range containerSizes {
|
||||
if size == 0 || idx+size > len(b) {
|
||||
|
|
@ -311,23 +355,22 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool)
|
|||
return err
|
||||
}
|
||||
subContainers = append(subContainers, subC)
|
||||
subContainerCodes = append(subContainerCodes, b[idx:end])
|
||||
subContainerOffsets = append(subContainerOffsets, idx)
|
||||
|
||||
idx += size
|
||||
}
|
||||
c.subContainers = subContainers
|
||||
c.subContainerCodes = subContainerCodes
|
||||
c.subContainerEnd = idx
|
||||
c.subContainerOffsets = subContainerOffsets
|
||||
}
|
||||
|
||||
//Parse data section.
|
||||
end := len(b)
|
||||
if !isInitcode {
|
||||
end = min(idx+dataSize, len(b))
|
||||
}
|
||||
if topLevel && len(b) != idx+dataSize {
|
||||
return errTruncatedTopLevelContainer
|
||||
}
|
||||
c.data = b[idx:end]
|
||||
c.dataOffest = idx
|
||||
|
||||
c.rawContainer = b
|
||||
|
||||
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
|
||||
var (
|
||||
index = toVisit[0]
|
||||
code = c.codeSections[index]
|
||||
code = c.codeSectionBytes(index)
|
||||
)
|
||||
if _, ok := visited[index]; !ok {
|
||||
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:]
|
||||
}
|
||||
// Make sure every code section is visited at least once.
|
||||
if len(visited) != len(c.codeSections) {
|
||||
if len(visited) != len(c.codeSectionOffsets) {
|
||||
return errUnreachableCode
|
||||
}
|
||||
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(" - KindCode: %02x", kindCode),
|
||||
fmt.Sprintf(" - KindData: %02x", kindData),
|
||||
fmt.Sprintf(" - DataSize: %04x", len(c.data)),
|
||||
fmt.Sprintf(" - Number of code sections: %d", len(c.codeSections)),
|
||||
fmt.Sprintf(" - DataSize: %04x", c.dataLen()),
|
||||
fmt.Sprintf(" - Number of code sections: %d", len(c.codeSectionOffsets)),
|
||||
}
|
||||
for i, code := range c.codeSections {
|
||||
output = append(output, fmt.Sprintf(" - Code section %d length: %04x", i, len(code)))
|
||||
for i := range c.codeSectionOffsets {
|
||||
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)))
|
||||
|
|
@ -490,12 +533,12 @@ func (c *Container) String() string {
|
|||
output = append(output, fmt.Sprintf(" - Type %v: %x", i,
|
||||
[]byte{typ.inputs, typ.outputs, byte(typ.maxStackHeight >> 8), byte(typ.maxStackHeight & 0x00ff)}))
|
||||
}
|
||||
for i, code := range c.codeSections {
|
||||
output = append(output, fmt.Sprintf(" - Code section %d: %#x", i, code))
|
||||
for i := range c.codeSectionOffsets {
|
||||
output = append(output, fmt.Sprintf(" - Code section %d: %#x", i, c.codeSectionBytes(i)))
|
||||
}
|
||||
for i, section := range c.subContainers {
|
||||
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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,8 +31,7 @@ import (
|
|||
// opRjump implements the RJUMP opcode.
|
||||
func opRjump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
code = scope.Contract.CodeAt(scope.CodeSection)
|
||||
offset = parseInt16(code[*pc+1:])
|
||||
offset = parseInt16(scope.Contract.Code[*pc+1:])
|
||||
)
|
||||
// move pc past op and operand (+3), add relative offset, subtract 1 to
|
||||
// account for interpreter loop.
|
||||
|
|
@ -54,8 +53,7 @@ func opRjumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]b
|
|||
// opRjumpv implements the RJUMPV opcode
|
||||
func opRjumpv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
code = scope.Contract.CodeAt(scope.CodeSection)
|
||||
maxIndex = uint64(code[*pc+1]) + 1
|
||||
maxIndex = uint64(scope.Contract.Code[*pc+1]) + 1
|
||||
idx = scope.Stack.pop()
|
||||
)
|
||||
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
|
||||
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
|
||||
// account for interpreter loop.
|
||||
*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
|
||||
func opCallf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
code = scope.Contract.CodeAt(scope.CodeSection)
|
||||
idx = binary.BigEndian.Uint16(code[*pc+1:])
|
||||
typ = scope.Contract.Container.types[idx]
|
||||
idx = binary.BigEndian.Uint16(scope.Contract.Code[*pc+1:])
|
||||
typ = scope.Contract.Container.types[idx]
|
||||
)
|
||||
if scope.Stack.len()+int(typ.maxStackHeight)-int(typ.inputs) > 1024 {
|
||||
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.CodeSection = uint64(idx)
|
||||
*pc = uint64(math.MaxUint64)
|
||||
*pc = uint64(scope.Contract.Container.codeSectionOffsets[idx]) - 1
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -111,15 +108,14 @@ func opRetf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
|||
// opJumpf implements the JUMPF opcode
|
||||
func opJumpf(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
code = scope.Contract.CodeAt(scope.CodeSection)
|
||||
idx = binary.BigEndian.Uint16(code[*pc+1:])
|
||||
typ = scope.Contract.Container.types[idx]
|
||||
idx = binary.BigEndian.Uint16(scope.Contract.Code[*pc+1:])
|
||||
typ = scope.Contract.Container.types[idx]
|
||||
)
|
||||
if scope.Stack.len()+int(typ.maxStackHeight)-int(typ.inputs) > 1024 {
|
||||
return nil, fmt.Errorf("stack overflow")
|
||||
}
|
||||
scope.CodeSection = uint64(idx)
|
||||
*pc = uint64(math.MaxUint64)
|
||||
*pc = uint64(scope.Contract.Container.codeSectionOffsets[idx]) - 1
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
@ -129,20 +125,19 @@ func opEOFCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
|
|||
return nil, ErrWriteProtection
|
||||
}
|
||||
var (
|
||||
code = scope.Contract.CodeAt(scope.CodeSection)
|
||||
idx = code[*pc+1]
|
||||
idx = scope.Contract.Code[*pc+1]
|
||||
value = scope.Stack.pop()
|
||||
salt = scope.Stack.pop()
|
||||
offset, size = scope.Stack.pop(), scope.Stack.pop()
|
||||
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")
|
||||
}
|
||||
|
||||
// Deduct hashing charge
|
||||
// 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 {
|
||||
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)
|
||||
// Skip the immediate
|
||||
*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 {
|
||||
stackvalue.Clear()
|
||||
} else {
|
||||
|
|
@ -182,16 +177,15 @@ func opReturnContract(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
|
|||
return nil, errors.New("returncontract in non-initcode mode")
|
||||
}
|
||||
var (
|
||||
code = scope.Contract.CodeAt(scope.CodeSection)
|
||||
idx = code[*pc+1]
|
||||
idx = scope.Contract.Code[*pc+1]
|
||||
offset = 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")
|
||||
}
|
||||
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 {
|
||||
return nil, errors.New("nonexistant subcontainer")
|
||||
}
|
||||
|
|
@ -202,11 +196,12 @@ func opReturnContract(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte
|
|||
}
|
||||
|
||||
// append the auxdata
|
||||
c.data = append(c.data, ret...)
|
||||
if len(c.data) < c.dataSize {
|
||||
c.rawContainer = append(c.rawContainer, ret...)
|
||||
newDataSize := c.dataLen()
|
||||
if newDataSize < c.dataSize {
|
||||
return nil, errors.New("incomplete aux data")
|
||||
}
|
||||
c.dataSize = len(c.data)
|
||||
c.dataSize = newDataSize
|
||||
|
||||
// probably unneeded as subcontainers are deeply validated
|
||||
if err := c.ValidateCode(interpreter.tableEOF, false); err != nil {
|
||||
|
|
@ -230,7 +225,7 @@ func opDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
|||
stackItem.Clear()
|
||||
scope.Stack.push(&stackItem)
|
||||
} 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))
|
||||
}
|
||||
return nil, nil
|
||||
|
|
@ -239,10 +234,9 @@ func opDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
|||
// opDataLoadN implements the DATALOADN opcode
|
||||
func opDataLoadN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
code = scope.Contract.CodeAt(scope.CodeSection)
|
||||
offset = uint64(binary.BigEndian.Uint16(code[*pc+1:]))
|
||||
offset = uint64(binary.BigEndian.Uint16(scope.Contract.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))
|
||||
*pc += 2 // move past 2 byte immediate
|
||||
return nil, nil
|
||||
|
|
@ -250,8 +244,7 @@ func opDataLoadN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) (
|
|||
|
||||
// opDataSize implements the DATASIZE opcode
|
||||
func opDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
length := len(scope.Contract.Container.data)
|
||||
item := uint256.NewInt(uint64(length))
|
||||
item := uint256.NewInt(uint64(scope.Contract.Container.dataLen()))
|
||||
scope.Stack.push(item)
|
||||
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
|
||||
// (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)
|
||||
return nil, nil
|
||||
}
|
||||
|
|
@ -273,8 +266,7 @@ func opDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
|||
// opDupN implements the DUPN opcode
|
||||
func opDupN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
code = scope.Contract.CodeAt(scope.CodeSection)
|
||||
index = int(code[*pc+1]) + 1
|
||||
index = int(scope.Contract.Code[*pc+1]) + 1
|
||||
)
|
||||
scope.Stack.dup(index)
|
||||
*pc += 1 // move past immediate
|
||||
|
|
@ -284,8 +276,7 @@ func opDupN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byt
|
|||
// opSwapN implements the SWAPN opcode
|
||||
func opSwapN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
code = scope.Contract.CodeAt(scope.CodeSection)
|
||||
index = int(code[*pc+1]) + 1
|
||||
index = int(scope.Contract.Code[*pc+1]) + 1
|
||||
)
|
||||
scope.Stack.swap(index + 1)
|
||||
*pc += 1 // move past immediate
|
||||
|
|
@ -295,8 +286,7 @@ func opSwapN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by
|
|||
// opExchange implements the EXCHANGE opcode
|
||||
func opExchange(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
code = scope.Contract.CodeAt(scope.CodeSection)
|
||||
index = int(code[*pc+1])
|
||||
index = int(scope.Contract.Code[*pc+1])
|
||||
n = (index >> 4) + 1
|
||||
m = (index & 0x0F) + 1
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,52 @@ import (
|
|||
"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) {
|
||||
for i, test := range []struct {
|
||||
want Container
|
||||
|
|
@ -31,18 +77,12 @@ func TestEOFMarshaling(t *testing.T) {
|
|||
}{
|
||||
{
|
||||
want: Container{
|
||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
codeSections: [][]byte{common.Hex2Bytes("604200")},
|
||||
data: []byte{0x01, 0x02, 0x03},
|
||||
dataSize: 3,
|
||||
},
|
||||
},
|
||||
{
|
||||
want: Container{
|
||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
codeSections: [][]byte{common.Hex2Bytes("604200")},
|
||||
data: []byte{0x01, 0x02, 0x03},
|
||||
dataSize: 3,
|
||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
codeSectionOffsets: []int{19}, // 604200
|
||||
codeSectionEnd: 22,
|
||||
dataOffest: 22,
|
||||
dataSize: 3,
|
||||
rawContainer: common.Hex2Bytes("ef000101000402000100030400030000800001604200010203"),
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -52,12 +92,10 @@ func TestEOFMarshaling(t *testing.T) {
|
|||
{inputs: 2, outputs: 3, maxStackHeight: 4},
|
||||
{inputs: 1, outputs: 1, maxStackHeight: 1},
|
||||
},
|
||||
codeSections: [][]byte{
|
||||
common.Hex2Bytes("604200"),
|
||||
common.Hex2Bytes("6042604200"),
|
||||
common.Hex2Bytes("00"),
|
||||
},
|
||||
data: []byte{},
|
||||
codeSectionOffsets: []int{31, 34, 39}, // 604200, 6042604200, 00
|
||||
codeSectionEnd: 40,
|
||||
dataOffest: 40,
|
||||
rawContainer: common.Hex2Bytes("ef000101000c02000300030005000104000000008000010203000401010001604200604260420000"),
|
||||
},
|
||||
},
|
||||
} {
|
||||
|
|
@ -80,13 +118,13 @@ func TestEOFSubcontainer(t *testing.T) {
|
|||
if err := subcontainer.UnmarshalBinary(common.Hex2Bytes("ef000101000402000100010400000000800000fe"), true); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
container := Container{
|
||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
codeSections: [][]byte{common.Hex2Bytes("604200")},
|
||||
subContainers: []*Container{subcontainer},
|
||||
data: []byte{0x01, 0x02, 0x03},
|
||||
dataSize: 3,
|
||||
}
|
||||
container := MakeTestContainer(
|
||||
[]*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
[][]byte{common.Hex2Bytes("604200")},
|
||||
[]*Container{subcontainer},
|
||||
[]byte{0x01, 0x02, 0x03},
|
||||
3,
|
||||
)
|
||||
var (
|
||||
b = container.MarshalBinary()
|
||||
got Container
|
||||
|
|
|
|||
|
|
@ -148,8 +148,8 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable,
|
|||
case DATALOADN:
|
||||
arg, _ := parseUint16(code[i+1:])
|
||||
// TODO why are we checking this? We should just pad
|
||||
if arg+32 > len(container.data) {
|
||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidDataloadNArgument, arg, len(container.data), i)
|
||||
if arg+32 > container.dataLen() {
|
||||
return nil, fmt.Errorf("%w: arg %d, last %d, pos %d", errInvalidDataloadNArgument, arg, container.dataLen(), i)
|
||||
}
|
||||
case RETURNCONTRACT:
|
||||
if !isInitCode {
|
||||
|
|
@ -176,8 +176,8 @@ func validateCode(code []byte, section int, container *Container, jt *JumpTable,
|
|||
if arg >= len(container.subContainers) {
|
||||
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 {
|
||||
return nil, fmt.Errorf("%w: container %d, have %d, claimed %d, pos %d", errEOFCreateWithTruncatedSection, arg, len(ct.data), ct.dataSize, i)
|
||||
if ct := container.subContainers[arg]; ct.dataLen() != ct.dataSize {
|
||||
return nil, fmt.Errorf("%w: container %d, have %d, claimed %d, pos %d", errEOFCreateWithTruncatedSection, arg, ct.dataLen(), ct.dataSize, i)
|
||||
}
|
||||
if visitedSubcontainers == nil {
|
||||
visitedSubcontainers = make(map[int]int)
|
||||
|
|
|
|||
|
|
@ -246,12 +246,14 @@ func TestValidateCode(t *testing.T) {
|
|||
metadata: []*functionMetadata{{inputs: 0, outputs: 0, maxStackHeight: 2}, {inputs: 2, outputs: 1, maxStackHeight: 2}},
|
||||
},
|
||||
} {
|
||||
container := &Container{
|
||||
types: test.metadata,
|
||||
data: make([]byte, 0),
|
||||
subContainers: make([]*Container, 0),
|
||||
}
|
||||
_, err := validateCode(test.code, test.section, container, &eofInstructionSet, false)
|
||||
container := MakeTestContainer(
|
||||
test.metadata,
|
||||
[][]byte{test.code},
|
||||
[]*Container{},
|
||||
[]byte{},
|
||||
0,
|
||||
)
|
||||
_, err := validateCode(test.code, test.section, &container, &eofInstructionSet, false)
|
||||
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)
|
||||
}
|
||||
|
|
@ -270,14 +272,16 @@ func BenchmarkRJUMPI(b *testing.B) {
|
|||
code = append(code, snippet...)
|
||||
}
|
||||
code = append(code, byte(STOP))
|
||||
container := &Container{
|
||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
data: make([]byte, 0),
|
||||
subContainers: make([]*Container, 0),
|
||||
}
|
||||
container := MakeTestContainer(
|
||||
[]*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
[][]byte{code},
|
||||
[]*Container{},
|
||||
[]byte{},
|
||||
0,
|
||||
)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := validateCode(code, 0, container, &eofInstructionSet, false)
|
||||
_, err := validateCode(code, 0, &container, &eofInstructionSet, false)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
|
@ -302,14 +306,16 @@ func BenchmarkRJUMPV(b *testing.B) {
|
|||
}
|
||||
code = append(code, byte(PUSH0))
|
||||
code = append(code, byte(STOP))
|
||||
container := &Container{
|
||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
data: make([]byte, 0),
|
||||
subContainers: make([]*Container, 0),
|
||||
}
|
||||
container := MakeTestContainer(
|
||||
[]*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
[][]byte{code},
|
||||
[]*Container{},
|
||||
[]byte{},
|
||||
0,
|
||||
)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := validateCode(code, 0, container, &eofInstructionSet, false)
|
||||
_, err := validateCode(code, 0, &container, &eofInstructionSet, false)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
|
@ -322,30 +328,32 @@ func BenchmarkRJUMPV(b *testing.B) {
|
|||
// - or code to again call into 1024 code sections.
|
||||
// We can't have all code sections calling each other, otherwise we would exceed 48KB.
|
||||
func BenchmarkEOFValidation(b *testing.B) {
|
||||
var container Container
|
||||
var code []byte
|
||||
maxSections := 1024
|
||||
types := make([]*functionMetadata, maxSections)
|
||||
codeSections := make([][]byte, maxSections)
|
||||
var code []byte
|
||||
for i := 0; i < maxSections; i++ {
|
||||
code = append(code, byte(CALLF))
|
||||
code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1)
|
||||
}
|
||||
// First container
|
||||
container.codeSections = append(container.codeSections, append(code, byte(STOP)))
|
||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0})
|
||||
codeSections = append(codeSections, append(code, byte(STOP)))
|
||||
types = append(types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0})
|
||||
|
||||
inner := []byte{
|
||||
byte(RETF),
|
||||
}
|
||||
|
||||
for i := 0; i < 1023; i++ {
|
||||
container.codeSections = append(container.codeSections, inner)
|
||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
|
||||
codeSections = append(codeSections, inner)
|
||||
types = append(types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
|
||||
}
|
||||
|
||||
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()
|
||||
if len(bin) > 48*1024 {
|
||||
b.Fatal("Exceeds 48Kb")
|
||||
|
|
@ -368,17 +376,18 @@ func BenchmarkEOFValidation(b *testing.B) {
|
|||
// - contain calls to some other code sections.
|
||||
// We can't have all code sections calling each other, otherwise we would exceed 48KB.
|
||||
func BenchmarkEOFValidation2(b *testing.B) {
|
||||
var container Container
|
||||
var code []byte
|
||||
maxSections := 1024
|
||||
types := make([]*functionMetadata, maxSections)
|
||||
codeSections := make([][]byte, maxSections)
|
||||
var code []byte
|
||||
for i := 0; i < maxSections; i++ {
|
||||
code = append(code, byte(CALLF))
|
||||
code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1)
|
||||
}
|
||||
code = append(code, byte(STOP))
|
||||
// First container
|
||||
container.codeSections = append(container.codeSections, code)
|
||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0})
|
||||
codeSections = append(codeSections, code)
|
||||
types = append(types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 0})
|
||||
|
||||
inner := []byte{
|
||||
byte(CALLF), 0x03, 0xE8,
|
||||
|
|
@ -397,10 +406,11 @@ func BenchmarkEOFValidation2(b *testing.B) {
|
|||
}
|
||||
|
||||
for i := 0; i < 1023; i++ {
|
||||
container.codeSections = append(container.codeSections, inner)
|
||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
|
||||
codeSections = append(codeSections, inner)
|
||||
types = append(types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
|
||||
}
|
||||
|
||||
container := MakeTestContainer(types, codeSections, []*Container{}, []byte{}, 0)
|
||||
bin := container.MarshalBinary()
|
||||
if len(bin) > 48*1024 {
|
||||
b.Fatal("Exceeds 48Kb")
|
||||
|
|
@ -424,7 +434,9 @@ func BenchmarkEOFValidation2(b *testing.B) {
|
|||
// - contain calls to other code sections
|
||||
// We can't have all code sections calling each other, otherwise we would exceed 48KB.
|
||||
func BenchmarkEOFValidation3(b *testing.B) {
|
||||
var container Container
|
||||
maxSections := 1024
|
||||
types := make([]*functionMetadata, maxSections)
|
||||
codeSections := make([][]byte, maxSections)
|
||||
var code []byte
|
||||
snippet := []byte{
|
||||
byte(PUSH0),
|
||||
|
|
@ -437,25 +449,25 @@ func BenchmarkEOFValidation3(b *testing.B) {
|
|||
}
|
||||
code = append(code, snippet...)
|
||||
// First container, calls into all other containers
|
||||
maxSections := 1024
|
||||
for i := 0; i < maxSections; i++ {
|
||||
code = append(code, byte(CALLF))
|
||||
code = binary.BigEndian.AppendUint16(code, uint16(i%(maxSections-1))+1)
|
||||
}
|
||||
code = append(code, byte(STOP))
|
||||
container.codeSections = append(container.codeSections, code)
|
||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 1})
|
||||
codeSections = append(codeSections, code)
|
||||
types = append(types, &functionMetadata{inputs: 0, outputs: 0x80, maxStackHeight: 1})
|
||||
|
||||
// Other containers
|
||||
for i := 0; i < 1023; i++ {
|
||||
container.codeSections = append(container.codeSections, []byte{byte(RJUMP), 0x00, 0x00, byte(RETF)})
|
||||
container.types = append(container.types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
|
||||
codeSections = append(codeSections, []byte{byte(RJUMP), 0x00, 0x00, byte(RETF)})
|
||||
types = append(types, &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 0})
|
||||
}
|
||||
// Other containers
|
||||
for i := 0; i < 68; i++ {
|
||||
container.codeSections[i+1] = append(snippet, byte(RETF))
|
||||
container.types[i+1] = &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 1}
|
||||
codeSections[i+1] = append(snippet, byte(RETF))
|
||||
types[i+1] = &functionMetadata{inputs: 0, outputs: 0, maxStackHeight: 1}
|
||||
}
|
||||
container := MakeTestContainer(types, codeSections, []*Container{}, []byte{}, 0)
|
||||
bin := container.MarshalBinary()
|
||||
if len(bin) > 48*1024 {
|
||||
b.Fatal("Exceeds 48Kb")
|
||||
|
|
@ -487,14 +499,16 @@ func BenchmarkRJUMPI_2(b *testing.B) {
|
|||
code = binary.BigEndian.AppendUint16(code, uint16(x))
|
||||
}
|
||||
code = append(code, byte(STOP))
|
||||
container := &Container{
|
||||
types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
data: make([]byte, 0),
|
||||
subContainers: make([]*Container, 0),
|
||||
}
|
||||
container := MakeTestContainer(
|
||||
[]*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}},
|
||||
[][]byte{code},
|
||||
[]*Container{},
|
||||
[]byte{},
|
||||
0,
|
||||
)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, err := validateCode(code, 0, container, &eofInstructionSet, false)
|
||||
_, err := validateCode(code, 0, &container, &eofInstructionSet, false)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -363,7 +363,7 @@ func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext)
|
|||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
|
|
@ -378,7 +378,7 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([
|
|||
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)
|
||||
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) {
|
||||
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) {
|
||||
|
|
@ -988,13 +988,12 @@ func makeLog(size int) executionFunc {
|
|||
// opPush1 is a specialized version of pushN
|
||||
func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
code = scope.Contract.CodeAt(scope.CodeSection)
|
||||
codeLen = uint64(len(code))
|
||||
codeLen = uint64(len(scope.Contract.Code))
|
||||
integer = new(uint256.Int)
|
||||
)
|
||||
*pc += 1
|
||||
if *pc < codeLen {
|
||||
scope.Stack.push(integer.SetUint64(uint64(code[*pc])))
|
||||
scope.Stack.push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
|
||||
} else {
|
||||
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 {
|
||||
return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
var (
|
||||
code = scope.Contract.CodeAt(scope.CodeSection)
|
||||
codeLen = len(code)
|
||||
codeLen = len(scope.Contract.Code)
|
||||
start = min(codeLen, int(*pc+1))
|
||||
end = min(codeLen, start+pushByteSize)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -232,6 +232,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool, i
|
|||
res []byte // result of the opcode execution function
|
||||
debug = in.evm.Config.Tracer != nil
|
||||
)
|
||||
|
||||
// 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
|
||||
// they are returned to the pools
|
||||
|
|
@ -243,6 +244,8 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool, i
|
|||
|
||||
if contract.IsEOF() {
|
||||
jt = in.tableEOF
|
||||
// Set EOF entrypoint
|
||||
pc = uint64(contract.Container.codeSectionOffsets[0])
|
||||
} else {
|
||||
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
|
||||
// enough stack items available to perform the operation.
|
||||
op = contract.GetOp(pc, callContext.CodeSection)
|
||||
op = contract.GetOp(pc)
|
||||
operation := jt[op]
|
||||
cost = operation.constantGas // For tracing
|
||||
// Validate stack
|
||||
|
|
|
|||
BIN
statetest
Normal file
BIN
statetest
Normal file
Binary file not shown.
Loading…
Reference in a new issue