2023-10-05
Web3
00
请注意,本文编写于 246 天前,最后修改于 109 天前,其中某些信息可能已经过时。

目录

Eth账户
ETH中的数据结构
状态树
交易树和收据树
共识机制
挖矿算法
权益证明
智能合约
The DAO

比特币被称为区块链1.0,以太坊被称为区块链2.0

以太坊针对BTC出现的问题进行了改进

-比特币以太坊
出块时间10min约15秒
共识机制工作量证明GHOST协议
挖矿算法mining puzzlememory hard
将来会使用权益证明代替工作量证明

以太坊还增加了对智能合约的支持。

BTC实现了去中心化的货币, 以太坊的还实现了去中心化的合约支持

智能合约的应用场景, 如果合同的参与方来自世界各地,没有一个统一的司法管辖权,如果我们通过实现写好的程序代码,每个人都只能按照这个规则来执行,这是一种比较好的解决方案,即使合同参与方在一个司法管辖权,合同的维护也费时费力,打官司花好多时间和精力,也不一定赢。智能合约的好处是一旦合约发布到区块链,谁都不能改,只能按照智能合约的规则来做事。

Eth账户

BTC系统没有账户的概念,转账需要提供前一笔交易的来源,有多少钱需要你提供私钥去UTFO里面查询合计。交易没有没有找零机制,这种方式的好处是隐私保护好,缺点是不符合常规,以太坊有账户的概念,可以显示账户余额。

有账户的好处是天然防御double speeding attach,缺点是replay attach。 举例来说 A转给B 10ETH 过了几十秒数据写入区块链,B又在网络中广播一次,这样B就能获得20ECH。以太坊在交易的时候增加交易次数nonce,转账时交易次数要成为交易内容的一部分,受交易者的签名保护。

ETH系统中有两种账户

  1. 外部账户 由公私钥产生的
  2. 合约账户 同样有nonce,一个合约账户可以调用另一个合约账户, 但是合约账户不能主动发起交易,只能通过外部账户发起交易

为什么以太坊不使用BTC得基于交易的账户模型?
因为ETH要支持合约账户,合约的地址不能改变。

ETH中的数据结构

  • 状态树
  • 交易树
  • 收据树

状态树

ETH要维护 账户地址 addrstate 之间的映射。要使用什么数据结构呢?

直观的方式是 hash表 ,如果不考虑hash碰撞,插入和修改都很方面。

但是要保证数据不可篡改,就需要使BTC状态的二叉树结构和hash签名 即 Merkle Tree, 这样如果一个账户的改动代价非常大。Merkle Tree没有提供查找和插入的快捷方法。

因为每次交易只是更改少量用户状态,所以更新的局部性很重要

以太坊使用的是一种MPT结构。它是一种树结构, 我们先看一下Trie Tree的结构,比如有如下几个单词排列成Trie Tree结构

这种结构的好处是

  1. 只要账户地址不同 就不会hash碰撞
  2. 新增节点修改节点改动小
  3. 无论元素怎么排列树的结构不变

但缺点是链越长 查找内存的次数就越多,效率越低

需要对路径进行压缩 Patricia Tree

这种结构在路径key值稀疏的情况下路径压缩效果才好。以太坊中的地址是 21602^{160} 这是一个非常大的数字,之所以要设计这么大的地址,是因为账户是本地产生的,这样可以有效的解决hash碰撞问题

MPTMerkle Patricia Tree的缩写, Merkle即把普通指针换成了hash指针,前一节说过Merkle Tree的特点是只要跟节点Merkle Tree Hash不变,任何节点都不能篡改。

以太坊中用的的是修改的MPTModified Merkle Patricia Tree

看一个ETF状态树的例子(注意,为了简化模式地址使用7位表示) image.png

每次发布一个区块时,这些树的有一些节点会发生变化,这些改变不是在原地改的,而是新建分支,原来的状态其实是保存下来的

下图是两个相邻区块有个账户状态发生变动后的MPT image.png

上图可以看出这是一个合约账户(因为有代码),交易次数发生变化,余额发生变化,其他都没有变化。

系统中每个全节点需要维护的不是一个MPT 而是每次出现一个区块都去新建一个MPT,但这个MPT绝大部分节点都是复用的,只有少数有改动的节点是新建的。

为什么保留历史状态,而不是原地修改?
因为ETC系统每隔10几秒就产生一个新区块,由于分布式网络的延迟,很容易出现分叉的情况,有了历史状态就可以快速回滚,沿着主线去挖矿。

