二、以太坊介绍
首先我们要知道我们为什么要学习以太坊,主要有以下四个原因:
- 以太坊是区块链2.0的代表,学习以太坊能了解到区块链技术的所有知识
- 引入了智能合约,拓宽了区块链的应用场景
- 对开发者友好、对用户友好,容易编写出简单的区块链应用,学习趣味性高
- Solidity 语法与 Javascript、Go 等语言接近,易上手
2.1、以太坊简介
区块链技术常常被认为是自互联网诞生以来最具颠覆性的技术,然而,自比特币诞生后一直没有很好的区块链应用开发平台。想要在比特币基础上开发区块链应用是非常复杂繁琐的,因为比特币仅仅是一个加密数字货币系统,无法用来实现更广阔的业务需求。以太坊是目前使用最广泛的支持完备应用开发的共有区块链系统。
和比特币不同,比特币只适合加密数字货币场景,不具备图灵完备性,也缺乏保存实时状态的账户概念,以及存在 PoW 机制带来的效率和资源浪费的问题,而以太坊作为区块链2.0的代表,目标是扩展智能合约和建立一个去中心化应用平台,具有图灵完备的特性、更高效的共识机制、支持智能合约等多种应用场景,使得开发者能够很方便地在以太坊上开发出基于区块链的应用。
2.1.1、以太坊的发展
2014年, Vitalik Buterin 发表了文章《以太坊:一个下一代智能合约和去中心化应用平台》。同年,Buterin 在迈阿密比特币会议中宣布启动以太坊项目,并提出了多项创新性的区块链技术。2015年,以太坊CCO Stephan Tual 在官方博客上宣布以太坊系统诞生,主网上线。
以太坊发展至今经历了“前沿”(Frontier)、“家园”(Homestead)以及现在所处的“大都会”(Metropolis)三个阶段。第四阶段“宁静”(Serenity)将作为以太坊的最后一个阶段,目前尚未有计划发布日期。
2.1.2、以太坊的特点
以太坊团队和外界对以太坊的描述都是“世界计算机”,这代表它是一个开源的、全球的去中心化计算架构。它执行称为智能合约的程序,并使用区块链来同步和存储系统状态,以及使用名为以太币的加密数字货币来计量和约束执行操作的资源成本。同时,以太坊提供了一系列的接口,使得开发者能够通过以太坊来开发去中心化 Web 应用DApps。
2.1.3、智能合约
相比比特币,以太坊最大的特点就是引入了智能合约。智能合约本质上就是一段编写好的程序,可以在特定的条件下被触发并执行特定的操作。由于区块链具有不可逆和不可篡改的特点,因此智能合约与区块链结合后,就成了一份“强制执行”的合约。
以太坊能够作为一个去中心化应用平台和”世界计算机”,其核心就是智能合约。智能合约的引入,使得开发者能够实现许多(理论上是任何)业务逻辑。如果说比特币是通过区块链技术开发的特定计算器,那么引入了智能合约的以太坊就是基于区块链技术的通用计算机。可以简单的理解成:比特币的交易系统就是一份写死的智能合约,而以太坊则将智能合约的开发权限交给开发者。
以太坊提供了对智能合约的全面支持,包括编写智能合约编程语言 Solidity 和运行智能合约的以太坊虚拟机(Ethereum Virtual Machine,EVM)。
2.1.4、幽灵协议
幽灵合约的英文是“Greedy Heaviest Observed Subtree" (GHOST) protocol,在介绍幽灵协议之前,先介绍以太坊中的叔区块、叔块奖励和叔块引用奖励这三个概念。
假设目前以太坊区块链中的区块高度(区块链上的区块个数)为6,现在产生了一笔新的交易,矿工A先将该笔交易打包成了区块 Block 7,在矿工A将 Block 7 广播到其他节点的这段时间里,矿工B和矿工C又分别产生了 Block 8 和 Block 9。Block 7、Block 8、Block 9 都指向 Block 6,即 Block 6 是他们的父区块。由于 Block 7 是最先产生的,因此 Block 7 被认为是有效区块,Block 8 和 Block 9 就是叔区块(作废区块)。
现在链上的区块高度为7,在这基础上又产生了新的交易,并被打包成了 Block 10。在以太坊中,Block 10 除了可以引用它的父区块 Block 7 外,还可以引用叔区块 Block 8 和 Block 9。并且,Block 8 和 Block 9 的矿工会因此获得一笔奖励,称为叔块奖励,Block 10 的矿工除了基础奖励之外,由于引用了叔区块,还会获得一笔额外的叔块引用奖励。
幽灵协议是以太坊的一大创新。由于在比特币中的出块时间被设计为10分钟,而以太坊为了提高出块速度,将出块时间设计为12秒(实际14~15秒左右),这样的高速出块意味着高速确认,高速确认会带来区块的高作废率和低安全性。因为区块需要花一定的时间才能广播至全网,如果矿工 A 挖出了一个区块,而矿工 B 碰巧在 A 的区块扩散至 B 之前挖出了另一个区块,矿工 B 的区块就会作废并且没有对区块链的网络安全做出贡献。此外,这样的高速确认还会带来中心化的问题:如果 A 拥有全网 30% 的算力而 B 拥有 10% 的算力,那么 A 将会在 70% 的时间内都在产生作废区块,而 B 在 90% 的时间内都在产生作废区块,这样,B 永远追不上 A,后果是 A 通过其算力份额拥有对挖矿过程实际上的控制权,出现了算力垄断,弱化了去中心化。
幽灵协议正是为了解决上述问题而引入的,协议的主要内容如下:
-
计算最长链时,不仅包括当前区块的父区块和祖区块,还包括祖先块的作废的后代区块(叔区块),将它们综合考虑来计算哪一个区块拥有支持其的最大工作量证明。这解决了网络安全性的问题 -
以太坊付给以“叔区块”身份为新块确认作出贡献的废区块87.5%的奖励(叔块奖励),把它们纳入计算的“侄子区块”将获得奖励的12.5%(叔块引用奖励)。这就使得即使产生作废区块的矿工也能够参与区块链网络贡献并获得奖励,解决了中心化倾向的问题 -
叔区块最深可以被其父母的第二代至第七代后辈区块引用。这样做是为了:
2.1.5、以太坊的组成部分
在以太坊中,包括了 P2P 网络、共识机制、交易、状态机、客户端这几个组成部分。
-
P2P 网络:在以太坊主网上运行,可通过TCP端口30303访问,并运行称为 DΞVp2p 的协议。 -
共识机制:以太坊目前使用名为 Ethash 的 POW 算法,计划在将来会过渡到称为 Casper 的 POS 算法。 -
交易:以太坊中的交易本质上是网络消息,包括发送者、接收者、值和数据载荷(payload)。 -
状态机:以太坊的状态转移由以太坊虚拟机(Ethereum Virtual Machine,EVM)处理,EVM 能够将智能合约编译成机器码并执行。 -
客户端:用于用户和以太坊进行交互操作的软件实现,最突出的是 Go-Ethereum(Geth) 和 Parity。
2.1.6、以太坊中的概念
- 账户:以太坊中的账户类似于银行账户、应用账户,每个账户有一个20字节的地址。账户又分为普通账户(又叫外部账户,External OwnedAccount,EOA)和合约账户(Contract)。普通账户是由以太坊使用者创建的账户,包含地址、余额和随机数;合约账户是创建智能合约时建立的账户,包含存储空间和合约代码
- 状态:状态是由账户和两个账户之间价值的转移以及信息的状态转换构成的
- 地址:地址是一个账户 ECDSA 公钥的 Keccak 散列最右边的160位,通过地址可以在以太坊上接收或发送交易。在 Etherscan
上,可以通过地址来查询一个账户的信息 - 交易:以太坊中的交易不仅包括发送和接收以太币,还包括向合约账户发送交易来调用合约代码、向空用户发送交易来生成以交易信息为代码块的合约账户
- Gas:Gas是以太坊中的一种机制,用于执行智能合约或交易操作的虚拟燃料。由于以太坊是图灵完备的,为了避免开发者无意或恶意编写出死循环等浪费资源或滥用资源的情况,以太坊中的每一笔交易都需支付一定的 Gas (燃料费),即需支付一定的以太币作为 Gas。Gas 的金额通常是由交易的发起者指定并支付的
- 挖矿:和比特币类似,以太坊同样通过挖矿来产生区块。在以太坊目前的 PoW机制下,每当一笔交易发出并广播,就会吸引矿工来将该交易打包成区块。每产生一个区块都会有一笔固定奖励给矿工,目前的固定奖励是3个以太。同时,区块中所有操作所需的 Gas 也会作为奖励给矿工。与比特币不同的是,以太坊中产生叔块的矿工可能会获得叔块奖励,引用叔块的矿工会获得叔块引用奖励
- DApp(去中心化应用):通过智能合约,开发者能够设计想要的逻辑,相当于是网站的后端。而 DApp则相当于是一个完整的网站(前端+后端),因此 DApp = 智能合约 + Web 前端。以太坊提供了一个名为 web3.js 的Javascript 库,通过 web3.js 可以实现 Web 与以太坊区块链的交互和与智能合约的交互,方便开发者创建 DApp
2.2、以太坊基础
2.2.1、以太坊中的货币
以太坊中的货币称为 以太币,单位为以太(Ether),也称 ETH 或符号 Ξ。以太可以被分割为更小的单位,最小的单位是 wei,1 以太 = 10^18 wei。以太币各单位的名称及之间的关系如下表:
2.2.2、以太坊钱包
以太坊钱包是用于创建和广播交易的应用程序,常用的钱包有
-
MetaMask,一款基于浏览器扩展的钱包,可以很方便地添加到 Chrome, FireFox 等支持扩展的浏览器中 -
Jaxx,一款跨平台、多币种的钱包 -
MyEtherWallet(MEW),一款基于 Web 的钱包,可以在任何浏览器中运行 Emerald -
Wallet,一款被设计来用于以太坊经典区块链的钱包,但也与其他以太坊区块链兼容
MetaMask 基础
以 Chrome 为例,访问 Google 网上应用商店,搜索 MetaMask 并添加至 Chrome
添加完成后 Chrome 会自动打开初始化页面
初次使用创建钱包
为钱包设置密码
创建密码后,MetaMask 会生成一串密语,密语是12个随机的英文单词,用于防止密码忘记。密语可以直接当成密码使用,因此需要妥善保管
注册完毕后就可以在 Chrome 地址栏右边的扩展程序栏点击 🦊 图标使用 MetaMask 了
获取测试以太
除了以太坊主网以外,以太坊还提供了 Ropsten, Kovan, Rinkeby, Goerli 这几个公共测试网络,另外还支持局域网测试网络和自建测试网络。在这里我们切换到 Ropsten 测试网络
随后点击 Buy 按钮,点击测试水管下方的获取以太
在打开的页面中点击 request 1 ether from faucet 就可以得到1个测试以太,当然,可以多次点击。
测试以太仅供测试使用,除此之外没有任何价值,测试完毕后剩下的以太可以发送到水龙头账户捐赠给水龙头,以供他人测试使用。
2.3、以太坊交易的数据结构
在以太坊网络中,交易执行属于一个事务。具有原子性、一致性、隔离性、持久性特点。
-
原子性: 是不可分割的最小执行单位,要么做,要么不做。 -
一致性: 同一笔交易执行,必然是将以太坊账本从一个一致性状态变到另一个一致性状态。 -
隔离性: 交易执行途中不会受其他交易干扰。 -
持久性: 一旦交易提交,则对以太坊账本的改变是永久性的。后续的操作不会对其有任何影响。
以太坊交易的本质是由外部拥有的账户发起的签名消息,由以太坊网络传输,并被序列化后记录在以太坊区块链上,交易是唯一可以触发状态更改或导致合约在EVM中执行的事物
2.3.1、交易的数据结构
以太坊的数据结构主要可以分为四部分:nonce、gas、交易目标和消息(主要部分)、交易签名
开头是一个 uint64 类型的数字,称之为随机数。用于撤销交易、防止双花和修改以太坊账户的 Nonce 值。
第二部分是关于交易执行限制的设置,gas 为愿意供以太坊虚拟机运行的燃料上限。 gasPrice 是愿意支付的燃料单价。gasPrcie * gas 则为愿意为这笔交易支付的最高手续费。
第三部分是交易发送者输入以太坊虚拟机执行此交易的初始信息: 虚拟机操作对象(接收方 To)、从交易发送方转移到操作对象的资产(Value),以及虚拟机运行时入参(input)。其中 To 为空时,意味着虚拟机无可操作对象,此时虚拟机将利用 input 内容部署一个新合约。
第四部分是交易发送方对交易的签名结果,可以利用交易内容和签名结果反向推导出签名者,即交易发送方地址。以上总结如下:
nonce:由发起人EOA发出的序列号,用于防止交易消息重播。 gas price:交易发起人愿意支付的gas单价(wei)。 start gas:交易发起人愿意支付的最大gas量。 to:目的以太坊地址。 value:要发送到目的地的以太数量。 data:可变长度二进制数据负载(payload)。 v,r,s:发起人EOA的ECDSA签名的三个组成部分。
交易消息的结构使用递归长度前缀(RLP)编码方案进行序列化,该方案专为在以太坊中准确和字节完美的数据序列化而创建。
2.3.2、交易中的nonce
按以太坊黄皮书的定义, nonce是一个标量值,它等于从这个地址发送的交易数,或者对于关联code的帐户来说,是这个帐户创建合约的数量。因此nonce便有以下特征:
nonce不会明确存储为区块链中帐户状态的一部分。相反,它是通过计算发送地址的已确认交易的数量来动态计算的。 nonce值还用于防止错误计算账户余额。nonce强制来自任何地址的交易按顺序处理,没有间隔,无论节点接收它们的顺序如何。 使用nonce确保所有节点计算相同的余额和正确的序列交易,等同于用于防止比特币“双重支付”(“重放攻击”)的机制。但是,由于以太坊跟踪账户余额并且不单独跟踪 UTXO ,因此只有在错误地计算账户余额时才会发生“双重支付”。nonce机制可以防止这种情况发生。
2.3.3、并发和nonce
以太坊是一个允许操作(节点,客户端,DApps)并发的系统,但强制执行单例状态。例如,出块的时候只有一个系统状态。假如我们有多个独立的钱包应用或客户端,比如 MetaMask 和 Geth,它们可以使用相同的地址生成交易。如果我们希望它们都够同时发送交易,该怎么设置交易的nonce呢?一般有以下两种做法:
- 用一台服务器为各个应用分配nonce,先来先服务——可能出现单点故障,并且失败的交易会将后续交易阻塞。
- 生成交易后不分配nonce,也不签名,而是把它放入一个队列等待。另起一个节点跟踪nonce并签名交易。同样会有单点故障的可能,而且跟踪nonce和签名的节点是无法实现真正并发的。
2.3.4、交易中的gas
Gas 中译是:瓦斯、汽油,代表一种可燃气体。 这形象地比喻以太坊的交易手续费计算模式,不同于比特币中直接支付比特币作为转账手续费, 以太坊视为一个去中心化的计算网络,当你发送Token、执行合约、转移以太币或者在此区块上干其他的时候,计算机在处理这笔交易时需要进行计算消耗网络资源,这样你必须支付燃油费购买燃料才能让计算机为你工作。最终燃料费作为手续费支付给矿工。
注:可以在Etherscan上查询gas price与confirmation time的关系,如下图
因为手续费等于gasPrice * gasUsed,用户在转账,特别是执行智能合约时 gasUsed 无法提前预知。 这样存在一个风险,当用户的交易涉及一个恶意的智能合约,该合约执行将消耗无限的燃料, 这样会导致交易方的余额全部消耗(恶意的智能合约有可能是程序Bug,如合约执行陷入一个死循环)。
为了避免合约中的错误引起不可预计的燃料消耗,用户需要在发送交易时设定允许消耗的燃料上限,即 gasLimit。 这样不管合约是否良好,最坏情况也只是消耗 gasLimit 量的燃料。
然而,一笔交易所必须支付的燃料已经在区块中通过该交易已执行的计算量记录。 如果你不想支出太多燃料,而故意设置过低的 gasLimit 是没太多帮助的。 你必须支付足够燃料来支付本交易所必要的计算资源。如果交易尚未执行完成,而燃料已用完, 将出现一个 Out of Gas 的错误。特别注意的是,即使交易失败,你也必须为已占用的计算资源所支付手续费。 比如,你通过合约给 TFBOYS 投票,设置 gasPrice=2 gwei,gasLimit=40000(实现投票需要40001的燃料开销), 最终你投票失败且仍然需要支付 40000*2 gwei= 80000 gwei= 0.00008 ETH。
另外,如果最终 gasUsed 低于 gasLimit,即燃料未用完。则剩余燃料(gasLimit - gasUsed )将在交易后退还给你。 比如你发送 1 Ether 到另一个账户B,设置 gas limit 为 400000,将有 400000 - 21000 返回给你。
注意:21000 是标准转账交易的gasUsed。因此一笔标准的转账交易你可以设置 gasLimit 为21000
2.4、以太坊账户
对比比特币的UTXO余额模型,以太坊使用“账户”余额模型。 以太坊丰富了账户内容,除余额外还能自定义存放任意多数据。 并利用账户数据的可维护性,构建智能合约账户。下面我们首先将比特币的UTXO余额模型与以太坊账户进行比较,说明其各自的优缺点以及适用性。
2.4.1、比特币UTXO和以太坊账户结构比较
在当前的区块链项目中,主要有两种记录保存方式,一种是账户/余额模型,一种是UTXO模型。比特币采用就是UTXO模型,以太坊、EOS等则采用的是账户/余额模型。
2.4.2、比特币UTXO
UTXO是 Unspent Transaction Output的缩写,意思是未花费的输出,可以简单理解为还没有用掉的收款。比如韩梅梅收到一笔比特币,她没有用掉,这笔比特币对她来说就是一个UTXO。
UTXO 核心设计思路是:它记录交易事件,而不记录最终状态。要计算某个用户有多少比特币,就要对其钱包里所有的UTXO求和,得到结果就是他的持币数量。UTXO模型在转账交易时,是以UTXO为单位的,也就是说在支付时,调用的是整数倍UTXO,比如1个UTXO,3个UTXO,没有0.5个UTXO的说法。
2.4.3、以太坊账户
为什么以太坊不用UTXO呢?显然是因为麻烦,以太坊的做法更符合直觉,以太坊中的状态就是系统中所有账户的列表,每个账户都包含了一个余额和以太坊特殊定义的数据(代码和内部存储)。如果发送账户有足够多的余额来进行支付,则交易有效,在这种情况下发送账户先扣款,而收款账户将记入这笔收入。如果接受账户有相关代码,则代码会自动运行,并且它的内部存储也可能被更改,或者代码还可能向其他账户发送额外的消息,这就会导致进一步的借贷资金关系。
2.4.4、优缺点比较
比特币UTXO的优点:
- 更高程度的隐私:如果用户为他们收到的每笔交易使用新地址,那么通常很难将账户互相链接。这很大程度上适用于货币,但不太适用于任何dapps,因为dapps通常涉及跟踪和用户绑定的复杂状态,可能不存在像货币那样简单的用户状态划分方案
- 潜在的可扩展性:UTXO在理论上更符合可扩展性要求,因为我们只需要依赖拥有UTXO的那些人去维护基于Merkle树的所有权证明就够了,即使包括所有者在内的每个人都决定忘记该数据,那么也只有所有者受到对应的UTXO的损失,不影响接下来的交易。而在账户模式中,如果每个人都丢失了与账户相对应的Merkle树的部分,那将会使得和该账户有关的消息完全无法处理,包括发币给它。
以太坊账户模式的优点:
- 可以节省大量空间:不将UTXOs分开存储,而是合成一个账户;每个交易只需要一个输入、一个签名并产生一个输出
- 更好的可替代性:货币本质上都是同质化、可替代的;UTXO的设计使得货币从来源分成了“可花费”和“不可花费”两类,这在实际应用中很难有对应模型
- 更加简单:更容易编码和理解,特别是设计复杂脚本的时候,UTXO的脚本逻辑复杂时更令人费解
- 便于维护持久轻节点:只要沿着特定方向扫描状态树,轻节点
可以很容易地随时访问账户相关的所有数据。而UTXO地每个交易都会使得状态引用发生改变,这对应节点来说长时间运行Dapp会有很大压力
2.4.5、总结
| BitCoin | Ethereum |
---|
设计定位 | 现金系统 | 去中心化应用平台 | 数据组成 | 交易列表(账本) | 交易和账户状态 | 交易对象 | UTXO | Accounts | 代码控制 | 脚本 | 智能合约 |
2.5、以太坊账户类型
以太坊作为智能合约操作平台,将账户划分为两类:外部账户(EOAs)和合约账户(contract account),下面分别做简要介绍:
2.5.1、外部账户(EOA) 外部账户是由人来控制的,也就是常规理解的普通账户,外部账户包含以太币余额,主要作用就是发送交易(是广义的交易,包括转币和触发合约代码),是由用户私钥控制的,没有关联代码,所有在以太坊上交易的发起者都是外部账户。
外部账户特点总结:
- 拥有以太余额。
- 能发送交易,包括转账和执行合约代码。
- 被私钥控制。
- 没有相关的可执行代码。
2.5.2、合约账户(CA)
合约账户有时也叫内部账户,有对应的以太币余额和关联代码,它是由代码控制的,可以通过交易或来自其他合约的调用消息来触发代码执行,执行代码时可以操作自己的存储空间,也可以调用其他合约
合约账户特点总结:
|