以太坊基础知识

什么是以太坊

以太坊(Ethereum)是一个去中心化的开源的有智能合约功能的公共区块链平台。 以太坊的概念首次在 2013 至 2014 年间由程序员维塔利克·布特林(Vitalik Buterin)受比特币启发后提出,大意为“下一代加密货币与去中心化应用平台”,在 2014 年通过 ICO 众筹得以开始发展。以太坊亦被称为“第二代的区块链平台”,仅次于比特币。目前为止,以太坊是被使用最多的区块链平台。

以太币(ETH 或 Ξ)是以太坊的原生加密货币,目前是市值第二高的加密货币,仅次于比特币。

以太坊特点

  1. 以太坊是 “世界计算机”,这代表它是一个开源的、全球分布的计算基础设施。
  2. 执行称为智能合约(smart contract)的程序。
  3. 使用区块链来同步和存储系统状态以及使用名为以太币(ether)的加密货币,以计量和约束执行资源成本。
  4. 本质是一个基于交易的状态机 (transaction-based state machine)。
  5. 以太坊平台使开发人员能够构建具有内置经济功能的强大去中心化应用程序(DApp);在持续自我正常运行的同时,它还减少或消除了审查,第三方界面和交易对手风险。

以太坊的组成部分

P2P 网络

以太坊在以太坊主网络上运行,该网络可在 TCP 端口 30303 上寻址,并运行一个名为 ÐΞVp2p 的协议。

交易(Transaction)

以太坊交易是网络消息,其中包括发送者(sender),接收者(receiver),值(value) 和数据的有效载荷(payload)。

以太坊虚拟机(EVM)

以太坊状态转换由以太坊虚拟机(EVM)处理,这是一个执行字节码(机器语言指令)的 基于堆栈的虚拟机。

数据库(Blockchain)

以太坊的区块链作为数据库(通常是 Google 的 LevelDB)本地存储在每个节点上,包含 序列化后的交易和系统状态。

客户端

以太坊有几种可互操作的客户端软件实现,其中最突出的是 Go-Ethereum(Geth)和 Parity。

以太坊中的重要概念

账户(Account)

账户包含地址,余额和随机数,以及可选的存储和代码的对象。

  • 普通账户(EOA),存储和代码均为空。
  • 合约账户(Contract),包含存储和代码。

地址(Address)

一般来说,代表一个 EOA 或合约,它可以在区块链上接收或发送交易, 更具体地说,它是 ECDSA 公钥的 keccak 散列的最右边的 160 位

交易(Transaction)

  • 可以发送以太币和信息。
  • 向合约发送的交易可以调用合约代码,并以信息数据为函数参数。
  • 向空用户发送信息,可以自动生成以信息为代码块的合约账户。

gas

以太坊用于执行智能合约的虚拟燃料, 以太坊虚拟机使用核算机制来衡量 gas 的消耗量并限制计算资源的消耗

以太坊的货币

以太坊的货币单位称为以太(ether),也可以表示为 ETH 或符号 Ξ

  • 以太币的发行规则:

    1 、挖矿前(Pre-mine,Genesis)

    2014 年 7 月 / 8 月间,为众筹大约发行了 7200 万以太币。这些币有的时候被称之为 “矿前”。众筹阶段之后,以太币每年的产量基本稳定,被限制不超过 7200 万的 25%。

    2、挖矿产出(Mining)

    • 区块奖励(block reward)
    • 叔块奖励(uncle reward)
    • 叔块引用奖励(uncle referencing reward)
  • 以太币产量未来的变化

    以太坊出块机制从工作量证明(PoW)转换为股权证明(PoS)后,以太币的发行会有什么变化尚未有定论。股权证明机制将使用一个称为 Casper 的协议。在 Casper 协议下,以太币的发行率将大大低于工作量证明下幽灵(GHOST)协议下的发行率。

以太坊的挖矿产出

区块奖励(Block rewards)

每产生一个新区块就会有一笔固定的奖励给矿工,初始是 5 个以太币,现在是 3 个。

叔块奖励(Uncle rewards)

