ArcBlock 博客 | 深入探索 EVM : 编译和部署智能合约

本文由ArcBlock软件工程师丁沛灵撰写,深入探讨以太坊虚拟机(EVM)如何执行智能合约。文章通过一个简单的Solidity智能合约示例,详细解释了字节码的编译过程、部署智能合约的代码结构,包括Payable检查、执行构造函数和复制代码等步骤,并介绍了World State、成员变量的存储和编译优化等概念。最后,文章强调了以太坊虚拟机的gas优化策略,并预告了下期将讨论有参数的构造函数和ABI Encoding等内容。
摘要由CSDN通过智能技术生成

作者:丁沛灵 (ArcBlock 软件工程师)

以太坊虚拟机(Ethereum Virtual Machine)是以太坊的基础,它负责执行所有的交易(Transaction),并且根据这些Transaction 来维护整个以太坊的账户状态,或者更准确的称之为 World State。Transaction分很多种,有最简单的以太币(Ether)交易,有部署或者调用智能合约的交易。智能合约(Smart Contract)是由虚拟机执行的代码,用以完成复杂的业务逻辑。Solidity 是目前最流行的编写智能合约的高级语言。由 Solidity 编写的智能合约会先被编译成可被虚拟机直接接受的字节码,然后会被用户以 Transaction 的方式发送给以太坊从而进行智能合约部署。在这之后,用户便可以调用智能合约的函数来完成业务逻辑。那么在整个流程中,Solidity 代码是如何被编译成字节码的?字节码在虚拟机中又是如何运行的?编译字节码的时候,虚拟机如何对其进行优化?本文将带你一起,详细剖析这些问题。

视频:深入探索 EVM

从一个例子开始

让我们从一个最简单的智能合约例子开始。

pragma solidity ^0.4.11;contract C {
    uint256 a;
    function C() {
      a = 1;
    }}

这段代码非常类似Java,为了简单起见,在这里我就借用一下Java的术语。这段智能合约有一个成员变量a,其类型是一个256位的无符号整型数。另外,它还有一个构造函数,在其中我们将成员变量a赋值为1。下面让我们来编译这段代码,我们有两个工具可以用来编译代码:

  • solc --bin --asm file_name.sol

  • http://remix.ethereum.org

第一个是一个命令行工具,大家需要先自行安装。第二个是一个强大的网页版IDE,它可以快速的编译,部署以及调试智能合约。编译后的代码我们称之为字节码(bytecode),如下所示:

60606040523415600e57600080fd5b600160008190555060358060236000396000f3006060604052600080fd00a165627a7a72305820d315875f56b532ab371cf9aa86a62850e13eb6ab194847011dcd641b9a9d2f8d0029

在这段字节码中,每个字符代表一个16进制数,每两个字符代表一个字节。这段字节码就是直接运行在虚拟机上的代码,虚拟机只需要按照事先定义好的规则,解释并且执行每个字节即可。但是对人类来说,直接阅读这些字节码太过繁琐,所以我们可以将其转换成对人类更友好的形式,操作码(OpCodes),如下所示:

PUSH1 0x60 PUSH1 0x40 MSTORE CALLVALUE ISZERO PUSH1 0xE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0x0 DUP2 SWAP1 SSTORE POP PUSH1 0x35 DUP1 PUSH1 0x23 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x60 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 0xd3 ISZERO DUP8 0x5f JUMP 0xb5 ORIGIN 0xab CALLDATACOPY SHR 0xf9 0xaa DUP7 0xa6 0x28 POP 0xe1 RETURNDATACOPY 0xb6 0xab NOT 0x48 0x47 ADD SAR 0xcd PUSH5 0x1B9A9D2F8D STOP 0x29

上面的字节码或者操作码是等价的,它们都可以被分为三个部分:

  • 部署智能合约的代码

60606040523415600e57600080fd5b600160008190555060358060236000396000f300

PUSH1 0x60 PUSH1 0x40 MSTORE CALLVALUE ISZERO PUSH1 0xE JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST PUSH1 0x1 PUSH1 0x0 DUP2 SWAP1 SSTORE POP PUSH1 0x35 DUP1 PUSH1 0x23 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP
  • 智能合约本身的代码

6060604052600080fd00

PUSH1 0x60 PUSH1 0x40 MSTORE PUSH1 0x0 DUP1 REVERT STOP
  • Auxdata

a165627a7a72305820d315875f56b532ab371cf9aa86a62850e13eb6ab194847011dcd641b9a9d2f8d0029

LOG1 PUSH6 0x627A7A723058 KECCAK256 0xd3 ISZERO DUP8 0x5f JUMP 0xb5 ORIGIN 0xab CALLDATACOPY SHR 0xf9 0xaa DUP7 0xa6 0x28 POP 0xe1 RETURNDATACOPY 0xb6 0xab NOT 0x48 0x47 ADD SAR 0xcd PUSH5 0x1B9A9D2F8D STOP 0x29

下面让我们来逐步讲解每个部分,看看它们都是怎么工作的。

1. 部署智能合约的代码

第一部分代码是事实上把智能合约部署到以太坊上的代码,也是我们重点讨论的部分。这段代码又可以被划分为三个部分:

  • Payable 检查

60606040523415600e57600080fd

PUSH1 0x60 PUSH1 0x40 MSTORE CALLVALUE ISZERO PUSH1 0xE JUMPI PUSH1 0x0 DUP1 REVERT
  • 执行构造函数

5b6001600081905550

JUMPDEST PUSH1 0x1 PUSH1 0x0 DUP2 SWAP1 SSTORE POP
  • 复制代码,并将其返回给内存

60358060236000396000f300

PUSH1 0x35 DUP1 PUSH1 0x23 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP

1.1 Payable检查

payable是Solidity的一个关键字,如果一个函数被其标记,那么用户在调用该函数的同时还可以发送以太币到该智能合约。而这部分字节码的意义就在于阻止用户在调用没有被payable标记的函数时,向该智能合约发送以太币。下面这张图是对这段代码进一步演算,左边两列分别是字节码和操作码,最右边一列是执行完该条语句之后栈的状态。

在上图中,前三句是将内存中从0x40开始往后32个字节的地址赋上0x60这个值,这是虚拟机保留的内存地址。后面的几句就是在通过查看发送的以太币是否为0来做payable检查。如果

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值