可以根据交易信息回滚啊?
以太坊中还有合约账户,合约账户是可以编程的,是图灵完备的语言,没办法根据结果逆向推到出初始状态。

状态树保存的是 (key,value) 键值对, 账户的状态是经过序列化存储到状态树当中的。序列化使用的是RLP Recursive Length Prefix,它的特点是极简主义,只支持一种数据结构nested array of bytes,难的事情都交给应用层去做

交易树和收据树

交易树和收据树也是使用MPT结构, 状态树的key是账户地址,交易树和收据树的key是发布区块中的序号, 这个序号是发布区块的那个节点决定的。

交易树和收据树有什么用呢? 一个用途是提供证明某个交易打包到某个区块里, 收据树证明某个收据的执行结果打包到某个区块里

以太坊中引入 bloom filter 数据结构提供复杂查询的操作,它包含在块头里。 如查询过去十天与智能合约相关的操作。 bloom filter 可以证明没有就是没有, 有未必真有,通过bloom filter可以更快速过滤绝大部分没有的区块,再从剩下的每个区块,再次使用bloom filter过滤可能的交易或收据。

以太坊可以看作是交易驱动的状态机, 状态转移必须是确定的,因为所有的全节点都需要同步状态,要保持状态一致

转账的地址可以不存在吗?
这个是可能的,以太坊和比特币都一样创建账户的时候是不知道的,只有交易后其他节点才会这道这个账户的存在。

状态树与交易树、收据树的区别是,状态树保存全部的状态,为什么状态树不能像交易树、收据树一样只保存与当前交易有关用户的状态呢?这样可以大幅度削减每隔节点状态树的大小。
这是不可以的,因为查找一个用户状态将变得非常复杂,极端情况你可能要找到创世纪块中,才发现这个账户不存在

共识机制

以太坊的出块事件是BTC的四十倍约15s出块,对于以太坊来说15s的出块造成分叉是常态,因为别的节点还没有接收到你的消息继续在挖当前区块,等到收到消息时可能已经挖出了区块,那么这对共识机制有什么挑战呢?

BTC的方案时作废,毕竟10分钟使得分叉很少,如果以太坊也按照这个方案,假设两个小的个体矿工和一个大矿主同时挖到一个区块,一方面大矿主的矿机会更靠近以太网网络中心,另一方面其他矿工会站队大矿主,因为大矿主更要可能挖出下一个区块,对其他矿工来说站队大矿主收益最大,这是很严重的不成正比的收益,如果整个挖矿都集中到几个大矿主手里,不利于以太网的问题。而BTC系统使用的是p2p Overlay Network所有网络节点都是平等的,不存在网络上的优势。

所以以太坊引入叔父节点的概念,侄子招安叔父,侄子和叔父都能获得奖励。这里的叔父并非仅仅指父亲的兄弟,也包括爷爷的兄弟,爷爷的爷爷的兄弟。

image.png

一个侄子最多招安两个叔父,侄子获得1/321/32的出块奖励,最近的叔父获得7/87/8的出块奖励,最多追溯7代,叔父的奖励是递减的, 叔父没有交易费,不过这个也不重要,因为与BTC一样出块奖励远大于交易费。如果叔父后面还有节点将得不到任何奖励,这是为了鼓励出现分叉尽早合并,另外也是为了增加分叉攻击的代价。

以太坊的奖励机制与BTC一样有两部分

  1. 出块奖励
  2. 汽油费 (类似BTC中的交易费)

以太坊的出块奖励没有规定每四年减半,前一篇文章说过,总量固定的东西也有坏处。

挖矿算法

BTC的挖矿算法总的来说是比较成功的,经受住了时间检验。但是挖矿专业化广为诟病,很多人呼吁应该one CPU, one vote,所以之后的加密货币在设计mining puzzle时,通过增加对内存的访memory hard mining puzzle来抗 ASIC芯片化。

此外挖矿算法还需要 “求解困难验证容易“ 。这个在BTC中,假设A挖到矿,经历了无数次的调整noncecoinbase的加密计算,然后发布到BTC网络,别的矿工只需要一次加密计算即可验证。 在以太坊中也是类似的,只是一次加密过程需要使用大量内存来缓冲中间计算结果。

以太坊的挖矿算法也有难度调节,比较复杂,有以下几点

  1. 为了维持出币的稳定,要考虑叔父区块的奖励
  2. 预留难度炸弹系数,用于将来把挖矿算法从工作量证明转向权益证明
  3. 难度系数最多调节正负1/2048