有些区块被挖得稍晚一些,因此不能作为主区块链的组成部分。比特币称这类区块为 “孤块”,并且完全舍弃它们。但是,以太币称它们为 “叔块”(uncles),并且在之后的区块中,可以引用它们。如果叔块在之后的区块链中作为叔块被引用,每个叔块会为挖矿者产出区块奖励的 7/8。这被称之为叔块奖励。

叔块引用奖励(Uncle referencing rewards)

矿工每引用一个叔块,可以得到区块奖励的 1/32 作为奖励(最多引用两个叔块)。

以太坊区块收入

普通区块收入

  • 固定奖励(挖矿奖励),每个普通区块都有。
  • 区块内包含的所有程序的 gas 花费的总和。
  • 如果普通区块引用了叔块,每引用一个叔块可以得到固定奖励的 1/32

叔块收入

叔块收入只有一项,就是叔块奖励,计算公式为: 叔块奖励 = (叔块高度 + 8 – 引用叔块的区块高度) * 普通区块奖励 / 8。

以太坊和图灵完备

1936 年,英国数学家艾伦 · 图灵(Alan Turing)创建了一个计算机的数学模型,它由一个控制器、一个读写头和一根无限长的工作带组成。纸带起着存储的作用,被分成一个个的小方格(可以看成磁带);读写头能够读取纸带上的信息,以及将运算结果写进纸带;控制器则负责根据程序对搜集到的信息进行处理。在每个时刻,机器头都要从当前纸带上读入一个方格信息,然后结合自己的内部状态查找程序表,根据程序输出信息到纸带方格上,并转换自己的内部状态,然后进行移动纸带。这样的机器称为图灵机。


如果一个系统可以模拟任何图灵机,它就被定义为 “图灵完备”(Turing Complete)的。 这种系统称为通用图灵机(UTM)

以太坊能够在称为以太坊虚拟机的状态机中执行存储程序,同时向内存读取和写入数据, 使其成为图灵完备系统,因此成为通用图灵机。考虑到有限存储器的限制,以太坊可以计 算任何可由任何图灵机计算的算法。简单来说,以太坊中支持循环语句,理论上可以运行 “无限循环” 的程序

去中心化应用

  • 基于以太坊可以创建智能合约 (Smart Contract) 来构建去中心化应用 (Decentralized Application,简称为 DApp)
  • 以太坊的构想是成为 DApps 编程开发的平台
  • DApp 至少由以下组成:
    • 区块链上的智能合约
    • Web 前端用户界面

以太坊应用

  • 基于以太坊创建新的加密货币(CryptoCurrency,这种能力是 2017 年各种 ICO 泛滥的技术动因)。
  • 基于以太坊创建域名注册系统、博彩系统。
  • 基于以太坊开发去中心化的游戏,比如 2017 年底红极一时的以太猫(CryptoKitties,最高单只猫售价高达 80W 美元)。

代币(Token)

  • 代币(token)也称作通证,本意为 “令牌”,代表有所有权的资产、货币、权限等在区块链上的抽象。
  • 可替代性通证(fungible token):指的是基于区块链技术发行的,互相可以替代的,可以接近无限拆分的 token。
  • 非同质通证(non-fungible token): 指的是基于区块链技术发行的,唯一的,不可替代的,大多数情况下不可拆分的 token,如加密猫(CryptoKitties)。

名词解释

  • IP: Ethereum Improvement Proposals,以太坊改进建议。
  • ERC:Ethereum Request for Comments 的缩写,以太坊征求意见。一些 EIP 被标记为 ERC,表示试图定义以太坊使用的特定标准的提议。
  • EOA:External Owned Account,外部账户。由以太坊网络的人类用户创建的账户。
  • Ethash:以太坊 1.0 的工作量证明算法。
  • HD 钱包:使用分层确定性(HD protocol)密钥创建和转账协议(BIP32)的钱包。
  • Keccak256:以太坊中使用的密码哈希函数。Keccak256 被标准化为 SHA-3。
  • Nonce:在密码学中,术语 nonce 用于指代只能使用一次的值。以太坊使用两种类型的随机数,账户随机数和 POW 随机数。

以太坊客户端

什么是以太坊客户端

以太坊客户端是一个软件应用程序,它实现以太坊规范并通过 p2p 网络与其他以太坊客户端进行通信。如果不同的以太坊客户端符合参考规范和标准化通信协议,则可以进行相互操作。

