以太坊智能合约之代码加载分析

时间:2024-03-13 13:40:44

代码构成

    以太坊智能合约代码由两部分构成,具体以如下代码为例来说明

contract C {
    uint256 public a = 2;
    function C() {
    }
    function d() {
        a = 3;
    }
    function e() {
        a = 1;
    }
}
  • 初始化代码

    初始化代码只在创建智能合约的时候调用,只被调用一次
    初始化代码又由两部分构成:
    • 构造函数
      比如上面的C()函数
      function C() {
      }
    • 全局变量初始化代码
      uint256 public a = 2;
  • 接口代码

    就是具体的实现函数,这个代码是智能合约非创建时间点的入口代码
function d() {
    a = 3;
}
function e() {
    a = 1;
}


汇编分析

初始化代码

  • 全局变量a初始化代码
.code
PUSH 80 contract C {\n uint256 publ...
PUSH 40 contract C {\n uint256 publ...
MSTORE contract C {\n uint256 publ...
PUSH 2 2
PUSH 0 uint256 public a = 2
SSTORE uint256 public a = 2
  • 返回接口代码的初始化代码
    检测value值,因为该合约构造函数不支持payable,所以如果value不为0,会revert
CALLVALUE function C() {\n }
DUP1 C {\n
ISZERO ac
PUSH [tag] 1 ac
JUMPI ac
PUSH 0 c
DUP1 b
REVERT t256 public
  • 返回接口代码的初始化代码
    以下代码会返回该程序.data的代码,也就是接口代码的内容,核心代码是CODECOPY, 也就是通过CODECOPY将接口代码.data内容拷贝到memory,然后通过RETURN返回对应的memory的内容,也就是返回接口代码.data数据给系统代码
tag 1 ac
JUMPDEST ac
POP function C() {\n }
PUSH #[$] 0000000000000000000000000000000000000000000000000000000000000000 contract C {\n uint256 publ...
DUP1 contract C {\n uint256 publ...
PUSH [$] 0000000000000000000000000000000000000000000000000000000000000000 contract C {\n uint256 publ...
PUSH 0 contract C {\n uint256 publ...
CODECOPY contract C {\n uint256 publ...
PUSH 0 contract C {\n uint256 publ...
RETURN contract C {\n uint256 publ...

接口代码

我们知道,一个只能合约可能有很多合约,而客户端只通过input字段传入了调用函数的信息,因而智能合约需要有一个解释器,用来解释客户端想调用智能合约的哪一个接口(函数) ,传递了什么参数值. 因而接口代码也分为两部分
  • 根据input数据查找函数
.data
0:
.code
PUSH 80 contract C {\n uint256 publ...
PUSH 40 contract C {\n uint256 publ...
MSTORE contract C {\n uint256 publ...
PUSH 4 contract C {\n uint256 publ...
CALLDATASIZE contract C {\n uint256 publ...
LT contract C {\n uint256 publ...
PUSH [tag] 1 contract C {\n uint256 publ...
JUMPI contract C {\n uint256 publ...
PUSH 0 contract C {\n uint256 publ...
CALLDATALOAD contract C {\n uint256 publ...
PUSH 100000000000000000000000000000000000000000000000000000000 contract C {\n uint256 publ...
SWAP1 contract C {\n uint256 publ...
DIV contract C {\n uint256 publ...
PUSH FFFFFFFF contract C {\n uint256 publ...
AND contract C {\n uint256 publ...
DUP1 contract C {\n uint256 publ...
PUSH DBE671F contract C {\n uint256 publ...
EQ contract C {\n uint256 publ...
PUSH [tag] 2 contract C {\n uint256 publ...
JUMPI contract C {\n uint256 publ...
DUP1 contract C {\n uint256 publ...
PUSH 8A054AC2 contract C {\n uint256 publ...
EQ contract C {\n uint256 publ...
PUSH [tag] 3 contract C {\n uint256 publ...
JUMPI contract C {\n uint256 publ...
DUP1 contract C {\n uint256 publ...
PUSH FFAE15BA contract C {\n uint256 publ...
EQ contract C {\n uint256 publ...
PUSH [tag] 4 contract C {\n uint256 publ...
JUMPI contract C {\n uint256 publ...
具体函数实现
tag 1 contract C {\n uint256 publ...
JUMPDEST contract C {\n uint256 publ...
PUSH 0 contract C {\n uint256 publ...
DUP1 contract C {\n uint256 publ...
REVERT contract C {\n uint256 publ...
tag 2 uint256 public a = 2
JUMPDEST uint256 public a = 2
CALLVALUE uint256 public a = 2
DUP1 C {\n
ISZERO ac
PUSH [tag] 5 ac
JUMPI ac
PUSH 0 c
DUP1 b
REVERT t256 public
tag 5 ac
JUMPDEST ac
POP uint256 public a = 2
PUSH [tag] 6 uint256 public a = 2
PUSH [tag] 7 uint256 public a = 2
JUMP uint256 public a = 2
tag 6 uint256 public a = 2
JUMPDEST uint256 public a = 2
PUSH 40 uint256 public a = 2
MLOAD uint256 public a = 2
DUP1 uint256 public a = 2
DUP3 uint256 public a = 2
DUP2 uint256 public a = 2
MSTORE uint256 public a = 2
PUSH 20 uint256 public a = 2
ADD uint256 public a = 2
SWAP2 uint256 public a = 2
POP uint256 public a = 2
POP uint256 public a = 2
PUSH 40 uint256 public a = 2
MLOAD uint256 public a = 2
DUP1 uint256 public a = 2
SWAP2 uint256 public a = 2
SUB uint256 public a = 2
SWAP1 uint256 public a = 2
RETURN uint256 public a = 2
tag 3 function d() {\n a = 3;...
JUMPDEST function d() {\n a = 3;...
CALLVALUE function d() {\n a = 3;...
DUP1 C {\n
ISZERO ac
PUSH [tag] 8 ac
JUMPI ac
PUSH 0 c
DUP1 b
REVERT t256 public
tag 8 ac
JUMPDEST ac
POP function d() {\n a = 3;...
PUSH [tag] 9 function d() {\n a = 3;...
PUSH [tag] 10 function d() {\n a = 3;...
JUMP function d() {\n a = 3;...
tag 9 function d() {\n a = 3;...
JUMPDEST function d() {\n a = 3;...
STOP function d() {\n a = 3;...
tag 4 function e() {\n a = 1;...
JUMPDEST function e() {\n a = 1;...
CALLVALUE function e() {\n a = 1;...
DUP1 C {\n
ISZERO ac
PUSH [tag] 11 ac
JUMPI ac
PUSH 0 c
DUP1 b
REVERT t256 public
tag 11 ac
JUMPDEST ac
POP function e() {\n a = 1;...
PUSH [tag] 12 function e() {\n a = 1;...
PUSH [tag] 13 function e() {\n a = 1;...
JUMP function e() {\n a = 1;...
tag 12 function e() {\n a = 1;...
JUMPDEST function e() {\n a = 1;...
STOP function e() {\n a = 1;...
tag 7 uint256 public a = 2
JUMPDEST uint256 public a = 2
PUSH 0 uint256 public a = 2
SLOAD uint256 public a = 2
DUP2 uint256 public a = 2
JUMP [out] uint256 public a = 2
tag 10 function d() {\n a = 3;...
JUMPDEST function d() {\n a = 3;...
PUSH 3 3
PUSH 0 a
DUP2 a = 3
SWAP1 a = 3
SSTORE a = 3
POP a = 3
JUMP [out] function d() {\n a = 3;...
tag 13 function e() {\n a = 1;...
JUMPDEST function e() {\n a = 1;...
PUSH 1 1
PUSH 0 a
DUP2 a = 1
SWAP1 a = 1
SSTORE a = 1
POP a = 1
JUMP [out] function e() {\n a = 1;...
.data

初始化代码执行源码分析


func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
    ….
    contract := NewContract(caller, AccountRef(contractAddr), value, gas)
    //初始化code为全部的数据(包括code,data数据)
    contract.SetCallCode(&contractAddr, crypto.Keccak256Hash(code), code)

    if evm.vmConfig.NoRecursion && evm.depth > 0 {
        return nil, contractAddr, gas, nil
    }

    if evm.vmConfig.Debug && evm.depth == 0 {
        evm.vmConfig.Tracer.CaptureStart(caller.Address(), contractAddr, true, code, gas, value)
    }
    start := time.Now()
    //这里会执行初始化代码,返回值是接口代码数据
    ret, err = run(evm, contract, nil)

    // check whether the max code size has been exceeded
    maxCodeSizeExceeded := evm.ChainConfig().IsEIP158(evm.BlockNumber) && len(ret) > params.MaxCodeSize
    // if the contract creation ran successfully and no errors were returned
    // calculate the gas required to store the code. If the code could not
    // be stored due to not enough gas set an error and let it be handled
    // by the error checking condition below.
    if err == nil && !maxCodeSizeExceeded {
        createDataGas := uint64(len(ret)) * params.CreateDataGas
        if contract.UseGas(createDataGas) {
            //将contract的code更新为接口代码
            evm.StateDB.SetCode(contractAddr, ret)
        } else {
            err = ErrCodeStoreOutOfGas
        }
    }
    return ret, contractAddr, contract.Gas, err
}

/********************************
* 本文来自CSDN博主"爱踢门"
* 转载请标明出处:http://blog.csdn.net/itleaks
******************************************/

以太坊智能合约之代码加载分析