以太坊智能合约之语言汇编分析

开发环境

Browser-solidity是一个solidity在线网页开发ide
https://ethereum.github.io/browser-solidity
记得用chrome浏览器打开
相关操作如下







合约汇编分析


     evm的变量存储是采用的方式存储的,核心是key的计算,对于不同类型的变量,这个key的计算方法也不一样

简单变量


contract C {
    uint256 a = 2;
    uint256 b = 3;
}
对应的汇编代码
PUSH1 0x2 PUSH1 0x0 SSTORE PUSH1 0x3 PUSH1 0x1 SSTORE
可见这种情况的 变量的key是变量的序号p(0, 1, 2, 3, ... )
 这种情况下key = p

固定长度数组


contract D {
    uint256[6] ar;
    function D() {
      ar[5] = 0x0ABCD;
    }
}

PUSH2 0xABCD PUSH1 0x0 PUSH1 0x5 PUSH1 0x6 DUP2 LT ISZERO ISZERO PUSH1 0x6A JUMPI INVALID JUMPDEST ADD DUP2 SWAP1 SSTORE

PUSH2 0xABCD                         [0xABCD]
PUSH1 0x0                            [0xABCD, 0x0]
PUSH1 0x5                            [0xABCD, 0x0, 0x5]
PUSH1 0x6                            [0xABCD, 0x0, 0x5, 0x6
DUP2 LT ISZERO ISZERO PUSH1 0x6A     [0xABCD, 0x0, 0x5, 0x0, 0x6A]
//判断是否超出数组范围
JUMPI INVALID                        [0xABCD, 0x0, 0x5
JUMPDEST                             [0xABCD, 0x0, 0x5
ADD                                  [0xABCD, 0x5]
DUP2                                 [0xABCD, 0x5, 0xABCD]
SWAP1                                [0xABCD, 0xABCD, 0x5]
SSTORE                               [0xABCD]

再增加一个赋值时
contract D {
    uint256[6] ar;
    function D() {
      ar[4] = 0x02345;
      ar[5] = 0x0ABCD;
    }
}
对应的汇编为
PUSH2 0x2345 PUSH1 0x0 PUSH1 0x4 PUSH1 0x6 DUP2 LT ISZERO ISZERO PUSH1 0x6A JUMPI INVALID JUMPDEST ADD DUP2 SWAP1 SSTORE POP

PUSH2 0xABCD PUSH1 0x0 PUSH1 0x5 PUSH1 0x6 DUP2 LT ISZERO ISZERO PUSH1 0x81 JUMPI INVALID JUMPDEST ADD DUP2 SWAP1 SSTORE

可见这种情况下的key = p + index

如果我们的上面的index变为参数呢
contract D {
    uint256[6] ar;
    function D(uint256 index) {
      ar[index] = 0x0789a;
    }
}

PUSH2 0x789A PUSH1 0x0 DUP3 PUSH1 0x6 DUP2 LT ISZERO ISZERO PUSH1 0x7F JUMPI INVALID JUMPDEST ADD DUP2 SWAP1 SSTORE

可见index是DUP3可以得到,那这个DUP3对应的就是参数,那这个参数如何解释出来的呢?其实就是函数参数解释的问题

函数名检验
PUSH1 0x4 CALLDATASIZE LT PUSH1 0x3F JUMPI PUSH1 0x0 CALLDATALOAD PUSH29 0x100000000000000000000000000000000000000000000000000000000 SWAP1 DIV PUSH4 0xFFFFFFFF AND DUP1 PUSH4 0x6E9ED8CF EQ PUSH1 0x44 JUMPI

获取calldata的前四个字节和函数生成的hash前自己个字节(0x6E9ED8CF)比较,如果相同则为合法的调用

获取函数参数
然后继续获取真正的参数
POP PUSH1 0x6C PUSH1 0x4 DUP1 CALLDATASIZE SUB DUP2 ADD SWAP1 DUP1 DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 SWAP3 SWAP2 SWAP1 POP POP POP PUSH1 0x6E JUMP
上面是优化前的代码,下面是优化后的代码
PUSH1 0x58 PUSH1 0x4 CALLDATALOAD PUSH1 0x5A JUMP JUMPDEST STOP JUMPDEST

PUSH1 0x58                            [0x58]
PUSH1 0x4                             [0x58, 0x4
CALLDATALOAD                          [0x58, arg]
PUSH1 0x5A                            []
JUMP JUMPDEST STOP JUMPDEST

PUSH1 0x6C                               [0x6C
PUSH1 0x4                                [0x6C, 0x4
DUP1                                     [0x6C, 0x4, 0x4]
CALLDATASIZE                             [0x6C, 0x4, 0x4, 0x36]
SUB                                      [0x6C, 0x4, 0x32]
DUP2                                     [0x6C, 0x4, 0x32, 0x4]
ADD                                      [0x6C, 0x4, 0x36]
SWAP1                                    [0x6C, 0x36, 0x4]
DUP1                                     [0x6C, 0x36, 0x4, 0x4]
DUP1 CALLDATALOAD SWAP1 PUSH1 0x20 ADD SWAP1 SWAP3 SWAP2 SWAP1 POP POP POP PUSH1 0x6E JUMP


map实例


contract C {
    mapping (uint => uint) map;
   
    function C() {
        map[1] = 2;
    }
}

对应的汇编如下:
PUSH1 0x2 PUSH1 0x0 DUP1 PUSH1 0x1 DUP2 MSTORE PUSH1 0x20 ADD SWAP1 DUP2 MSTORE PUSH1 0x20 ADD PUSH1 0x0 KECCAK256 DUP2 SWAP1 SSTORE

  PUSH1 2                     [0x2] //右侧[]为stack
PUSH1 0                     [0x2, 0x0]
DUP1                      [0x2, 0x0, 0x0] 
  PUSH1 1                     [0x2, 0x0, 0x0, 0x01]  
  DUP2                  [0x2, 0x0, 0x0, 0x01, 0x0]  
  MSTORE [0x2, 0x0, 0x0]

PUSH 20            [0x2, 0x0, 0x0, 0x20]              
  ADD                         [0x2, 0x0, 0x20]  
  SWAP1                       [0x2, 0x20, 0x0]
  DUP2                  [0x2, 0x20, 0x0, 0x20]
  MSTORE                      [0x2, 0x20]
  
  PUSH1 20                    [0x2, 0x20, 0x20]
  ADD                   [0x2, 0x40]
PUSH1 0        [0x2, 0x40, 0x0]

KECCAK256                   [0x2, sha3]
SWAP1                       [sha3, 0x2]
SSTORE []

上面的代码中
map[1] = 2在stateDb是以形式保存,所以核心的逻辑是如何生成根据map变量和'1'这个key生成最后的newkey
newkey = KECCAK256(k . p) 
这个例子中,k=1, p=0

KECCAK256工作流程

KECCAK256执行时需要两部分数据
  • 输入数据 (memory)
  • 存在memory中的输入数据的位置大小信息(stack)
准备memory:
Mem[0] = 0x1

 PUSH1 2                    [0x2]
PUSH1 0                    [0x2, 0x0]
DUP1                      [0x2, 0x0, 0x0] 
  PUSH1 1                    [0x2, 0x0, 0x0, 0x01]  
  DUP2                  [0x2, 0x0, 0x0, 0x01, 0x0]  
  MSTORE [0x2, 0x0, 0x0]

Mem[0x20] = 0x0
PUSH1 20           [0x2, 0x0, 0x0, 0x20]              
  ADD                         [0x2, 0x0, 0x20]  
  SWAP1                       [0x2, 0x20, 0x0]
  DUP2                  [0x2, 0x20, 0x0, 0x20]
  MSTORE                      [0x2, 0x20]

准备stack:

PUSH1 20                      [0x2, 0x20, 0x20]
ADD                   [0x2, 0x40]
PUSH1 0        [0x2, 0x40, 0x0]

生成hash
KECCAK256                     [0x2, sha3]

赋值
SWAP1                         [sha3, 0x2]
SSTORE []


/********************************
* 本文来自CSDN博主"爱踢门"
* 转载请标明出处:http://blog.csdn.net/itleaks
******************************************/
以太坊智能合约之语言汇编分析_第1张图片

你可能感兴趣的:(区块链,以太坊源码分析)