以太坊是一个开源项目,由 “以太坊黄皮书” 正式规范定义。除了各种以太坊改进提案之外,此正式规范还定义了以太坊客户端的标准行为,因为有了明确的正式规范,以太网客户端有了许多独立开发的软件实现,它们之间又可以彼此交互。

基于以太坊规范的网络

存在各种基于以太坊规范的网络,这些网络基本符合 “以太坊黄皮书” 中定义的形式规范,但它们之间可能相互也可能不相互操作。

这些基于以太坊的网络中有:以太坊,以太坊经典,Ella,Expanse,Ubiq,Musicoin 等等。

虽然大多数在协议级别兼容,但这些网络通常具有特殊要求,以太坊客户端软件的维护人员、需要进行微小更改、以支持每个网络的功能或属性。

以太坊的多种客户端

  • go-ethereum (Go):
    官方推荐,开发使用最多, Geth 是由以太坊基金会积极开发的 Go 语言实现,因此被认为是以太坊客户端的 “官方” 实现。通常,每个基于以太坊的区块链都有自己的 Geth 实现
  • parity (Rust): 最轻便客户端,在历次以太坊网络攻击中表现卓越。
  • cpp-ethereum (C++)
  • pyethapp (python)
  • ethereumjs (javascript)
  • EthereumJ / Harmony (Java)

JSON-RPC

以太坊客户端提供了 API 和一组远程调用(RPC)命令,这些命令被编码为 JSON。这被称为 JSON-RPC API。本质上,JSON-RPC API 就是一个接口,允许编写的程序使用以太坊客户端作为网关,访问以太坊网络和链上数据。

通常,RPC 接口作为一个 HTTP 服务,端口设定为 8545。出于安全原因,默认情况下,它仅限于接受来自 localhost 的连接。

要访问 JSON-RPC API,可以使用编程语言编写的专用库,例如 JavaScript 的 web3.js,或者也可以手动构建 HTTP 请求并发送 / 接收 JSON 编码的请求。

以太坊全节点

全节点是整个主链的一个副本,存储并维护链上的所有数据,可以随时验证新区块的合法性,运行全节点将耗费巨大的成本,包括硬件资源和带宽。

区块链的健康和扩展弹性,取决于具有许多独立操作和地理上分散的全节点。每个全节点都可以帮助其他新节点获取区块数据,并提供所有交易和合约的独立验证。

以太坊开发不需要在实时网络(主网)上运行的全节点。可以使用测试网络的节点来代替,也可以用本地私链,或者使用服务商提供的基于云的以太坊客户端,这些几乎都可以执行所有操作。

远程客户端和轻节点

远程客户端:
不存储区块链的本地副本或验证块和交易。这些客户端一般只提供钱包的功能,可以创建和广播交易。远程客户端可用于连接到现有网络,MetaMask 就是一个这样的客户端。

轻节点:
不保存链上的区块历史数据,只保存区块链当前的状态。轻节点可以对块和交易进行验证。

全节点的优缺点

  • 优点
    • 为以太坊网络的灵活性和抗审查性提供有力支持。
    • 权威地验证所有交易。
    • 可以直接与公共区块链上的任何合约交互。
    • 可以离线查询区块链状态(帐户,合约等)。
    • 可以直接把自己的合约部署到公共区块链中。
  • 缺点
    • 需要巨大的硬件和带宽资源,而且会不断增长。
    • 第一次下载往往需要几天才能完全同步。
    • 必须及时维护、升级并保持在线状态以同步区块。。

公共测试网络节点的优缺点

  • 优点
    • 一个 testnet 节点需要同步和存储更少的数据,大约 10GB,具体取决于不同的网络
    • 一个 testnet 节点一般可以在几个小时内完全同步
    • 部署合约或进行交易只需要发送测试以太,可以从 “水龙头” 免费获得
    • 测试网络是公共区块链,有许多其他用户和合约运行(区别于私链)
  • 缺点
    • 测试网络上使用测试以太,它没有价值。因此,无法测试交易对手的安全性,因为没有任何利害关系
    • 测试网络上的测试无法涵盖所有的真实主网特性。例如,交易费用虽然是发送交易所必需的,但由于 gas 免费,因此 testnet 上往往不会考虑。而且一般来说,测试网络不会像主网那样经常拥堵