具体算法如果感兴趣可以看一下这个视频

权益证明

据说去年9月份以太坊已经完成从工作量证明到权益证明的转换。

权益证明的思路是,谁的币多,谁的话语权多,大家一起投票决定谁来挖矿,投票是类似摇骰子的过程,这样保证话语权少的矿工也能参与挖矿。

如果一个矿工拥有的币最多,那么他每次都有最高的概率挖到矿,这也是不公平的,所有有的加密货币要求,你投入的币要锁定一段时间,等到挖出多少个块以后才能使用。

这样又会有人两边下注(当出现分叉时),因为暂时落后的一个分支也有可能成为主链,两边下注的好处是挖矿的机会增加了,而锁币减少了。

所以权益证明引入了验证者,

  1. 竞选验证者需要缴纳保证金,验证者负责推进达成共识,
  2. 验证者有多个,每轮投票需要2/3以上的验证者支持才算有效。
  3. 验证者不是终身制,每轮ephoch(假设50个区块)换一届
  4. 如果验证着履行职责将获得奖励
  5. 如果验证者 行政不作为(不投票)则罚钱、乱投票(给两边下注的矿工投票)则没收财产,这些钱会被销毁掉
  6. 验证者任期过了还需要等待一段时间才能取回保证金,这是为了其它矿工可以检举揭发验证者的不良行为。

权益证明的好处

  1. 环保节能(也有人说挖矿用于消耗过剩产能)
  2. 分叉攻击难度更大,想获得全网51%的投票钱,就需要买币,越买价格就越高,以太坊的开发者更高兴。
  3. 初期不会被扼杀在摇篮里。

那么为什么以太坊一开始不使用权益证明?
主要是权益证明挖矿算法不成熟,基于工作量证明的BTC挖矿算法已经运行很多年没有问题。

智能合约

什么是智能合约?
智能合约是运行在区块链上的一段代码,代码的逻辑定义了合约的内容。

下面一张图总结了区块链运行原理和智能合约所在位置

image.png

拿竞拍来举例子,你想搞一个竞拍,你的写一个Solidity程序,然后发布到区块链中,别人怎么知道你这个合约,你需要线下宣传。

智能合约的帐户保存了合约当前的运行状态

  • balance:当前余额
  • nonce:交易次数
  • code:合约代码
  • storage:存储,数据结构是一棵MPT

Solidity是智能合约最常用的语言,语法上与JavaScript很接近,合约中每行代码的执行都要消耗汽油费。

前面介绍账户的时候说过,合约账务服务直接发起交易,必须外部账户调用,外部账户调用智能合约有两种方式。

  1. 直接调用
  2. 使用address类型的call()函数

智能合约的创建和运行

  • 智能合约的代码写完后,要编译成bytecode
  • 创建合约:外部帐户发起一个转账交易到0x0的地址
  • 转账的金额是0,但是要支付汽油费
  • 合约的代码放在data域里

智能合约运行在EVM(Ethereum Virtual Machine)上

以太坊是一个交易驱动的状态机

  • 调用智能合约的交易发布到区块链上后,每个矿工都会执行这个交易,从当前状态确定性地转移到下一个状态

另外智能合约的交易具有原子性,遇到异常会回滚本次所有操作。

智能合约发布后,所有矿工必须先执行职能合约再挖矿

执行了智能合约消耗很多时间,结果挖矿失败有补偿吗?
没有任何补偿,这对挖矿慢的矿工比较吃亏

你不给我汽油费我就不验证,直接同步你的状态继续挖下一个矿不就可以了吗?
如果这种风气蔓延会危害区块链的安全,没人验证,挖到矿的矿工补救可以胡作非为了吗?

如果你不去验证,不去更新本地的三棵树,以后就没办法挖矿了
执行错误的交易也要发不到区块链上,不然汽油费扣不掉

智能合约不支持多线程,多线程对内存访问顺序不同,执行结果可能不同。因为要保证状态一致,所以不支持多线程.

智能合约如何保证程序的正确性?万一有人想搞破坏写个死循环怎么办?

因为Solidity是图灵完备的语言,没有办法保证你的程序正确性,以太坊的机制是将程序的正确性推给创建合约的人,每行代码的执行都需要消耗汽油费,不同的指令消耗的汽油费不同,如果程序运行失败,汽油费是不退的,这样创建合约的人就不会瞎搞。

