当你作为 Solidity 开发者开始写 Ethereum 智能合约的时候,你会很快接触到一些概念像是EVM(Ethereum Virtual Machine),bytecode 和 ABI(application binary interface),如果你是一个 Javascript 开发者(就像我第一次学习代码的时候一样),这些专有名字可能对你来说并不陌生,你可能想知道在 Solidity 和 Ethereum 世界中,这些名字和你理解的意思一不一样。
这篇文章会从技术的角度去了解这三个概念,看完这篇文章以后,你会知道什么是 EVM,Bytecode 和 ABI,而且会了解怎么样在项目中快速生成和使用 bytecode 和 ABI。
VM 和 EVM
让我们从以太坊虚拟机(EVM)开始了解,首先先把 EVM 放在一边,理解什么是 VM。通俗的来讲,VM 也是一个能够在硬件上运行的软件,但是与其他软件不同的是,VM 主要是被设计来模拟硬件的。即这个软件是用来“假装”自己是一个硬件,就像音乐 App 是一个虚拟的播放系统一样,它不是一个物理的硬件,但是模拟了一个物理机器。
为什么我们需要虚拟机?答案是虚拟机可以有效地扩展,管理和升级软件运行所以来的基础设施。要使用 1000 个物理服务器的业务,你可能只需要使用 20 个然后在每一个物理服务器上跑 50 个虚拟机。你甚至可以让每一个虚拟机都运行不同的操作系统,比如一个虚拟机运行 Windows 服务器,第二个运行 Linux Debian,第三个运行 Gentoo Linux,然后第四个运行 ChromeOS!
在同一个硬件上的不同虚拟即运行多个操作系统
这样做的好处是你可以在这些虚拟机上运行多个应用,所有的这些都是运行在同一个硬件中的,所以这个硬件的计算资源和系统资源的使用效率更高,提升了基础设施的性价比。
以太坊虚拟机(EVM)也是一种虚拟机。但是 EVM 的目的是创造一个去中心化的“世界计算机”,而不是最高效地使用硬件资源。区块链网络中每一个“节点”都是一个单独的硬件,EVM 是众多节点的集合。每一个运行客户端软件的节点,都会实现以太坊技术规范,因为他们需要彼此连接,形成一个网络。然后这个网络上的节点会同步他们的状态,以形成一个经常更新的巨大的数据库。网络中的节点需要实现对数据状态的同步,而数据的同步是由共识算法实现的。
EVM 作为一个去中心化虚拟机,可以运行叫做智能合约的程序。就像其他应用一样,我们写完智能合约以后会去编它,然后才能被部署(部署在以太坊区块链网络)。因为区块链是不可篡改的,所以合约一旦部署完成,就不能被修改了。合约部署完成以后,EVM 就是这个合约中的代码被执行的环境,也就是运行我们部署的智能合约的虚拟机。
我们使用人类可读的代码语言来写程序的(即使在刚开始学习的时候,可读性并不是非常强),这是因为我们需要阅读,编辑,维护和 debug 软件。但是机器是无法执行人类可读代码的,它们只能够识别二进制数据,也就是一串由 0 和 1 所组成的数据流。所以在我们完成代码之后,需要请求编译器(也是一个软件)来将其编译以便它在机器上运行。
在 Solidity 中,我们编译代码以后,会得到两个“artifact”:bytecode 和 ABI。
Solidity 中的 Bytecode
Bytecode 是 Solidity 代码被翻译以后的信息,它包括了二进制的计算机指令。Bytecode 通常是将数字,常量和其他信息以一种编码方式写在一起。每一个指令都是一个被称为opcode 的操作,这些 opcode 的大小都是 1 比特(8 位)。这也是为什么我们叫它“bytecode”,即长度为 one-byte 的 opcode。
因为每一行代码都会被分拆变成一个个 opcode,所以计算机会在运行代码的时候会清楚的知道要做什么。
在以太坊,其实被部署在区块链上的就是 bytecode。当我们用 Metamask 将合约部署到区块链,并确认交易的时候,我们实际上可以看到 bytecode 被部署了。bytecode 可以被拆分成多个 opcode,但是本文不会涉及。
通过 Metamask 部署智能合约时可以看到 bytecode.
Bytecode 就是我们存储在以太坊网络的东西,同时它在我们与智能合约交互的时候会被执行。有很多工具和库(包括 Solidity 编译机,solc)可以帮助你将 Solidity 代码编译为 bytecode。最快的方式就是通过在线 IDE Remix来编译合约,然后复制 ABI 和 bytecode。
这里举个小例子,怎样生成和复制 bytecode,点击这个连接然后打开 Chainlink Price Feed,可以在 Remix 中看到 Solidity 智能合约。点击编译然后就可以复制 bytecode 了,非常简单!
使用 Remix 生成 bytecode
什么是 Solidity ABI?
你可能听说过 API(application programming interfaces),它实际上包含很多方法,函数,变量和常量,你可以通过 API 与一个库,网络的 endpoint,后端服务或者是其他的软件服务和应用。API 可以以一种可控,稳定和易于使用的方式,将一个软件的功能提供给外界。API 是一个接口,可以帮助两个软件彼此之间进行交互。
ABI 是 application binary interfaces,它定义了智能合约中可以进行交互的方法和变量。因为智能合约在部署到区块链之前就已经转变为 bytecode,所以我们需要知道有哪些可以使用的操作,同时我们也需要一个标准来展示这些接口以便别的编程语言也可以与智能合约交互。尽管 Javascript 是与智能合约交互最常用的语言(主要是因为 Javascript 是一个前端语言,而我们通常调用合约的方式通过前端网页),但是只要你有 ABI,就可以使用任何一种变成语言与合约交互。
Solidity 中 ABI 的结构
所以 ABI 就是一些定义,帮助我们知道方法名字,参数,数据类型以便我们与合约交互,同时也是智能合约 emit 的事件的结构。对于函数,以下是在 ABI 中找到的属性:
- type: 定义了函数的类型,类型是
function
,constructor
,receive
orfallback
中的一个。 - name: 函数的名称是什么。
- inputs: 一个包含了下列信息的对象的数组
- name: 参数名称。
- type: 参数类型。
- components: 参数类型是 tuple 的时候会使用到。
- outputs: 类似于 inputs 的对象数组。
- stateMutability: 标注函数状态的字符串。值可以是
view
,pure
,view
,nonpayable
, 和payable
。
定制化的错误和时间有很相似的数据结构,你可以在官方文档中学习它。
ABI 可以被表示成一个类似于下面的 JSON 文件
[
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "getLatestPrice",
"outputs": [
{
"internalType": "int256",
"name": "",
"type": "int256"
}
],
"stateMutability": "view",
"type": "function"
}
]
你可以再次在 Remix IDE 中打开 Chainlink Price Feed 合约,然后生成类似于上述 ABI。编译代码,然后再获取 ABI,如下图所示。
使用 Remix 快速生成 ABI
你可以从 ABI 中看出一个函数是一个普通函数还是构造函数,它的输入是什么,返回值的数据类型是什么和其他更多的信息。这就是你与智能合约交互时所需要用到的信息。当然,在使用方式上,你可以最终会使用像是 EtherJS 这样的库来与合约交互,但是 ABI 是必要的。
还有另外一个获取 ABI 和 Bytecode 的方法:你可以通过 Remix 中的“Compilation Details”获得需要的信息,如下图所示。注意这里你只能获得你自己写的智能合约的 ABI 和 bytecode,引入的库和合约没法通过这种方式获取对应的 ABI 和 bytecode。
通过 Reimx 获得 ABI 和 bytecode
你现在已经了解 EVM 是什么,bytecode 是什么,为什么 ABI 对于智能合约很重要了。更重要的是,你已经学习到一些技巧,在浏览器中就可以获得 compiled artifact,希望这些知识可以帮助你提升开发效率。
您可以关注 Chainlink 预言机并且私信加入开发者社区,有大量关于智能合约的学习资料以及关于区块链的话题!