本地私链的优缺点

  • 优点
    • 磁盘上几乎没有数据,也不同步别的数据,是一个完全 “干净” 的环境
    • 无需获取测试以太,你可以任意分配以太,也可以随时自己挖矿获得
    • 没有其他用户,也没有其他合约,没有任何外部干扰
  • 缺点
    • 没有其他用户意味与公链的行为不同。发送的交易并不存在空间或交易顺序的竞争
    • 除自己之外没有矿工意味着挖矿更容易预测,因此无法测试公链上发生的某些情况
    • 没有其他合约,意味着你必须部署要测试的所有内容

用 Geth 搭建以太坊私链

一种是直接用源码安装,直接克隆 git 仓库,获取源代码的副本。

1
git clone https://github.com/ethereum/go-ethereum.git

另一种是到官网直接下载对应系统的安装程序。

查看geth version,确保在真正运行之前安装正常。

启动节点同步

安装好了 Geth,可以尝试运行一下它。执行下面的命令,geth 就会开始同步区块,并存储在当前目录下。这里的--syncmode fast参数表示会以 “快速” 模式同步区块。在这种模式下,只会下载每个区块头和区块体,但不会执行验证所有的交易,直到所有区块同步完毕再去获取一个系统当前的状态。这样就节省了很多交易验证的时间。

1
geth –datadir . --syncmode fast

如果想同步测试网络的区块,可以用下面的命令:

1
geth --testnet --datadir . --syncmode fast

--testnet这个参数会告诉 geth 启动并连接到最新的测试网络,测试网络的区块和交易数量会明显少于主网,所以会更快一点。但即使是用快速模式同步测试网络,也会需要几个小时的时间。

搭建自己的私链

因为公共网络的区块数量太多,同步耗时太长,为了方便快速了解 Geth,可以试着用它来搭一个只属于自己的私链。

首先,需要创建网络的 “创世”(genesis)状态,这写在一个小小的 JSON 文件里(例如,将其命名为 genesis.json):

1
2
3
4
5
6
7
8
9
10
11
{
"config": {
"chainId": 15
},
"difficulty": "2000",
"gasLimit": "2100000",
"alloc": {
"7df9a875a174b3bc565e6424a0050ebc1b2d1d82": { "balance": "300000" },
"f41c74c9ae680c1aa78f42e5647a62f353b7bdde": { "balance": "400000" }
}
}

要创建一条以它作为创世块的区块链,可以使用下面的命令:

1
geth --datadir path/to/custom/data/folder init genesis.json

在当前目录下运行 geth,就会启动这条私链,注意要将 networked 设置为与创世块配置里的 chainId 一致。

1
geth --datadir path/to/custom/data/folder --networkid 15

现在,节点正常启动,恭喜!已经成功启动了一条自己的私链。

Geth 控制台命令

Geth Console 是一个交互式的 JavaScript 执行环境,里面内置了一些用来操作以太坊的 JavaScript 对象,可以直接调用这些对象来获取区块链上的相关信息。这些对象主要包括:

  • eth:主要包含对区块链进行访问和交互相关的方法;

  • net:主要包含查看 p2p 网络状态的方法;

  • admin:主要包含与管理节点相关的方法;

  • miner:主要包含挖矿相关的一些方法;

  • personal:包含账户管理的方法;

  • txpool:包含查看交易内存池的方法;

  • web3:包含以上所有对象,还包含一些通用方法
    常用命令有:

  • personal.newAccount():创建账户;

  • personal.unlockAccount():解锁账户;

  • eth.accounts:列出系统中的账户;

  • eth.getBalance():查看账户余额,返回值的单位是 Wei;

  • eth.blockNumber:列出当前区块高度;

  • eth.getTransaction():获取交易信息;

  • eth.getBlock():获取区块信息;

  • miner.start():开始挖矿;

  • miner.stop():停止挖矿;

  • web3.fromWei():Wei 换算成以太币;

  • web3.toWei():以太币换算成 Wei;

  • txpool.status:交易池中的状态;

以太坊交易

