Skip to content

Commit

Permalink
update instructions for execution last opcode only (#1068)
Browse files Browse the repository at this point in the history
* update instructions for execution last opcode only

* copy jump table before executing last opcode

---------

Co-authored-by: hexoscott <[email protected]>
  • Loading branch information
2 people authored and Stefan-Ethernal committed Sep 20, 2024
1 parent 8a0ab45 commit b4052c7
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 1 deletion.
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
}
11 changes: 10 additions & 1 deletion 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,7 +217,10 @@ 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
in.jt[op].execute(pc, in, callContext)

jtForLastOpCode := copyJumpTable(in.jt)
overrideJumpTableForLastOpcode(jtForLastOpCode, in.evm.ChainRules())
jtForLastOpCode[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
}

0 comments on commit b4052c7

Please sign in to comment.