如果有bug确实不能改,只能重新发布一个合约, 如果合约已经运行,这其实是很麻烦,因为创建合约的人要需要严格的测试。

The DAO

DAO 去中心化的自治组织

The DAO 基于众筹的去中心化组织,它的本质是运行在以太坊上的智能合约。你想投资什么,你可以把以太币发给这个智能合约,The DAO会给你代币,需要投资哪个项目是大家投票决定的,你手中的代币越多,投票权重就越大,最后如果有了收益,按智能合约的规章制度进行分配。

如果小部分人的投资项目得不到认可怎么办?比如有一个小众化项目,这一小部分人可以用拆分的方法从The DAO里面独立出来,成立自己的子资金,叫 child DAO,将代币在转换会以太币,再转入他们要投资的项目了。这也是投资者取回收益的唯一方式。

民主制度并不是绝对的少数服从多数,而是也要尊重少数人的选择的权利。这在当时被称为伟大的尝试,引起了很大的兴奋,它从2016年5月份开始众筹,一个月时间就筹集到了价值1.5亿美元的以太币(这是当时的价格,现在肯定以太币更值钱),这个效率比很多众筹网站效率高很多。

遗憾的是 The DAO 只存活了三个月,问题就出在TheDAO这个程序有bug。 image.png 正确的做法应该是先清零再转账,黑客就是利用这个漏洞(重入攻击),转走了The DAO 13\frac{1}{3}的钱。拆分有7天的讨论期,28天的锁定期,正是这个28天给了以太坊社区采用补救措施的时间。但这也导致了以太坊的分裂

一部分人认为应该回滚这次交易,还有一部分人认为不应该回滚,以太坊本身程序没问题,是以太坊上的运行的一个智能合约有漏洞,以太坊有那么多的智能合约,出了问题就会滚,那不就乱套了吗?那你还怎么说什么不可篡改的去中心化的账本?

由于这个事件影响很大,大家应该听过“大而不能倒”, 以太坊社区采取补救措施,两步走 先锁定这些账户, “凡事与TheDao这个基金相关的账户不允许任何交易” 。这是一个软分叉,假设升级协议的矿工为集合A,未升级协议的矿工为集合B,那么 集合A中有人挖到矿 集合B中的矿工都认,相反集合B中有人挖到矿,集合A中的矿工不认, 最后大家都会升级协议,系统中不会存在永久分叉。

遗憾的是升级后的软件又有"bug",因为没有认可这笔交易,所以没有收汽油费,看似很合理,结果黑客又利用这个漏洞发布大量非法交易,浪费矿工的时间,升级了协议的矿工受不了又退回之前的版本,软分叉失败了,这时候形势比较严峻了,剩余时间也不多了。

以太坊社区又推出了一个硬分叉的方案,通过升级软件协议,将 TheDao这个智能合约涨上的钱转移到另一个智能合约上,新的智能合约只有一个功能退钱。这样做争议很大,以太坊社区采用投票的方案,大部分人支持硬分叉,也升级了软件版本。

但是故事并没有结束,当初那些反对硬分叉的矿工并没有放弃,他们认为,一方面投票的人不多,另一方面投票就能说明问题吗?大多说人的意见一定是正确的吗?中国有很多人买不起药,把那些富豪的钱给平分了,可以吗?所以有一些矿工还在旧链上继续挖,这些人坚持根正苗红纯而又纯的去中心化,那些人都是高修正主义,过一段时间后交易所开始支持旧链上的货币ETC。

由于分叉前使用的都是同一个套账本这回导致重放攻击,后来给这两条链增建了 Chain ID 区分

刚刚查了下ETC目前还存活,ETC为了存活设计了通缩模型(每四年出块减半),尽管如此它相对于ETH这些年价格几乎没有增长。

反思:智能合约不可篡改的弊端

  1. 在中心化的世界,软件有漏洞很正常发布补丁很容易也很快,但是在区块链的世界不可想象,软件更新需要硬分叉的方式。发布硬分叉要说明理由,这无疑又把漏洞暴露出去了。(有传闻说,TheDAO的开发团队在TheDao发布前几天就收到智能条约存在安全漏洞的消息,但没有来的及发布更新后的软件)
  2. 即使发现漏洞,我们像冻结账户、终止交易都是很困难的,补救方案是利用漏洞把钱转移到另一个地址。

智能合约是用来写控制逻辑的,不适合存储(汽油费很贵),只有那些在互不信任的实体之间建立共识的操作才适合写在智能合约里。

本文作者:郭敬文

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!