以太币单位

  • 以太坊的货币单位称为以太,也称为 ETH 或符号 Ξ 。
  • ether 被细分为更小的单位,直到可能的最小单位,称为 wei;1 ether = 10^18 wei 。
  • 以太的值总是在以太坊内部表示为以 wei 表示的无符号整数值。
  • 以太的各种单位都有一个使用国际单位制(SI)的科学名称,和一个口语名称。

以太坊账户

私钥、公钥和地址

  • 私钥(Private Key):以太坊私钥是一个 256 位的随机数,用于发送以太的交易中创建签名来证明自己对资金的所有权

  • 公钥(Public Key):公钥是由私钥通过椭圆曲线加密 secp256k1 算法单向生成的 512 位 (64 字节)数

  • 地址(Address):地址是由公钥的 Keccak-256 单向哈希,取最后 20 个字节(160 位) 派生出来的标识符

以太坊账户类型

以太坊账户分为外部账户 (Externally owned account, EOA)和 合约账户 (Contract accounts)。

EOA

外部账户:普通用户用私钥控制的账户

  • 有对应的以太币余额
  • 可发送交易(转币或触发合约代码)
  • 由用户私钥控制
  • 没有关联代码

合约账户

一种拥有合约代码的账户,它不属于任何人,也没有私钥与之对应。

  • 有对应的以太币余额
  • 有关联代码
  • 由代码控制
  • 可通过交易或来自其它合约的调用消息来触发代码执行
  • 执行代码时可以操作自己的存储空间,也可以调用其它合约

以太坊钱包

以太坊钱包是我们进入以太坊系统的门户。它包含了私钥,可以代表我们创建和广播交易,常见得钱包有:

  • MetaMask:一个浏览器扩展钱包,可在浏览器中运行。下面例子中使用的是 MetaMask。

  • Jaxx:一款多平台、多币种的钱包,可在各种操作系统上运行,包括 Android,iOS,Windows,Mac 和 Linux。

  • MyEtherWallet(MEW):一个基于 web 的钱包,可以在任何浏览器中运行。

  • Emerald Wallet:旨在与 ETC 配合使用,但与其他基于以太坊的区块链兼容。

交易

以太坊中的交易是一个签名的数据包,由 EOA 发送到另一个账户,由以太坊网络传输,并被序列化后记录在以太坊区块链上,并且以太坊是一个全局单例状态机,交易是唯一可以触发状态更改或导致合约在 EVM 中执行的事物。

数据包里包含:

  • nonce:由发起人 EOA 发出的序列号,用于防止交易消息重播
  • gas price:交易发起人愿意支付的 gas 单价(wei)
  • start gas:交易发起人愿意支付的最大 gas 量
  • to:目的以太坊地址
  • value:要发送到目的地的以太数量
  • data:可变长度二进制数据负载(payload)
  • v,r,s:发起人 EOA 的 ECDSA 签名的三个组成部分

交易消息的结构使用递归长度前缀(RLP)编码方案进行序列化,该方案专为在以太坊中准确和字节完美的数据序列化而创建。

nonce

对于 EOA 账户,nonce 等于从这个账户地址发送的交易数。 对于合约账户,nonce 等于这个这个账户中创建的合约数。

nonce 值还用于防止错误计算账户余额。nonce 强制来自任何地址的交易按顺序处理,没有间隔,无论节点接收它们的顺序如何。使用 nonce 确保所有节点计算相同的余额和正确的序列交易,等同于用于防止比特币 “双重支付”(“重放攻击”)的机制。

gas

当交易或消息触发 EVM 运行时,每个指令都会在网络的每个节点上执行。对于每个执行的操作,都存在固定的成本,这个成本用一定量的 gas 表示。发起交易时,需要从执行代码的矿工那里用以太币购买 gas。

gas 与消耗的系统资源对应,这是具有自然成本的。因此在设计上 gas 和 ether 有意地解耦,消耗的 gas 数量代表了对资源的占用,而对应的交易费用则还跟 gas 对以太的单价有关。这两者是由自由市场调节的:gas 的价格实际上是由矿工决定的,他们可以拒绝处理 gas 价格低于最低限额的交易。我们不需要专门购买 gas ,只需将以太币添加到帐户即可,客户端在发送交易时会自动用以太币购买 gas。而以太币本身的价格通常由于市场力量而波动。

