Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update instructions for execution last opcode only #1068

Merged
merged 3 commits into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions core/vm/instructions_zkevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/holiman/uint256"
"github.com/ledgerwatch/erigon/common"
"github.com/ledgerwatch/erigon/core/types"
"github.com/ledgerwatch/erigon/crypto"
"github.com/ledgerwatch/erigon/params"
)

Expand Down Expand Up @@ -360,3 +361,136 @@ func opDelegateCall_zkevm(pc *uint64, interpreter *EVMInterpreter, scope *ScopeC
interpreter.returnData = ret
return ret, nil
}

// OpCoded execution overrides that are used for executing the last opcode in case of an error
func opBlockhash_zkevm_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
num := scope.Stack.Peek()

ibs := interpreter.evm.IntraBlockState()
ibs.GetBlockStateRoot(num)

return nil, nil
}

func opCodeSize_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return nil, nil
}

func opExtCodeSize_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.Peek()
interpreter.evm.IntraBlockState().GetCodeSize(slot.Bytes20())
return nil, nil
}

func opExtCodeCopy_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
var (
stack = scope.Stack
a = stack.Pop()
)
addr := libcommon.Address(a.Bytes20())
interpreter.evm.IntraBlockState().GetCode(addr)
return nil, nil
}

func opExtCodeHash_zkevm_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.Peek()
address := libcommon.Address(slot.Bytes20())
ibs := interpreter.evm.IntraBlockState()
ibs.GetCodeSize(address)
ibs.GetCodeHash(address)
return nil, nil
}

func opSelfBalance_lastOpCode(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error) {
interpreter.evm.IntraBlockState().GetBalance(callContext.Contract.Address())
return nil, nil
}

func opBalance_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
slot := scope.Stack.Peek()
address := libcommon.Address(slot.Bytes20())
interpreter.evm.IntraBlockState().GetBalance(address)
return nil, nil
}

func opCreate_zkevm_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly {
return nil, ErrWriteProtection
}
var (
value = scope.Stack.Pop()
gas = scope.Contract.Gas
)
if interpreter.evm.ChainRules().IsTangerineWhistle {
gas -= gas / 64
}

caller := scope.Contract
address := crypto.CreateAddress(caller.Address(), interpreter.evm.IntraBlockState().GetNonce(caller.Address()))

interpreter.evm.IntraBlockState().GetBalance(caller.Address())
nonce := interpreter.evm.IntraBlockState().GetNonce(caller.Address())
interpreter.evm.IntraBlockState().SetNonce(caller.Address(), nonce+1)
interpreter.evm.IntraBlockState().AddAddressToAccessList(address)
interpreter.evm.IntraBlockState().GetCodeHash(address)
interpreter.evm.IntraBlockState().GetNonce(address)
interpreter.evm.IntraBlockState().CreateAccount(address, true)
interpreter.evm.IntraBlockState().SetNonce(address, 1)
interpreter.evm.IntraBlockState().SubBalance(caller.Address(), &value)
interpreter.evm.IntraBlockState().AddBalance(address, &value)
interpreter.evm.IntraBlockState().SetCode(address, []byte{0})

return nil, nil
}

func opCreate2_zkevm_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly {
return nil, ErrWriteProtection
}
var (
endowment = scope.Stack.Pop()
offset, size = scope.Stack.Pop(), scope.Stack.Pop()
salt = scope.Stack.Pop()
input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
)

caller := scope.Contract
codeAndHash := &codeAndHash{code: input}
address := crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())

interpreter.evm.IntraBlockState().GetBalance(caller.Address())
nonce := interpreter.evm.IntraBlockState().GetNonce(caller.Address())
interpreter.evm.IntraBlockState().SetNonce(caller.Address(), nonce+1)
interpreter.evm.IntraBlockState().AddAddressToAccessList(address)
interpreter.evm.IntraBlockState().GetCodeHash(address)
interpreter.evm.IntraBlockState().GetNonce(address)
interpreter.evm.IntraBlockState().CreateAccount(address, true)
interpreter.evm.IntraBlockState().SetNonce(address, 1)
interpreter.evm.IntraBlockState().SubBalance(caller.Address(), &endowment)
interpreter.evm.IntraBlockState().AddBalance(address, &endowment)
interpreter.evm.IntraBlockState().SetCode(address, []byte{0})

return nil, nil
}

func opReturn_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
return nil, nil
}

func opSload_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
loc := scope.Stack.Peek()
interpreter.hasherBuf = loc.Bytes32()
interpreter.evm.IntraBlockState().GetState(scope.Contract.Address(), &interpreter.hasherBuf, loc)
return nil, nil
}

func opSstore_lastOpCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
if interpreter.readOnly {
return nil, ErrWriteProtection
}
loc := scope.Stack.Pop()
val := scope.Stack.Pop()
interpreter.hasherBuf = loc.Bytes32()
interpreter.evm.IntraBlockState().SetState(scope.Contract.Address(), &interpreter.hasherBuf, val)
return nil, nil
}
8 changes: 8 additions & 0 deletions core/vm/interpreter_zkevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ func getJumpTable(cr *chain.Rules) *JumpTable {
return jt
}

func overrideJumpTableForLastOpcode(jt *JumpTable, cr *chain.Rules) {
// currently an opcode execution does not change in any fork from 4 do 12.
// If, in future, there is a change then we can override it based on fork id
overrideJumpTableForLastOpcodeForkId12(jt)
}

func shouldExecuteLastOpCode(op OpCode) bool {
switch op {
case BLOCKHASH:
Expand Down Expand Up @@ -211,6 +217,8 @@ func (in *EVMInterpreter) RunZk(contract *Contract, input []byte, readOnly bool)
// we can safely use pc here instead of pcCopy,
// because pc and pcCopy can be different only if the main loop finishes normally without error
// but is it finishes normally without error then "ret" != nil and the .execute below will never be invoked at all

overrideJumpTableForLastOpcode(in.jt, in.evm.ChainRules())
in.jt[op].execute(pc, in, callContext)
}
}()
Expand Down
15 changes: 15 additions & 0 deletions core/vm/jump_table_zkevm.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,18 @@ func newForkID8InstructionSet() JumpTable {
validateAndFillMaxStack(&instructionSet)
return instructionSet
}

func overrideJumpTableForLastOpcodeForkId12(jt *JumpTable) {
jt[BLOCKHASH].execute = opBlockhash_zkevm_lastOpCode
jt[CODESIZE].execute = opCodeSize_lastOpCode
jt[EXTCODESIZE].execute = opExtCodeSize_lastOpCode
jt[EXTCODECOPY].execute = opExtCodeCopy_lastOpCode
jt[EXTCODEHASH].execute = opExtCodeHash_zkevm_lastOpCode
jt[SELFBALANCE].execute = opSelfBalance_lastOpCode
jt[BALANCE].execute = opBalance_lastOpCode
jt[CREATE].execute = opCreate_zkevm_lastOpCode
jt[RETURN].execute = opReturn_lastOpCode
jt[CREATE2].execute = opCreate2_zkevm_lastOpCode
jt[SLOAD].execute = opSload_lastOpCode
jt[SSTORE].execute = opSstore_lastOpCode
}
Loading