diff --git a/core/vm/eof.go b/core/vm/eof.go index 2bef6f0a75..5a7d104aeb 100644 --- a/core/vm/eof.go +++ b/core/vm/eof.go @@ -67,13 +67,10 @@ func isEOFVersion1(code []byte) bool { // Container is an EOF container object. type Container struct { types []*functionMetadata - codeSectionOffsets []int - codeSectionEnd int + codeSectionOffsets []int // contains all the offsets of the codeSections. The last item marks the end subContainers []*Container - subContainerOffsets []int - subContainerEnd int - dataOffest int - dataSize int // might be more than len(data) + subContainerOffsets []int // contains all the offsets of the subContainers. The last item marks the end + dataSize int // might be more than len(data) rawContainer []byte } @@ -108,48 +105,44 @@ 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 { +// 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] - } 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]] + return c.rawContainer[c.codeSectionOffsets[index]:c.codeSectionOffsets[index+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 { +// 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] - } 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]] + 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.dataOffest + 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. func (c *Container) MarshalBinary() []byte { + // Drop the end markers + codeSectionOffsets := c.codeSectionOffsets[:len(c.codeSectionOffsets)-1] + // Build EOF prefix. b := make([]byte, 2) copy(b, eofMagic) @@ -159,9 +152,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.codeSectionOffsets))) - for s := range c.codeSectionOffsets { - b = binary.BigEndian.AppendUint16(b, uint16(c.codeSectionSize(s))) + b = binary.BigEndian.AppendUint16(b, uint16(len(codeSectionOffsets))) + for s := range codeSectionOffsets { + b = binary.BigEndian.AppendUint16(b, uint16(len(c.codeSectionAt(s)))) } var encodedContainer [][]byte if len(c.subContainers) != 0 { @@ -181,13 +174,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 s := range c.codeSectionOffsets { - b = append(b, c.codeSectionBytes(s)...) + for s := range codeSectionOffsets { + b = append(b, c.codeSectionAt(s)...) } for _, section := range encodedContainer { b = append(b, section...) } - b = append(b, c.rawContainer[c.dataOffest:]...) + b = append(b, c.rawContainer[c.dataOffset():]...) return b } @@ -333,8 +326,8 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool) codeSectionOffsets[i] = idx idx += size } - c.codeSectionOffsets = codeSectionOffsets - c.codeSectionEnd = idx + // add the end marker to the codeSection offsets + c.codeSectionOffsets = append(codeSectionOffsets, idx) // Parse the optional container sizes. if len(containerSizes) != 0 { if len(containerSizes) > maxContainerSections { @@ -360,15 +353,14 @@ func (c *Container) unmarshalContainer(b []byte, isInitcode bool, topLevel bool) idx += size } c.subContainers = subContainers - c.subContainerEnd = idx - c.subContainerOffsets = subContainerOffsets + // add the end marker to the subContainer offsets + c.subContainerOffsets = append(subContainerOffsets, idx) } //Parse data section. if topLevel && len(b) != idx+dataSize { return errTruncatedTopLevelContainer } - c.dataOffest = idx c.rawContainer = b @@ -398,7 +390,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.codeSectionBytes(index) + code = c.codeSectionAt(index) ) if _, ok := visited[index]; !ok { res, err := validateCode(code, index, c, jt, refBy == refByEOFCreate) @@ -430,7 +422,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.codeSectionOffsets) { + if len(visited) != len(c.codeSectionOffsets)-1 { return errUnreachableCode } for idx, container := range c.subContainers { @@ -507,6 +499,8 @@ func sum(list []int) (s int) { } func (c *Container) String() string { + // Drop the end markers + codeSectionOffsets := c.codeSectionOffsets[:len(c.codeSectionOffsets)-1] var output = []string{ "Header", fmt.Sprintf(" - EOFMagic: %02x", eofMagic), @@ -514,14 +508,13 @@ func (c *Container) String() string { fmt.Sprintf(" - KindType: %02x", kindTypes), fmt.Sprintf(" - TypesSize: %04x", len(c.types)*4), fmt.Sprintf(" - KindCode: %02x", kindCode), + fmt.Sprintf(" - Number of code sections: %d", len(codeSectionOffsets)), fmt.Sprintf(" - KindData: %02x", kindData), - fmt.Sprintf(" - DataSize: %04x", c.dataLen()), - fmt.Sprintf(" - Number of code sections: %d", len(c.codeSectionOffsets)), + fmt.Sprintf(" - DataSize: %04x", c.dataSize), } - for i := range c.codeSectionOffsets { - output = append(output, fmt.Sprintf(" - Code section %d length: %04x", i, c.codeSectionSize(i))) + for i := range codeSectionOffsets { + 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))) if len(c.subContainers) > 0 { for i, section := range c.subContainers { @@ -533,12 +526,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 := range c.codeSectionOffsets { - output = append(output, fmt.Sprintf(" - Code section %d: %#x", i, c.codeSectionBytes(i))) + for i := range codeSectionOffsets { + output = append(output, fmt.Sprintf(" - Code section %d: %#x", i, c.codeSectionAt(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.rawContainer[c.dataOffest:])) + output = append(output, fmt.Sprintf(" - Data: %#x", c.rawContainer[c.dataOffset():])) return strings.Join(output, "\n") } diff --git a/core/vm/eof_instructions.go b/core/vm/eof_instructions.go index 4fc5c2c568..380f4a2401 100644 --- a/core/vm/eof_instructions.go +++ b/core/vm/eof_instructions.go @@ -134,10 +134,11 @@ func opEOFCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( if int(idx) >= len(scope.Contract.Container.subContainerOffsets) { return nil, fmt.Errorf("invalid subcontainer") } + subContainer := scope.Contract.Container.subContainerAt(int(idx)) // Deduct hashing charge // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow - hashingCharge := (params.Keccak256WordGas) * ((uint64(scope.Contract.Container.subContainerSize(int(idx))) + 31) / 32) + hashingCharge := (params.Keccak256WordGas) * ((uint64(len(subContainer)) + 31) / 32) if ok := scope.Contract.UseGas(hashingCharge, interpreter.evm.Config.Tracer, tracing.GasChangeUnspecified); !ok { return nil, ErrGasUintOverflow } @@ -154,7 +155,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.subContainerBytes(int(idx)), gas, &value, &salt) + res, addr, returnGas, suberr := interpreter.evm.EOFCreate(scope.Contract, input, subContainer, gas, &value, &salt) if suberr != nil { stackvalue.Clear() } else { @@ -185,7 +186,7 @@ func opReturnContract(pc *uint64, interpreter *EVMInterpreter, scope *ScopeConte return nil, fmt.Errorf("invalid subcontainer") } ret := scope.Memory.GetPtr(offset.Uint64(), size.Uint64()) - containerCode := scope.Contract.Container.subContainerBytes(int(idx)) + containerCode := scope.Contract.Container.subContainerAt(int(idx)) if len(containerCode) == 0 { return nil, errors.New("nonexistant subcontainer") } @@ -225,7 +226,7 @@ func opDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ stackItem.Clear() scope.Stack.push(&stackItem) } else { - data := getData(scope.Contract.Container.rawContainer, uint64(scope.Contract.Container.dataOffest)+offset, 32) + data := scope.Contract.Container.getDataAt(offset, 32) scope.Stack.push(stackItem.SetBytes(data)) } return nil, nil @@ -236,7 +237,7 @@ func opDataLoadN(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ( var ( offset = uint64(binary.BigEndian.Uint16(scope.Contract.Code[*pc+1:])) ) - data := getData(scope.Contract.Container.rawContainer, uint64(scope.Contract.Container.dataOffest)+offset, 32) + data := scope.Contract.Container.getDataAt(offset, 32) scope.Stack.push(new(uint256.Int).SetBytes(data)) *pc += 2 // move past 2 byte immediate return nil, nil @@ -258,7 +259,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.rawContainer, uint64(scope.Contract.Container.dataOffest)+offset.Uint64(), size.Uint64()) + data := scope.Contract.Container.getDataAt(offset.Uint64(), size.Uint64()) scope.Memory.Set(memOffset.Uint64(), size.Uint64(), data) return nil, nil } diff --git a/core/vm/eof_test.go b/core/vm/eof_test.go index 707e87e584..fd4fbe30b1 100644 --- a/core/vm/eof_test.go +++ b/core/vm/eof_test.go @@ -40,10 +40,9 @@ func MakeTestContainer( idx += len(code) testBytes = append(testBytes, code...) } - codeSectionEnd := idx + codeSectionOffsets = append(codeSectionOffsets, idx) var subContainerOffsets []int - subContainerEnd := 0 if len(subContainers) > 0 { subContainerOffsets = make([]int, len(subContainers)) for _, subContainer := range subContainers { @@ -52,7 +51,8 @@ func MakeTestContainer( idx += len(containerBytes) testBytes = append(testBytes, containerBytes...) } - subContainerEnd = idx + // set the subContainer end marker + subContainerOffsets = append(subContainerOffsets, idx) } testBytes = append(testBytes, data...) @@ -60,11 +60,8 @@ func MakeTestContainer( return Container{ types: types, codeSectionOffsets: codeSectionOffsets, - codeSectionEnd: codeSectionEnd, subContainers: subContainers, subContainerOffsets: subContainerOffsets, - subContainerEnd: subContainerEnd, - dataOffest: subContainerEnd, dataSize: dataSize, rawContainer: testBytes, } @@ -78,9 +75,7 @@ func TestEOFMarshaling(t *testing.T) { { want: Container{ types: []*functionMetadata{{inputs: 0, outputs: 0x80, maxStackHeight: 1}}, - codeSectionOffsets: []int{19}, // 604200 - codeSectionEnd: 22, - dataOffest: 22, + codeSectionOffsets: []int{19, 22}, // 604200, endMarker dataSize: 3, rawContainer: common.Hex2Bytes("ef000101000402000100030400030000800001604200010203"), }, @@ -92,9 +87,7 @@ func TestEOFMarshaling(t *testing.T) { {inputs: 2, outputs: 3, maxStackHeight: 4}, {inputs: 1, outputs: 1, maxStackHeight: 1}, }, - codeSectionOffsets: []int{31, 34, 39}, // 604200, 6042604200, 00 - codeSectionEnd: 40, - dataOffest: 40, + codeSectionOffsets: []int{31, 34, 39, 40}, // 604200, 6042604200, 00, endMarker rawContainer: common.Hex2Bytes("ef000101000c02000300030005000104000000008000010203000401010001604200604260420000"), }, }, diff --git a/statetest b/statetest deleted file mode 100644 index 19be488d3a..0000000000 Binary files a/statetest and /dev/null differ