gas 的计算

在发起交易时发起人会先设置一个gas limit,代表消耗 gas 得上限,相当于押金。 实际支付的 gas 数量是执行过程中消耗的 gas (gasUsed),gas limit 中剩余的部分会返回给发送人。最终支付的 gas 费用是 gasUsed 对应的以太币费用,单价由设定的 gasPrice 而定。

最终支付费用 totalCost = gasPrice * gasUsed totalCost 会作为交易手续费(Tx fee)支付给矿工

交易的接收者:to

to字段是一个 20 字节的以太坊地址,可以是 EOA 也可以是合约地址。

在以太坊中,任何 20 字节的值都被认为是有效的,以太坊没有做验证。如果 20 字节值没有对应的地址或者是不存在的合约,交易也是有效的,但是以太坊会销毁发送的以太,使其永远无法访问。

交易的 value 和 data

valuedata是交易主要的“有效负载”,它们可以有 4 中组合。

  • 仅有 value: 表示一笔以太的付款。
  • 仅有 data: 一般表示合约调用。
  • 同时有 value 和 data: 进行合约调用,同时忘合约中发送以太。
  • 既没有 value 也没有 data: 只是在浪费 gas,但它是有效的。

特殊交易:创建(部署)合约

有一种特殊的交易,具有数据负载且没有 value,那就是一个创建新合约的交易。这个交易的to地址是一个特殊的地址,即零地址: 0x0。该地址既不代表 EOA 也不代表合约。它永远不会花费以太或发起交易,它仅用作目的地,具有特殊含义 “创建合约”。

虽然零地址仅用于合同注册,但它有时会收到来自各种地址的付款。 这种情况要么是偶然误操作,导致失去以太;要么是故意销毁以太。

以太坊虚拟机 (EVM)

以太坊虚拟机 (EVM) 简介

以太坊虚拟机( EVM )是智能合约的运行环境, 作为区块验证协议的一部分,参与网络的每个节点都会运行 EVM。他们会检查正在验证的块中列出的交易,并运行由 EVM 中的交易触发的代码。

EVM 不仅是沙盒封装的,而且是完全隔离的,也就是说在 EVM 中运行的代码是无法访问网络、文件系统和其他进程的, 甚至智能合约之间的访问也是受限的。

合约以字节码的格式(EVM bytecode)存在于区块链上, 合约通常以高级语言(solidity)编写,通过 EVM 编译器编译为字节码,最终通过客户端上载部署到区块链网络中。

EVM的工作原理

代码编译和部署

在以太坊上开发智能合约时,开发者使用Solidity等高级编程语言编写合约代码,并通过编译器将其转换为EVM能够理解和执行的字节码。

编译得到的字节码会被写入以太坊区块链,并分配一个唯一的合约地址。合约地址用于唯一标识合约在区块链上的存储位置。

交易执行

当一个合约交易被广播到以太坊网络时,EVM的节点会接收到该交易,并进行验证。验证通过后,EVM开始执行交易中的智能合约代码。

EVM执行环境

EVM提供了一个独立的执行环境,每个交易都在自己的环境中执行。EVM的执行环境由以下几个组成部分:

  • 栈(Stack)

    EVM 使用栈来存储和处理数据。栈是一种后进先出(LIFO)结构,用于执行指令时的操作数栈和返回值栈。

    存放部分局部值类型变量,几乎免费使用的内存,但有数量限制。

  • 内存(Memory)

    EVM 提供了一块可扩展的内存区域,用于临时存储和操作数据。智能合约可以通过读写内存来进行复杂的计算和数据处理。

    每一次消息调用,合约会临时获取一块干净的内存空间,生命周期仅为整个方法执行期间,函数调用后回收,因为仅保存临时变量,故读写 gas 开销较小。

  • 存储(Storage)
    EVM 提供了持久化的存储空间,用于永久保存合约的状态和数据。这是一个将 256 位字映射到 256 位字的 key-value 存储区,可以理解为合约的数据库,每个合约都有自己的存储空间。

    由于会永久保存合约状态变量,所以读写的 gas 开销也最大。

  • 指令集(Instruction Set)

    EVM 定义了一套指令集,用于执行智能合约的操作和功能。指令集包括基本的算术运算、逻辑运算、存储读写操作等,也可以做到有条件和无条件跳转。所有的指令都是针对 “256 位的字(word)“这个基本的数据类型来进行操作。

执行过程

EVM的执行过程可以简单概括为以下几个步骤:

解析交易:EVM首先解析交易数据,提取出合约地址、调用参数等信息。

创建执行环境:EVM为该交易创建一个独立的执行环境,包括栈、存储和内存空间。

执行合约代码:EVM按照合约代码的指令顺序逐条执行,通过操作码来执行各种操作,如计算、存储、跳转等。

栈操作:EVM使用栈来保存和处理数据。在执行过程中,可以将数据压入栈顶、从栈顶弹出数据,进行栈上计算操作。

存储操作:EVM提供了持久化的存储空间Storage,可以读取和写入合约的状态和数据。通过存储操作码,可以将数据存储到存储空间或从存储空间中读取数据。

内存操作:EVM的临时内存空间可用于临时存储数据。通过内存操作码,可以将数据加载到内存中或从内存中读取数据。

跳转操作:EVM支持跳转操作,可以根据条件或无条件地改变代码执行的流程,实现条件判断、循环等控制结构。

异常处理:在执行过程中,如果遇到错误或异常情况,EVM会停止执行并回滚状态,确保合约的执行不会导致不一致的状态。

结束执行:当合约代码执行完毕或遇到返回指令时,EVM将结束执行,并将执行结果返回给调用者。

编写智能合约

MetaMask 钱包

以太坊钱包是我们进入以太坊系统的门户。它包含了私钥,可以代表我们创建和广播交易。本文我们使用 MetaMask 钱包。 MetaMask 钱包可以在谷歌浏览器插件中安装。

添加测试地址

在以太坊主网上进行交易和执行智能合约需要支付 gas 费用,这些费用在开发和测试阶段可能会累积成相当大的开销。 因此,在开发和测试阶段一般都是使用太坊测试网络。这些测试网络通常提供免费的测试以太币。

这里有一些以太坊测试地址:https://talk.comunion.org/d/650-comunion-rpc-erc-20

本文选择 Avalanch 测试链:

Avalanch Avalanch - Avalanch Testent
网络名称:Avalanche Fuji Testnet
新增 RPC URL:https://api.avax-test.network/ext/bc/C/rpc
Chain ID:43113
货币符号:AVAX
区块链浏览器 URL:https://cchain.explorer.avax-test.network
水龙头地址:https://faucet.avax.network/

领取测试币

进入测试网络的水龙头地址https://faucet.avax.network/中,连接 MetaMask

连接到 MetaMask 后,地址栏中会显示钱包的地址,点击Request 2 AVAX,等待交易完成,钱包地址中会有 2 AVAX 代币。

MateMask 中添加测试网络。

点击下面的将子网添加到钱包,并将钱包切换到测试网络。

完成后,可以在钱包查看测试网中的账户信息

在 Remix 上构建简单的水龙头合约

Remix 是一个在线用于构建和部署以太坊智能合约的开发工具和平台。它提供了一个用户友好的界面和功能强大的集成开发环境(IDE),帮助开发者更轻松地编写、测试和部署智能合约。

Remix 集成了 Solidity 编译器,可以将 Solidity 合约代码编译成 EVM(以太坊虚拟机)字节码。开发者可以使用 Remix 提供的编译器生成合约的字节码和 ABI(应用二进制接口),以便在以太坊上部署和调用合约。

Remix 的地址:https://remix.ethereum.org

编写水龙头合约

水龙头是一件相对简单的事情:它会向任何要求的地址发出以太。

contracts目录下,新建一个Faucet.sol文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Version of Solidity compiler this program was written for
pragma solidity ^0.4.19;
// Our first contract is a faucet!
contract Faucet {
// Give out ether to anyone who asks
function withdraw(uint withdraw_amount) public {
// Limit withdrawal amount
require(withdraw_amount <= 100000000000000000);
// Send the amount to the address that requested it
msg.sender.transfer(withdraw_amount);
}
// Accept any incoming amount
function () public payable {}
}

