mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-05-04 23:32:55 +00:00
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:
commit
b78b30717c
8 changed files with 229 additions and 165 deletions
|
|
@ -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) {
|
||||||
|
|
|
||||||
102
core/vm/eof.go
102
core/vm/eof.go
|
|
@ -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")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue