Skip to content

GitLab

  • Projects
  • Groups
  • Snippets
  • Help
    • Loading...
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
zkevm-circuits
zkevm-circuits
  • Project overview
    • Project overview
    • Details
    • Activity
    • Releases
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 0
    • Issues 0
    • List
    • Boards
    • Labels
    • Service Desk
    • Milestones
  • Merge Requests 0
    • Merge Requests 0
  • CI / CD
    • CI / CD
    • Pipelines
    • Jobs
    • Schedules
  • Operations
    • Operations
    • Incidents
    • Environments
  • Packages & Registries
    • Packages & Registries
    • Package Registry
  • Analytics
    • Analytics
    • CI / CD
    • Repository
    • Value Stream
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Members
    • Members
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar

新注册的用户请输入邮箱并保存,随后登录邮箱激活账号。后续可直接使用邮箱登录!

  • zkp
  • zkevm-circuitszkevm-circuits
  • Wiki
    • Zkevm docs
    • 4 core
  • core call

core call · Changes

Page history
feat: update core-call --story=1018487 authored Aug 01, 2024 by qingyun Ma's avatar qingyun Ma
Hide whitespace changes
Inline Side-by-side
Showing with 316 additions and 5 deletions
+316 -5
  • zkevm-docs/4-core/core-call.markdown zkevm-docs/4-core/core-call.markdown +316 -5
  • No files found.
zkevm-docs/4-core/core-call.markdown
View page @ 3476bea1
# CALL指令
## 概述
## CALL
### 概述
CALL指令用于实现智能合约间的交互,即从当前智能合约调用另一个智能合约的函数。CALL指令从栈上弹出调用所需的操作数(如目标地址、调用的参数数据,eth amount等),然后执行目标智能合约的代码;CALL指令在执行结束后会向栈上写入一个状态码标识目标合约的函数执行成功或失败,成功时向栈上写入1否则写入0。Note:CALL指令不会引发回滚,即使调用失败当前智能合约仍会继续执行.
......@@ -17,8 +19,7 @@ CALL指令结束写入的操作数: |success|
* 调用方/者:CALL指令的调用者合约
* 被调用方:CALL指令的被调用方合约(即栈上的操作数:addr)
## ZKEVM中CORE指令的设计
### ZKEVM中CORE指令的设计
在zkevm中CORE指令由多个gadget配合完成相应合约调用功能,每个gadget负责CORE指令的一部分逻辑,同时多个gadget之间有相应的执行顺序。
......@@ -40,8 +41,7 @@ CALL指令调用结束后|END_CALL|21
CALL指令调用结束后|POST_CALL_1|22
CALL指令调用结束后|POST_CALL_2|23
## 各gadget的电路布局以及负责的功能
### 各gadget的电路布局以及负责的功能
#### CORE_1 gadget
......@@ -243,3 +243,314 @@ core_5 用于CALL指令结束后回到调用方上下文执行环境时的操作
## 其他的CALL(2024/07/30)
### 概述
- CALL
- callee合约对象中过存储的callerAddress为caller的address
- callee合约对象的address为callee的address
- callee合约对象中的code为callee的code(toAddress对应的code)
- STATICCALL
- callee合约对象中过存储的callerAddress为caller的address
- callee合约对象的address为callee的address
- callee合约对象中的code为callee的code(toAddress对应的code)
- CALLCODE:
- callee合约对象中过存储的callerAddress为caller的地址
- callee合约对象的address为caller的address
- callee合约对象中的code为callee的code(toAddress对应的code)
- DELEGATECALL:
- callee合约对象中过存储的callerAddress为caller.parent的地址
- callee合约对象的address为caller的address
- callee合约对象中的code为callee的code(toAddress对应的code)
因为每一次CALL的stack都是新的,memory是公用的,storage修改的是callee对象中的address对应的slot,所以CALLCODE和DELEGATECALL可以做到调用指定的合约但修改的是自己的存储状态数据
又因为DELEGATE中存储的callerAddress为caller.Parent.Address, 所以DELEGATE中操作msg.sender, msg.value和在caller中操作一样的效果
### 代码修改
1. 添加opcode selector选择器
2. CALL的stack操作数是7个,STATICCALL和DELEGATECALL的stack操作数是8个(需要修改栈的弹出位置以及stamp delta, stack pointer)
3. DELEGATECALL中目标合约代码执行过程中的contract_addr为调用者的contract_addr, sender_addr为调用者的sender_addr
4. LOG_TOPIC_NUM_ADDR中所使用的addr是contract_addr并不是code_addr
说明:
- 除了DELEGATECALL和CALLCODE的其他操作中,contract_addr和code_addr是相等的, msg.sender为caller的addr
- DELEGATECALL中contract_addr为caller.addr, sender为caller.sender
- CALLCODE中contract_addr为caller.addr, sender为caller
注:并未实现CALLCODE,原因参考:(https://docs.soliditylang.org/en/v0.8.26/050-breaking-changes.html,https://docs.soliditylang.org/zh/v0.8.17/050-breaking-changes.html)
### 参数比较
STATICCALL和DELEGATECALL比着CALL和CALLCODE少了一个value的参数
```shell
CALL
|gas|addr|value|argsOffset|argsLength|retOffset|retLength 返回值success
CALLCODE
|gas|addr|value|argsOffset|argsLength|retOffset|retLength 返回值success
DELEGATECALL
|gas|addr|argsOffset|argsLength|retOffset|retLength 返回值success
STATICCALL
|gas|addr|argsOffset|argsLength|retOffset|retLength 返回值success
```
指令行为比较
```shell
CALL
success,
memory[retOffset:retOffset+retLength] =
address(addr).call.gas(gas).value(value)
(memory[argsOffset:argsOffset+argsLength])
CALLCODE
success,
memory[retOffset:retOffset+retLength] =
address(addr).callcode.gas(gas).value(value)
(memory[argsOffset:argsOffset+argsLength])
DELETGATECALL
success,
memory[retOffset:retOffset+retLength] =
address(addr).delegatecall.gas(gas)
(memory[argsOffset:argsOffset+argsLength])
STATICCALL
success, memory[retOffset:retOffset+retLength] =
address(addr).staticcall.gas(gas)
(memory[argsOffset:argsOffset+argsLength])
```
### evm指令代码比较
四个CALL调用逻辑一摸一样
CALL
```go
func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
stack := scope.Stack
// Pop gas. The actual gas in interpreter.evm.callGasTemp.
// We can use this as a temporary value
temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.Address(addr.Bytes20())
// Get the arguments from the memory.
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
if interpreter.readOnly && !value.IsZero() {
return nil, ErrWriteProtection
}
if !value.IsZero() {
gas += params.CallStipend
}
ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, &value)
if err != nil {
temp.Clear()
} else {
temp.SetOne()
}
stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.Gas += returnGas
interpreter.returnData = ret
return ret, nil
}
```
CALLCODE
```go
func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
stack := scope.Stack
// We use it as a temporary value
temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
if !value.IsZero() {
gas += params.CallStipend
}
ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, &value)
if err != nil {
temp.Clear()
} else {
temp.SetOne()
}
stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.Gas += returnGas
interpreter.returnData = ret
return ret, nil
}
```
DELEGATECALL
```go
func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
stack := scope.Stack
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
// We use it as a temporary value
temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas)
if err != nil {
temp.Clear()
} else {
temp.SetOne()
}
stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.Gas += returnGas
interpreter.returnData = ret
return ret, nil
}
```
STATICCALL
```go
func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
// Pop gas. The actual gas is in interpreter.evm.callGasTemp.
stack := scope.Stack
// We use it as a temporary value
temp := stack.pop()
gas := interpreter.evm.callGasTemp
// Pop other call parameters.
addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
toAddr := common.Address(addr.Bytes20())
// Get arguments from the memory.
args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas)
if err != nil {
temp.Clear()
} else {
temp.SetOne()
}
stack.push(&temp)
if err == nil || err == ErrExecutionReverted {
scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
}
scope.Contract.Gas += returnGas
interpreter.returnData = ret
return ret, nil
}
```
### evm调用代码比较
CALL
```go
// callee的地址
addrCopy := addr
// NewContract(caller地址, callee地址,value, gas)
// c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object}
contract := NewContract(caller, AccountRef(addrCopy), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas
```
CALLCODE
```go
addrCopy := addr
// NewContract(caller地址, callee地址,value, gas)
// c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object}
contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
ret, err = evm.interpreter.Run(contract, input, false)
gas = contract.Gas
```
DELEGATECALL
```go
addrCopy := addr
// NewContract(caller地址, callee地址,value, gas)
// c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object}
// c.CallerAddress = parent.CallerAddress
contract := NewContract(caller, AccountRef(addrCopy), new(uint256.Int), gas)
contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
ret, err = evm.interpreter.Run(contract, input, true)
gas = contract.Gas
```
```go
// CallerAddress: 默认是调用者的地址,
func NewContract(caller ContractRef, object ContractRef, value *uint256.Int, gas uint64) *Contract {
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object}
if parent, ok := caller.(*Contract); ok {
// Reuse JUMPDEST analysis from parent context if available.
c.jumpdests = parent.jumpdests
} else {
c.jumpdests = make(map[common.Hash]bitvec)
}
// Gas should be a pointer so it can safely be reduced through the run
// This pointer will be off the state transition
c.Gas = gas
// ensures a value is set
c.value = value
return c
}
func (c *Contract) AsDelegate() *Contract {
// NOTE: caller must, at all times be a contract. It should never happen
// that caller is something other than a Contract.
parent := c.caller.(*Contract)
// 被调用合约中记录的调用者的地址和调用者的调用者是同一个地址
c.CallerAddress = parent.CallerAddress
// value和调用者的value一致
c.value = parent.value
return c
}
```
Clone repository
  • basics
    • evm
    • halo2
  • code notes
    • binary_number_with_real_selector
    • how to use macro
    • simple_lt
    • simple_lt_word
  • Home
  • image
  • zkevm docs
    • 1 introduction
    • 10 public
    • 11 fixed
    • 12 exp
    • 13 keccak
    • 14 comparisons
    • 15 differences
View All Pages

Copyright © 2024 ChainWeaver Org. All Rights Reserved. 版权所有。

京ICP备2023035722号-3

京公网安备 11010802044225号