contract Faucet { : 该行声明了一个合约对象,类似于其他面向对象语言中的类声明

function withdraw(uint withdraw_amount) public {: 该函数名为 withdraw,它接受一个名为 withdraw_amount 的无符号整数(uint)参数。它被声明为公共函数,这意味着它可以被其他合约调用

require(withdraw_amount <= 100000000000000000);: 该行设定提款限额。

使用内置的 Solidity 函数 require 来测试一个前提条件,即 withdraw_amount 小于或等于 100000000000000000 wei,这是 ether 的 基本单位,相当于 0.1 ether。

如果使用大于该数量的 withdraw_amount 调用 withdraw 函数,则此处的 require 函数将导致合约执行停止并因异常而失败

msg.sender.transfer(withdraw_amount);: 该行是实际提现。

  • msg 对象:所有合约都可以访问的输入之一,它表示触发此合约执行的交易
  • sender 属性:交易的发件人地址
  • transfer 函数:是一个内置函数,它将以太从合约传递到调用它的地址。向后读,这意味着将以太转移到触发此合约执行的 msg 的发送者
  • transfer 函数将金额作为其唯一参数。我们将 withdraw_amount 值作为参数传递给上面几行声明的 withdraw 函数

function () public payable {}: 此函数是所谓的 “回退” 或默认函数,如果触发合约的交易未命名合约中的任何已声明函数或任何函数或未包含数据,则调用此函数

编译水龙头合约

现在我们有了第一个示例合约,需要使用 Solidity 编译器将 Solidity 代码转换为 EVM 字节码,以便它可以由 EVM 执行。

在编译页面,首先要选择与代码中pragma solidity ^0.4.19相同的 solidity 版本。然后点击Compile Faucet.sol编译程序。

在区块链上创建合约

我们写了合约并把它编译成字节码。现在,我们需要在以太坊区块链上 “注册” 合约。我们将使用测试网来测试我们的合约。

在区块链上注册合约涉及创建一个特殊交易,其目的地是一个 “零地址”,也就是地址为:0x0000000000000000000000000000000000000000。零地址是一个特殊地址,告诉以太坊区块链我们想要注册合约。不过我们不需要手动输入这么多个 0,Remix IDE 将为我们处理所有这些,并将交易发送到 MetaMask,由钱包账户确认提交。

将运行环境关联到 MetaMask 上的测试链,然后点击 Deploy部署合约

部署完成后会生成一个合约地址。同时包含了一些合约的其他信息,像合约地址的余额:balance,调用方法名withdraw等。

与合约交互

以太坊合约是控制资金的程序,它在称为 EVM 的虚拟机内运行。它们由特殊交易创建,该交易提交其字节码以记录在区块链上。一旦他们在区块链上创建,他们就有了一个以太坊地址,就像钱包一样。只要有人将某个交易发送到合约地址,就会导致合约在 EVM 中运行,并将该合约作为其输入。

发送到合约地址的交易可能包含 ether 或数据或两者。如果它们含有 ether,则将其 “存入” 合约余额。如果它们包含数据,则数据可以在合约中指定命名函数并调用它,将参数传递给函数

资助合约

目前,合约在其历史记录中只有一个交易:合约创建交易。

合约也还没有以太(零余额)。那是因为我们没有在创建交易中向合约发送任何以太。

我们可以给合约发一些以太,打开 MetaMask,给合约的地址发送以太,就像发送给其他任何以太坊地址一样。

当交易完成后,再看合约的余额。

调用合约

接下来,让我们从水龙头中提取一些资金。要提现,我们必须构造一个调用 withdraw 函数的交易,并将 withdraw_amount 参数传递给它。为了使事情变得简单,Remix 将为我们构建该交易,并由MetaMask发送交易。

我们输入要体现的金额,然后点击withdraw,表示调用withdraw方法。

该交易导致合约在 EVM 内部运行,当 EVM 运行水龙头合约的提现功能时,首先它调用 require 函数并验证我们的金额小于或等于允许的最大提现 0.1 以太;然后它调用 transfer 函数向我们发送以太,运行转账功能会产生一个内部交易,从合约的余额中withdraw_amount的以太币存入我们的钱包地址;

当交易完成后,可以看到合约中的余额减少了。并且钱包中会多一条交易记录

我们可以点击在区块浏览器上查看,查看这条交易的详细信息。

迟到