From 629dc0b4437b3c74b1729e8d660d79978016b23b Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Mon, 2 May 2022 18:24:34 +0200 Subject: [PATCH 1/2] Remove the code analysis interface to reduce PR footprint --- core/vm/contract.go | 9 +-------- core/vm/gas_table.go | 4 ++-- core/vm/instructions.go | 35 +++++++---------------------------- core/vm/interpreter.go | 2 +- 4 files changed, 11 insertions(+), 39 deletions(-) diff --git a/core/vm/contract.go b/core/vm/contract.go index eaa4cb7c65b6..51213958536b 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -28,13 +28,6 @@ type ContractRef interface { Address() common.Address } -// AnalyzedContract is an interface for a piece of contract -// code that has undegone jumpdest analysis, and whose bytecode -// can be queried to determine if it is "contract code" -type AnalyzedContract interface { - IsCode(dest uint64) bool -} - // AccountRef implements ContractRef. // // Account references are used during EVM initialisation and @@ -65,7 +58,7 @@ type Contract struct { CodeAddr *common.Address Input []byte - // is the execution frame represented by this object a contract deployment + // is the execution frame represented by this object a contract deployment IsDeployment bool Gas uint64 diff --git a/core/vm/gas_table.go b/core/vm/gas_table.go index 94e3792797b5..df990c3901e7 100644 --- a/core/vm/gas_table.go +++ b/core/vm/gas_table.go @@ -122,7 +122,7 @@ func gasCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memory uint64Length = 0xffffffffffffffff } _, offset, nonPaddedSize := getDataAndAdjustedBounds(contract.Code, uint64CodeOffset, uint64Length) - statelessGas = touchEachChunksOnReadAndChargeGas(offset, nonPaddedSize, contract.Address().Bytes()[:], nil, nil, evm.Accesses, contract.IsDeployment) + statelessGas = touchEachChunksOnReadAndChargeGas(offset, nonPaddedSize, contract.Address().Bytes()[:], nil, evm.Accesses, contract.IsDeployment) } usedGas, err := gasCodeCopyStateful(evm, contract, stack, mem, memorySize) return usedGas + statelessGas, err @@ -150,7 +150,7 @@ func gasExtCodeCopy(evm *EVM, contract *Contract, stack *Stack, mem *Memory, mem // behavior from CODECOPY which only charges witness access costs for the part of the range // which overlaps in the account code. TODO: clarify this is desired behavior and amend the // spec. - statelessGas = touchEachChunksOnReadAndChargeGas(uint64CodeOffset, uint64Length, targetAddr[:], nil, nil, evm.Accesses, contract.IsDeployment) + statelessGas = touchEachChunksOnReadAndChargeGas(uint64CodeOffset, uint64Length, targetAddr[:], nil, evm.Accesses, contract.IsDeployment) } usedGas, err := gasExtCodeCopyStateful(evm, contract, stack, mem, memorySize) return usedGas + statelessGas, err diff --git a/core/vm/instructions.go b/core/vm/instructions.go index 559c4e7918d5..bddef1661c59 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -375,28 +375,28 @@ func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([ paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(scope.Contract.Code, uint64CodeOffset, length.Uint64()) if interpreter.evm.chainConfig.IsCancun(interpreter.evm.Context.BlockNumber) { - touchEachChunksOnReadAndChargeGas(copyOffset, nonPaddedCopyLength, scope.Contract.Address().Bytes()[:], scope.Contract.Code, scope.Contract, interpreter.evm.Accesses, scope.Contract.IsDeployment) + touchEachChunksOnReadAndChargeGas(copyOffset, nonPaddedCopyLength, scope.Contract.Address().Bytes()[:], scope.Contract.Code, interpreter.evm.Accesses, scope.Contract.IsDeployment) } scope.Memory.Set(memOffset.Uint64(), uint64(len(paddedCodeCopy)), paddedCodeCopy) return nil, nil } // touchEachChunksAndChargeGas is a helper function to touch every chunk in a code range and charge witness gas costs -func touchEachChunksOnReadAndChargeGas(offset, size uint64, address []byte, code []byte, contract AnalyzedContract, accesses *types.AccessWitness, deployment bool) uint64 { +func touchEachChunksOnReadAndChargeGas(offset, size uint64, address []byte, code []byte, accesses *types.AccessWitness, deployment bool) uint64 { // note that in the case where the copied code is outside the range of the // contract code but touches the last leaf with contract code in it, // we don't include the last leaf of code in the AccessWitness. The // reason that we do not need the last leaf is the account's code size // is already in the AccessWitness so a stateless verifier can see that // the code from the last leaf is not needed. - if contract != nil && (size == 0 || offset > uint64(len(code))) { + if size == 0 || offset > uint64(len(code)) { return 0 } var ( statelessGasCharged uint64 endOffset uint64 ) - if contract != nil && offset+size > uint64(len(code)) { + if offset+size > uint64(len(code)) { endOffset = uint64(len(code)) } else { endOffset = offset + size @@ -445,8 +445,7 @@ func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) if interpreter.evm.chainConfig.IsCancun(interpreter.evm.Context.BlockNumber) { code := interpreter.evm.StateDB.GetCode(addr) paddedCodeCopy, copyOffset, nonPaddedCopyLength := getDataAndAdjustedBounds(code, uint64CodeOffset, length.Uint64()) - cb := codeBitmap(code) - touchEachChunksOnReadAndChargeGas(copyOffset, nonPaddedCopyLength, addr[:], code, &cb, interpreter.evm.Accesses, false) + touchEachChunksOnReadAndChargeGas(copyOffset, nonPaddedCopyLength, addr[:], code, interpreter.evm.Accesses, false) scope.Memory.Set(memOffset.Uint64(), length.Uint64(), paddedCodeCopy) } else { codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64()) @@ -923,27 +922,7 @@ func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]by if interpreter.evm.chainConfig.IsCancun(interpreter.evm.Context.BlockNumber) && *pc%31 == 0 { // touch next chunk if PUSH1 is at the boundary. if so, *pc has // advanced past this boundary. - - // touch push data by adding the last byte of the pushdata - var value [32]byte - chunk := *pc / 31 - count := uint64(0) - // Look for the first code byte (i.e. no pushdata) - for ; count < 31 && !scope.Contract.IsCode(chunk*31+count); count++ { - } - value[0] = byte(count) - endMin := (chunk + 1) * 31 - if endMin > uint64(len(scope.Contract.Code)) { - endMin = uint64(len(scope.Contract.Code)) - } - copy(value[1:], scope.Contract.Code[chunk*31:endMin]) - index := trieUtils.GetTreeKeyCodeChunk(scope.Contract.Address().Bytes(), uint256.NewInt(chunk)) - statelessGas := interpreter.evm.Accesses.TouchAddressOnReadAndComputeGas(index) - if scope.Contract.IsDeployment { - interpreter.evm.Accesses.SetLeafValue(index, nil) - } else { - interpreter.evm.Accesses.SetLeafValue(index, value[:]) - } + statelessGas := touchEachChunksOnReadAndChargeGas(uint64(*pc+1), uint64(1), scope.Contract.Address().Bytes()[:], scope.Contract.Code, interpreter.evm.Accesses, scope.Contract.IsDeployment) scope.Contract.UseGas(statelessGas) } } else { @@ -968,7 +947,7 @@ func makePush(size uint64, pushByteSize int) executionFunc { } if interpreter.evm.chainConfig.IsCancun(interpreter.evm.Context.BlockNumber) { - statelessGas := touchEachChunksOnReadAndChargeGas(uint64(startMin), uint64(pushByteSize), scope.Contract.Address().Bytes()[:], scope.Contract.Code, scope.Contract, interpreter.evm.Accesses, scope.Contract.IsDeployment) + statelessGas := touchEachChunksOnReadAndChargeGas(uint64(startMin), uint64(pushByteSize), scope.Contract.Address().Bytes()[:], scope.Contract.Code, interpreter.evm.Accesses, scope.Contract.IsDeployment) scope.Contract.UseGas(statelessGas) } diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go index 7b93c389a71d..fb51cb482e75 100644 --- a/core/vm/interpreter.go +++ b/core/vm/interpreter.go @@ -194,7 +194,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) ( if in.evm.ChainConfig().IsCancun(in.evm.Context.BlockNumber) && !contract.IsDeployment { // if the PC ends up in a new "page" of verkleized code, charge the // associated witness costs. - contract.Gas -= touchEachChunksOnReadAndChargeGas(pc, 1, contract.Address().Bytes()[:], contract.Code, contract, in.evm.TxContext.Accesses, contract.IsDeployment) + contract.Gas -= touchEachChunksOnReadAndChargeGas(pc, 1, contract.Address().Bytes()[:], contract.Code, in.evm.TxContext.Accesses, contract.IsDeployment) } // If we are in witness mode, then raise an error From b80cb8966ed283afe6e57353e0d933478daec146 Mon Sep 17 00:00:00 2001 From: Guillaume Ballet <3272758+gballet@users.noreply.github.com> Date: Mon, 2 May 2022 19:52:06 +0200 Subject: [PATCH 2/2] fix unit test --- core/vm/instructions.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/vm/instructions.go b/core/vm/instructions.go index bddef1661c59..e1e99da935fd 100644 --- a/core/vm/instructions.go +++ b/core/vm/instructions.go @@ -389,14 +389,14 @@ func touchEachChunksOnReadAndChargeGas(offset, size uint64, address []byte, code // reason that we do not need the last leaf is the account's code size // is already in the AccessWitness so a stateless verifier can see that // the code from the last leaf is not needed. - if size == 0 || offset > uint64(len(code)) { + if code != nil && (size == 0 || offset > uint64(len(code))) { return 0 } var ( statelessGasCharged uint64 endOffset uint64 ) - if offset+size > uint64(len(code)) { + if code != nil && offset+size > uint64(len(code)) { endOffset = uint64(len(code)) } else { endOffset = offset + size