区块链入门教程之从比特币到以太访再到智能合约从架构概念到应用实战(DAPP)(三、以太访介绍、原理、概念讲解)

时间:2022-06-05 08:50:28

区块链入门教程之从比特币到以太访再到智能合约从架构概念到应用实战(DAPP)(三、以太访介绍、原理、概念讲解)

作者: AlexTan
CSDN: http://blog.csdn.net/alextan_
Github: https://github.com/AlexTan-b-z
e-mail: alextanbz@gmail.com

前言

(ps:这是本博主撰写的第二部系列作品,第一部是写的java入门教程,受到了不少读者的喜欢,如果你也喜欢的话,欢迎关注哟!)

本教程主要面向区块链新手,用通俗易懂的方式讲解区块链技术。

前一篇我们讲了比特币的架构概览,这篇我们来讲一下以太访的架构以及两者之间的区别。

以太访是什么?

这个问题我在另外一篇实战文章中也提到过:以太坊+IPFS+WEB 电商平台开发讲解

可以简单理解为以太访就是一个区块链平台,什么是区块链平台?也就是以太访它把区块链技术底层都实现了,同时暴露给外部(区块链的上层应用)一些SDK供上层应用开发者使用,这个SDK也就是我们经常听说的智能合约,当然也包括web3(包括web3.js, web3.py等)。如果学过内核或者操作系统的人应该能更容易理解,以太访下的区块链底层技术就好比内核技术,它所暴露的SDK就好比内核层提供给应用层的系统调用。当然也可以用上文里提到的安卓的例子去理解,不过相对来说博主认为内核的例子更相似一点。

以太访被称为区块链2.0,它致力于把区块链技术应用在其他应用需求上,至于这些需求是什么是由企业家或其他区块链应用层开发者所去构想与实践的。

以太访虚拟机(EVM)

什么是以太访虚拟机?学过Java的朋友应该知道Java虚拟机(JVM),先解释一下什么是JVM吧。JAVA语言的个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上(比如说Windows、Linux)运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。这就是Java的能够“一次编译,到处运行”的原因。

同样,EVM也是为了达到同样的目的。但是EVM不同的是它是去中心化的,以太访网络中的每个节点(挖矿节点)都运行着以太访虚拟机,并执行相同的命令。因此,有人把它形象地称之为“世界电脑”。

这个贯穿整个以太坊网络的大规模并行运算并不是为了使运算更高效。实际上,这个过程使得在以太坊上的运算比在传统“电脑”上更慢更昂贵。然而,每个以太坊节点都运行着以太坊虚拟机是为了保持整个区块链的一致性。去中心化的一致使以太坊有极高的故障容错性,保证零停机,而且可以使存储在区块链上的数据保持永远不变且抗审查。

以太坊虚拟机是执行交易代码的引擎,也是以太坊与其他系统的核心区别。请注意,虚拟机应该同“合约与消息模型”分开考虑。例如,SIGNEXTEND操作码是虚拟机的一个功能,但实际上“某个合约调用其他合约并指定子调用的gas限定值”是“合约与消息模型”的一部分。 EVM的设计目标如下:
简单:操作码尽可能的少并且低级;数据类型尽可能少;虚拟机的结构尽可能少;
结果明确:在VM规范语句中,没有任何可能产生歧义的空间,结果应该是完全确定的。此外,计算步骤应该是精确的,以便可以测量gas的消耗量;
• **节约空间:**EVM组件应尽可能紧凑;
预期应用应具备专业化能力:在VM上构建的应用应能处理20字节的地址,以及32位的自定义加密值,拥有用于自定义加密的模数运算、读取区块和交易数据与状态交互等能力;
简单安全:为了让VM不被利用,应该能够容易地让建立一套gas消耗成本模型的操作;
优化友好:应该易于优化,以便即时编译(JIT)和VM的加速版本能够构建出来。

同时EVM也有如下特殊设计:

• 临时/永久存储的区别:
我们先来看看什么是临时存储和永久存储。
临时存储:存在于VM的每个实例中,并在VM执行结束后消失;
永久存储:存在于区块链状态层。
假设执行下面的树(S代表永久存储,M代表临时存储):
1.A调用B;
2.B设置B.S[0]=5,B.M[0]=9 ;
3.B调用C;
4.C调用B。
此时,如果B试图读取B.S[0],它将得到B前面存入的数据,也就是5;但如果B试图读取B.M[0],它将得到0,因为B.M是临时存储,读取它的时候是虚拟机的一个新的实例。
在一个内部调用中,如果设置B.M[0] = 13和 B.S[0] = 17 ,然后内部调用和C的调用都终止,再执行B的外部调用,此时读取M,将会看到B.M[0] = 9(此值在上一次同一VM执行实例中设置的),B.S[0] = 17。如果B的外部调用结束,然后A再次调用B,将看到B.M[0] = 0,B.S[0] = 17。这个区别的目的是:1.每个执行实例都分配有内存空间,不会因为循环调用而减损,这让安全编程更加容易。2.提供一个能够快速操作的内存形式:因为需要修改树,所以存储更新必然很慢。

• 栈/内存模式
早期,计算状态有三种:栈(stack,一个32字节标准的LIFO),内存(memory,可无限扩展的临时字节数组),存储(storage,永久存储)。在临时存储端,栈和内存的替代方案是memory-only范式,或者是寄存器和内存的混合体(两者区别不大,寄存器本质上也是一种内存)。在这种情况下,每个指令都有三个参数,例如:ADD R1 R2 R3: M[R1] = M[R2] + M[R3] 。选择栈范式的原因很明显,它使代码缩小了4倍。

• 单词大小32字节
在大多数结构中,如比特币,单词大小是4或8字节。4或8字节对存储地址或加密计算来说局限性太大了。而太大的值又很难建立相应安全的gas模型。32字节是一个理想大小,因为它足够存储下许多加密算法的实现以及地址,又不会因为太大而导致效率低下。

• 以太访自己的虚拟机
我们的虚拟机使用java、Lisp或Lua等语言开发。我们认为开发一款专业的虚拟机是值得的,因为:

  1. 我们的VM规范比其他许多虚拟机简单的多,因为其他虚拟机为复杂性付出的代价更小,也就是说它们更容易变得复杂;然而,在我们的方案中每额外增加一点复杂性,都会给集约化发展带来障碍,以及潜在的安全缺陷,比如造成共识失败,这就让我们的复杂性成本很高,因而不容易造成复杂;
  2. 我们的VM更加专业化,如支持32字节;
  3. 我们不会有复杂的外部依赖,复杂的外部依赖会导致我们安装失败;
  4. 完善的审查机制,可以具体到特殊的安全需求;对外部VM而言,这一点无论如何都是必要的。

• 使用了可变、可扩展的内存大小
固定内存的大小是不必要的限制,太小或太大都不合适。如果内存大小是固定的,每次访问内存都需要检查访问是否超出边界,显然这样的效率并不高。

• 栈大小没有限制
没什么特别理由!许多情况下,该设计不是绝对必要的;因为,gas的开销和区块层gas的限制总是会充当每种资源消耗的上限。

• 1024调用深度限制
许多编程语言在栈的深度过大时触发中断比在内存过载时触发中断的策略要快的多。所以区块中gas限制所隐含的限制是不够的。

• 无类型
为了简单起见,可以使用DIV, SDIV, MOD, SMOD的有符号或无符号的操作码代替(事实证明,对于操作码ADD和MUL,有符号和无符号是对等的);转换成定点运算在所有情况下都很简单,例如,在32位深度下,a * b -> (a * b) / 2^32, a / b -> a * 2^32 / b ,+, - 和 * 在整数下不变。

VM中某些操作码的函数和作用很容易理解,但也有一些不太好理解,以下是一些特殊的原因:

*• ADDMOD, MULMOD *
大多数情况下,addmod(a, b, c) = a * b % c,但在椭圆曲线算法中,使用的是32字节模数运算,直接执行a * b % c 实际上是在执行((a * b) % 2^256) % c会得到完全不同的结果。计算公式a * b % c 使用32字节空间的32字节值是非常不常见且繁琐。

• SIGNEXTEND
SIGNEXTEND操作码的作用是为了方便从大的有符号整数到小的有符号整数的类型转换。小的有符号整数是很有用的,因为未来的即时编译虚拟机可能会检测长时间运行的代码块,小的有符号整数能加快处理。

• SHA3
在以太坊代码中SHA3作为安全的高强度的hash集合应用非常广泛,通常在使用存储器时需要使用Hash函数来确保安全,以防止恶意冲突,在验证默克尔树和类似以太坊的数据结构时也需要使用到Hash函数。重要的是,与SHA3的相似的hash函数,如SHA256,ECRECVOR,RIPEM160不是以操作码的形式包含在里面,而是以伪合约的形式。这样做的目的是将它们放在一个单独的类别中,如果当我们以后提出适当的“本地扩展”系统时,可以添加更多这样的合约,而不需要扩展操作码。

• ORIGIN
ORIGIN操作码由交易的发送者提供,主要的作用是允许合约退回支付的gas。

• COINBASE
COINBASE的主要作用是:1.如果使用COINBASE操作码,则允许子货币对网络安全作出贡献;2.开放使用矿工作为一个去中心化的经济体,来设置基于子共识的应用,如Schellingcoin。

• PREVHASH
PREVHASH作为一个半安全的随机来源,允许合约评估上一个区块的默克尔树状态证明,而不需要高度复杂的递归结构“以太坊轻客户端”。

• EXTCODESIZE, EXTCODECOPY
主要的作用是让合约依据模板检查其他合约的代码,甚至是在与其他合约交互前,模拟它们。

• JUMPDEST
当跳转(jump)目的地限制在几个索引时(尤其是,动态目的跳转的计算复杂度是O(log(有效挑战目的数量)),而静态跳转总是恒定的),JIT虚拟机实现起来更简单。于是,我们需要:1.对有效变量跳转目的地做限制;2.使用静态而不是动态跳转的激励方式。为了达到这两个目标,我们定下了以下规则:1.紧接着push后的跳转可以跳到任何地方,而不仅是另一个jump;2.其他的jump只能跳转到JUMPDEST。对跳转的限制是必须的,你可以通过查看代码中的先前操作来决定是静态还是动态的跳转。缺乏对静态跳转的需求是激励使用它们的原因。禁止跳转进入push数据也会加快JIT虚拟机的编译和执行。

*• LOG *
LOG是事件的日志。

• CALLCODE
该操作码允许合约使用自己的存储器,在单独的栈空间和内存中调用其他合约的“函数”。这样可以在区块链上灵活实现标准库代码。

• SELFDESTRUCT
允许合约删除它自己,前提是它已经不需要存在了。SELFDESTRUCT并非立即执行,而是在交易执行完之后执行。这是因为这样做可以恢复那些执行后大大增加了缓存复杂度的SELFDESTRUCT操作码。

• PC
尽管理论上不需要PC操作码,因为所有的PC操作码都可以根据将push操作的索引加入实际程序计数器来代替实现,但使用PC可以创建独立代码的位置(可复制粘贴到其他合约的编译函数,如果它们以不同索引结束,不要打断)。

以太访核心——智能合约

智能合约,简单的理解就是运行在EVM上的代码,实际上是转成字节码后再在EVM上运行的,目前最流行的合约语言是Solidity,后面我们会具体讲解有关智能合约的东西。为什么称这样的代码为合约呢?因为部署在以太访上的智能合约是无法被任何人篡改的,就像签署合同签署后就不能更改了同理。因此就叫合约了。

在代码里,其实合约就是一个对象,一个类,它有成员变量、成员函数等,只不过一旦合约部署后,就相当于实例化了一个类。

与比特币最大的不同——账户

为什么说这是和比特币最大的不同呢?以太坊抛弃了UTXO的方案,转而使用更简单的方法:采用状态(state)的概念存储一系列账户,每个账户都有自己的余额,以及以太坊特有的数据(代码或内部存储器)。如果交易发起方的账户余额足够支付交易费用,则交易有效,那么发起方账户会扣除相应金额,而接受账户则计入该金额。 我们都知道比特币的每个用户都是有私钥、公钥的对吧。同样,以太访也有。但是在以太访中,存在两种账户类型:一种是外部账户,另外一种是合约账户

什么是外部账户呢?外部账户就是真正意义上的账户,即这个账户对应到具体的哪个人,是属于其拥有人的,是可控的(可控的意思是拥有人可以任意使用账户里的以太币等),实际上是被私钥控制。

那么什么是合约账户呢?顾名思义,合约账户就是一个合约,只是把合约账户化了,简单的理解,合约就像一个机器人,它不被私钥控制,也就是说不受某个人的控制,它只受编写好的合约代码控制。

下面我们来具体介绍一下两者的区别:

  1. 外部拥有的账户,被私钥控制且没有任何代码与之关联
  2. 合约账户,被它们的合约代码控制且有代码与之关联

区块链入门教程之从比特币到以太访再到智能合约从架构概念到应用实战(DAPP)(三、以太访介绍、原理、概念讲解)

理解外部拥有账户和合约账户的基本区别是很重要的。一个外部拥有账户可以通过创建和用自己的私钥来对交易进行签名,来发送消息给另一个外部拥有账户或合约账户。在两个外部拥有账户之间传送的消息只是一个简单的价值转移。但是从外部拥有账户到合约账户的消息会激活合约账户的代码,允许它执行各种动作。(比如转移代币,写入内部存储,挖出一个新代币,执行一些运算,创建一个新的合约等等)。

不像外部拥有账户,合约账户不可以自己发起一个交易。相反,合约账户只有在接收到一个交易之后(从一个外部拥有账户或另一个合约账户接),为了响应此交易而触发一个交易。我们将会在“交易和消息”部分来了解关于合约与合约之间的通信。

区块链入门教程之从比特币到以太访再到智能合约从架构概念到应用实战(DAPP)(三、以太访介绍、原理、概念讲解)

因此,在以太坊上任何的动作,总是被外部控制账户触发的交易所发动的。

区块链入门教程之从比特币到以太访再到智能合约从架构概念到应用实战(DAPP)(三、以太访介绍、原理、概念讲解)

如果对账户还不是很理解的朋友,当讲到后面具体的合约实战后,相信就不难理解了。

账户状态

账户状态有四个组成部分,不论账户类型是什么,都存在这四个组成部分:

  • nonce:如果账户是一个外部拥有账户,nonce代表从此账户地址发送的交易序号,相当于交易id。如果账户是一个合约账户,nonce代表合约的创建账户创建的合约序号
  • balance: 此地址拥有Wei的数量。1Ether=10^18Wei
  • storageRoot: Merkle Patricia树的根节点Hash值。Merkle树会将此账户存储内容的Hash值进行编码,默认是空值
  • codeHash:此账户EVM(以太坊虚拟机,后面细说)代码的hash值。对于合约账户,就是被Hash的代码并作为codeHash保存。对于外部拥有账户,codeHash域是一个空字符串的Hash值

区块链入门教程之从比特币到以太访再到智能合约从架构概念到应用实战(DAPP)(三、以太访介绍、原理、概念讲解)

看了上面的解释,可能还是有的朋友不是很理解到底什么是nonce?

nonce解释

这个nonce和比特币里的nonce可以说是完全不同的了。为了防止交易的重播攻击,每笔交易必须有一个nonce随机数,针对每一个账户nonce都是从0开始,当nonce为0的交易处理完之后,才会处理nonce为1的交易,并依次加1的交易才会被处理。以下是nonce使用的几条规则:

  • 当nonce太小,交易会被直接拒绝。因为太小的nonce值已经存在(除非为负数)。
  • 当nonce太大,交易会一直处于队列之中,这也就是导致我们上面描述的问题的原因;
  • 当发送一个比较大的nonce值,然后补齐开始nonce到那个值之间的nonce,那么交易依旧可以被执行。
  • 当交易处于queue中时停止geth客户端,那么交易queue中的交易会被清除掉,queue是存在于内存中的。

合约账户的nonce同理,合约账户的nonce相当于是账户创建的合约的id而已。(ps: 博主关于这点理解花费了一点时间,所以讲得比较多,很多其他博文说这个nonce是合约账户创建的合约序号,其实这种说法严格说来博主认为是不对的,因为前面也解释了什么是合约账户,它是由编写好的代码控制,而不是由私钥(用户)控制,合约账户其实就是合约本身,它也有一个地址,但并不由私钥控制,因此合约账户根本不能创建合约,合约是被外部账户创建(部署)的,而这个nonce值代表的是外部账户创建合约的序号而已,即这个外部账户创建的第几个合约(合约id)。)

请注意这节讲的是账户状态!!!

看到这里,可能还有的朋友有点蒙,请注意这里讲的是账户状态,什么是账户状态?顾名思义,即某一个时刻的状态嘛,从数据结构来讲,其实可以把它当做交易里的一部分(或者说是和交易是一一对应的关系),即当发生一笔交易时(或者是部署合约的操作时),账户的状态。这样解释相信大家就容易理解多了。

消息和交易

以太坊的消息在某种程度上类似于比特币的交易,但是两者之间存在三点重要的不同。第一,以太坊的消息可以由外部实体或者合约创建,然而比特币的交易只能从外部创建。第二,以太坊消息可以选择包含数据。第三,如果以太坊消息的接受者是合约账户,可以选择进行回应,这意味着以太坊消息也包含函数概念。

以太坊中“交易”是指存储从外部账户发出的消息的签名数据包。交易包含消息的接收者、用于确认发送者的签名、以太币账户余额、要发送的数据和两个被称为STARTGAS和GASPRICE的数值。为了防止代码的指数型爆炸和无限循环,每笔交易需要对执行代码所引发的计算步骤-包括初始消息和所有执行中引发的消息-做出限制。STARTGAS就是限制,GASPRICE是每一计算步骤需要支付矿工的费用。如果执行交易的过程中,“用完了瓦斯”,所有的状态改变恢复原状态,但是已经支付的交易费用不可收回了。如果执行交易中止时还剩余瓦斯,那么这些瓦斯将退还给发送者。创建合约有单独的交易类型和相应的消息类型;合约的地址是基于账号随机数和交易数据的哈希计算出来的。

消息机制的一个重要后果是以太坊的“头等公民”财产-合约与外部账户拥有同样权利,包括发送消息和创建其它合约的权利。这使得合约可以同时充当多个不同的角色,例如,用户可以使去中心化组织(一个合约)的一个成员成为一个中介账户(另一个合约),为一个偏执的使用定制的基于量子证明的兰波特签名(第三个合约)的个人和一个自身使用由五个私钥保证安全的账户(第四个合约)的共同签名实体提供居间服务。以太坊平台的强大之处在于去中心化的组织和代理合约不需要关心合约的每一参与方是什么类型的账户。

以太坊状态转换函数

以太坊的状态转换函数:APPLY(S,TX) -> S',可以定义如下:

  1. 检查交易的格式是否正确(即有正确数值)、签名是否有效和随机数是否与发送者账户的随机数匹配。如否,返回错误。
  2. 计算交易费用:fee=STARTGAS * GASPRICE,并从签名中确定发送者的地址。从发送者的账户中减去交易费用和增加发送者的随机数。如果账户余额不足,返回错误。
  3. 设定初值GAS = STARTGAS,并根据交易中的字节数减去一定量的瓦斯值。
  4. 从发送者的账户转移价值到接收者账户。如果接收账户还不存在,创建此账户。如果接收账户是一个合约,运行合约的代码,直到代码运行结束或者瓦斯用完。
  5. 如果因为发送者账户没有足够的钱或者代码执行耗尽瓦斯导致价值转移失败,恢复原来的状态,但是还需要支付交易费用,交易费用加至矿工账户。
  6. 否则,将所有剩余的瓦斯归还给发送者,消耗掉的瓦斯作为交易费用发送给矿工。 例如,假设合约的代码如下:
if not self.storage[calldataload(0)]:
    self.storage[calldataload(0)] = calldataload(32)

需要注意的是,在现实中合约代码是用底层以太坊虚拟机(EVM)代码写成的。上面的合约是用我们的高级语言Serpent语言写成的,它可以被编译成EVM代码。假设合约存储器开始时是空的,一个值为10以太,瓦斯为2000,瓦斯价格为0.001以太并且64字节数据,第一个三十二字节的块代表号码2和第二个代表词CHARLIE。的交易发送后,状态转换函数的处理过程如下:

  1. 检查交易是否有效、格式是否正确。
  2. 检查交易发送者至少有2000*0.001=2个以太币。如果有,从发送者账户中减去2个以太币。
  3. 初始设定gas=2000,假设交易长为170字节,每字节的费用是5,减去850,所以还剩1150。
  4. 从发送者账户减去10个以太币,为合约账户增加10个以太币。
  5. 运行代码。在这个合约中,运行代码很简单:它检查合约存储器索引为2处是否已使用,注意到它未被使用,然后将其值置为CHARLIE。假设这消耗了187单位的瓦斯,于是剩余的瓦斯为1150 - 187 = 963。
  6. 向发送者的账户增加963*0.001=0.963个以太币,返回最终状态。

如果没有合约接收交易,那么所有的交易费用就等于GASPRICE乘以交易的字节长度,交易的数据就与交易费用无关了。另外,需要注意的是,合约发起的消息可以对它们产生的计算分配瓦斯限额,如果子计算的瓦斯用完了,它只恢复到消息发出时的状态。因此,就像交易一样,合约也可以通过对它产生的子计算设置严格的限制,保护它们的计算资源。

与比特币的另一个不同——gas费

以太访中为什么要有Gas

其实gas费就是交易产生的手续费,上面所讲到的以太访上的任何动作,总是被外部账户触发的交易所发动的,因此像部署合约、调用合约里的“某些”函数,都会产生gas费。为什么说它和比特币不同呢,大家都知道比特币交易也会产生手续费,但比特币的手续费是直接用比特币去缴纳,这就存在随着比特币的升值,比特币的交易手续费会越来越高的问题。而以太访专门设计gas费,主要就是为了解决这个问题的。同比特币一样,这些gas费最终都是奖励给矿工的。

gas与以太币的换算

在以太访中,gas是通过ETH(以太币)兑换而来的,其具体数额为:

TX fee = gasPrice * gasUsed

gasPrice由用户决定,一般来说,gasPrice越高,交易受到确认的速度越快。因此具体价格是受整个市场的供求关系,即矿工和交易(合约也是一种交易)发起者的博弈 来调控的。

gasUsed 是由交易(或者是执行合约代码)的计算量来决定的,戳这里可以查看每一种操作所需要消耗的gas量。简单的说,是由合约代码的复杂度所决定的。因此,合约代码要编写得尽量简洁高效。

gas消耗计算有以下特点:

• 对于任何交易,都将收取21000gas的基本费用。这些费用可用于支付运行椭圆曲线算法所需的费用。该算法旨在从签名中恢复发送者的地址以及存储交易所花费的硬盘和带宽空间。

• 交易可以包括无限量的“数据”。虚拟机中的某些操作码,可以让合约允许交易对这些数据的访问。数据的固定消耗计算是:每个零字节4gas,非零字节68gas。这个公式的产生是因为合约中大部分的交易数据由一些列的32字节的参数组成,其中多数参数具有许多前导零字节。该结构看起来似乎效率不高,但由于压缩算法的存在,实际上还是很有效率的。我们希望此结构能够代替其他更复杂的机制:这些机制根据预期字节数严格包装参数,从而导致编译阶段复杂性大增。这是三明治复杂模型的一个例外,但由于成本效益比,这也是合理的模型。

• 用于设置账户存储器的操作码SSTORE的消耗是:1.将零值改为非零值时,消耗20000gas;2.将零值变成零值,或非零值变非零值,消耗5000gas;3.将非零值变成零值,消耗5000gas,加上交易执行成功后退回的20000gas。退款金额上限是交易消耗gas总额的50%。这样设置会激励人们清除存储器。我们注意到,正因为缺乏这样的激励,许多合约造成了存储空间没有被有效使用,从而导致了存储快速膨胀;为存储收取费用提供了很多好处,同时不会失去合约一旦确立就可以永久存在的保证。延迟退款机制是必要的,因为可以阻止拒绝服务攻击。攻击者发送一笔含有少量gas的交易,循环清除大量的存储,直到用光gas,这样消耗了大量的验证算力,但实际并没有真正清除存储或消耗大量gas。50%的上限的是为了确保获得了一定交易gas的旷工依然能够确定执行交易的计算时间的上限。

• 合约提供的消息的数据是没有成本的。因为在消息调用期间不需要实质复制任何数据,调用数据可以简单地视为指向父合约内存的指针,该指针在子进程执行时不会改变。

• 内存是一个可以无限扩展的数组,然而,每扩展32字节的内存就会消耗1gas的成本,不足32字节以32字节计。

• 某些操作码的计算时间极度依赖参数,gas开销计算是动态变化的。例如,EXP的的开销是指数级别的;复制操作码(如:CALLDATACOPY, CODECOPY, EXTCODECOPY)的开销是1+1(每复制32字节)。内存扩展的开销不包含在这里,因为它触发了二次攻击。

• 如果值不是零,操作码CALL会额外消耗9000gas。这是因为任何值传输都会引起归档节点的历史存储显著增大。请注意,实际消耗是6700,在此基础上,我们强制增加了一个自动给予接受者的gas值,这个值最小2300。这样做是为了让接受交易的钱包至少有足够的gas来记录交易。

gas机制的另一个重要部分是gas价格本身体现出的经济学原理。比特币中,默认的方法是采取纯粹自愿的收费方式,矿工扮演守门人的角色并且动态设置收费的最小值。以太坊中允许交易发送者设置任意数目的gas。这种方式在比特币社区非常受欢迎,因为它是“市场经济”的体现:允许矿工和交易者之间依据供需关系来决定价格。然而,这种方式的问题是,交易处理并不遵循市场原则。尽管可以将交易处理看作是矿工向发送者提供的服务(这听起来很直观),但实际上矿工所处理的每个交易都必须由网络中的每个节点处理,所以交易处理的大部分成本都由第三方机构承担,而不是决定是否处理它的矿工。

当前,因为缺乏矿工在实际中的行为的明确信息,所以我们将采取一个非常简单公平的方法:投票系统,来设定gas限定值。矿工有权将当前区块的gas限定值设定在最后区块的gas限定值的0.0975% (1/1024)内。所以最终的gas限定值应该是矿工们设置的中间值。我们希望将来能够采用软分叉的方法来使用更加精确的算法。

gasLimit

可能有的朋友听说过gasLimit,那什么又是gasLimit呢?区块gas limit是单个区块允许的最多gas总量,以此可以用来决定单个区块中能打包多少笔交易。例如,我们有5笔交易的gas limit分别是10、20、30、40和50.如果区块gas limit是100,那么前4笔交易就能被成功打包进入这个区块。矿工有权决定将哪些交易打包入区块。所以,另一个矿工可以选择打包最后两笔交易进入这个区块(50+40),然后再将第一笔交易打包(10)。如果你尝试将一个会使用超过当前区块gas limit的交易打包,这个交易会被网络拒绝,你的以太坊客户端会反馈错误”交易超过区块gas limit”。以下例子是来自于以太坊StackExhcange的帖子。

目前区块的gas limit是 4,712,357 gas,数据来自于ethstats.net,这表示着大约224笔转账交易(gas limit为21000)可以被塞进一个区块(区块时间大约在15-20秒间波动)。这个协议允许每个区块的矿工调整区块gas limit,任意加减 1/2024(0.0976%)。

谁来决定

区块的gas limit是由在网络上的矿工决定的。与可调整的区块gas limit协议不同的是一个默认的挖矿策略,即大多数客户端默认最小区块gas limit为4,712,388。

区块gas limit是怎样改变的

以太坊上的矿工需要用一个挖矿软件,例如ethminer。它会连接到一个geth或者Parity以太坊客户端。Geth和Pairty都有让矿工可以更改配置的选项。这里是geth挖矿命令行选项以及Parity的选项。

gas费的作用

前面说到“以太坊上的运算比在传统’电脑‘上更昂贵”,为什么昂贵,其罪魁祸首就是gas费了。那为什么要有gas费呢?其最大目的就是避免计算资源被滥用,造成整个网络堵塞、甚至瘫痪。以太访是去中心化的,资源是公用的,但矿工的计算资源是有限的,因此一旦计算资源被全部占用,其他交易就没法被确认,就会造成整个网络的堵塞,甚至瘫痪。而且合约语言是图灵完备的(可以简单理解为代码里可以写循环),要是没有Gas费的话,一些人写一堆死循环很容易造成整个网络的瘫痪。所谓DoS攻击就是指此。因此gas费的作用就是避免这样的情况发生。

与比特币的又一个不同——私有链、联盟链、公有链

比特币是只有一个公有链的,而以太访提出了私有链、联盟链、公有链。以下是博客发文《关于公有链和私有链》的摘录,它解释了三种区块链在许可方面的区别:

  • 公有链:世界上所有人都可以阅读和发送交易。如果他们合法都有希望看到自己被包括在内。世界上任何人都能参与到共识形成过程——决定在链条上添加什么区块以及现状是怎样的。作为中心化或准中心化信任的替代品,公有链受加密经济的保护,加密经济是经济激励和加密图形验证的结合,用类似工作量证明或权益证明的机制,遵循的总原则是人们影响共识形成的程度和他们能够影响的经济资源数量成正比。这类区块链通常被认为是”完全去中心化”。
  • 联盟链:共识形成过程由预先选择的一系列的节点所掌控,例如,设想一个有15个金融机构的团体,每个机构都操作一个节点,为了使区块生效,其中的10个必须签署那个区块。阅读区块链的权利可能是公开的,或仅限于参与者,也有混合的路径,比如区块的根散表和应用程序编程接口一起公开,使公共成员可以进行一定量的查询,重获一部分区块链状态的加密图形证明。这类区块链被认为是”部分去中心化”。
  • 私人区块链:书写许可对一个组织保持中心化。阅读许可可能是公开的或者限制在任意程度。应用很可能包含对单个公司内部的数据库管理,审查等,因此公共的可读性在很多情况下根本不必要,但在另一些情况下人们又想要公共可读性。 私有链/联盟链可能和公有链毫无联系,他们仍然通过投资以太坊软件开发,对以太坊整体生态系统有利。经过一段时间,这会转变成软件改善,知识共享和工作机会。

以太访挖矿算法

以太访目前的挖矿算法为Ethash。Ethash是目前以太坊基于POW工作量证明的一个共识引擎(也叫挖矿算法)。它的前身是Dagger Hashimoto算法。

Dagger Hashimoto

作为以太坊挖矿算法Ethash的前身,Dagger Hashimoto的目的是:

  • 抵制矿机(ASIC,专门用于挖矿的芯片)
  • 轻客户端验证
  • 全链数据存储

Dagger和Hashimoto其实是两个东西,

Hashimoto算法

是这个人Thaddeus Dryja创造的。旨在通过IO限制来抵制矿机。在挖矿过程中,使内存读取限制条件,由于内存设备本身会比计算设备更加便宜以及普遍,在内存升级优化方面,全世界的大公司也都投入巨大,以使内存能够适应各种用户场景,所以有了随机访问内存的概念RAM,因此,现有的内存可能会比较接近最优的评估算法。Hashimoto算法使用区块链作为源数据,满足了上面的1和3的要求。

Dagger算法

是这个人Vitalik Buterin发明的。它利用了有向无环图DAG同时实现了Memory-Hard Function内存计算困难但易于验证Memory-easy verification的特性(我们知道这是哈希算法的重要特性之一)。它的理论依据是基于每个特定场合nonce只需要大型数据总量树的一小部分,并且针对每个特定场合nonce的子树的再计算是被禁止挖矿的。因此,需要存储树但也支持一个独立场合nonce的验证价值。Dagger算法注定要替代现存的仅内存计算困难的算法,例如Scrypt(莱特币采用的),它是计算困难同时验证亦困难的算法,当他们的内存计算困难度增加至真正安全的水平,验证的困难度也随之难上加难。然而,Dagger算法被证明是容易受到Sergio Lerner发明的共享内存硬件加速技术,随后在其他路径的研究方面,该算法被遗弃了。

Memory-Hard Function

直接翻译过来是内存困难函数,这是为了地址矿机而诞生的一种思想。我们知道挖矿是靠我们的电脑,但是有些硬件厂商会制造专门用于挖矿的硬件设备,它们并不是一台完整的PC机,例如ASIC、GPU以及FPGAs(我们经常能听到GPU挖矿等)。所以这些作为矿机的设备是超越普通PC挖矿的存在,这是不符合我们区块链的去中心化精神的,所以我们要让挖矿设备平等。那么该如何让挖矿设备是平等的呢?

上面谈到Dagger算法的时候其实提到了,这里换一种方式再来介绍一下,现在CPU都是多核的,如果从计算能力来讲,CPU有几核就可以模拟几台设备同时平行挖矿,自然效率就高些,但是这里采用的衡量对象是内存,一台电脑只有一个总内存。我们做过java多线程开发的朋友知道,无论机器性能有多高,但我们写的程序就是单线程的,那么这个程序运行在高配多核电脑和低配单核电脑的区别不大,只要他们的单核运算能力和内存大小一样即可。所以也是这个原理,通过Dagger算法,我们将挖矿流程锁定在以内存为衡量标准的硬件性能上,只要通过“塞一堆数据到内存中”的方式,让多核平行处理发挥不出来,降低硬件的运算优势,只与内存大小有关,这样无论是PC机还是ASIC、GPU以及FPGAs,都可达到平等挖矿的诉求,这也是ASIC-resistant原理,目前抵制矿机的主要手段。

两个问题的研究

在Dagger以及Dagger Hashimoto算法中,有两个问题的研究是被搁置的,

  • 基于区块链的工作量证明:一个POW函数包括了运行区块链上的合约。该方法被抛弃是因为这是一个长期的攻击缺陷,因为攻击者能够创建分叉,然后通过一个包含秘密的快速“trapdoor”井盖门的运行机制的合约在该分叉上殖民。
  • 随机环路:一个POW函数由这个人Vlad Zamfir开发,包含了每1000个场合nonces就生成一个新的程序的功能。本质上来讲,每次选择一个新的哈希函数,会比可重配置的FPGAs(可重编程的芯片,不必重新焊接电路板就可通过软件技术重新自定义硬件功能)更快。该方法被暂时搁置,是因为它很难看到有什么机制可以用来生成随机程序是足够全面,因此它的专业化收益是较低的。然而,我们并没有看到为什么这个概念无法让它生效的根本原因,所以暂时搁置。

Dagger Hashimoto算法

(区别于Hashimoto)Dagger Hashimoto不是直接将区块链作为数据源,而是使用一个1GB的自定义生成的数据集cache。

这个数据集是基于区块数据每N个块就会更新。该数据集是使用Dagger算法生成,允许一个自己的高效计算,特定于每个轻客户端校验算法的场合nonce。

(区别于Dagger)Dagger Hashimoto克服了Dagger的缺陷,它用于查询区块数据的数据集是半永久的,只有在偶然的间隔才会被更新(例如每周一次)。这意味着生成数据集将非常容易,所以Sergio Lerner的争议共享内存加速变得微不足道了。

挖矿补充

以太坊将过渡到POS(proof-of-stake),代替传统的POW,挖矿将会被淘汰掉,所以现在不推荐再去做一名矿工(前期购买设备等成本较大,POS实现前未必能回本)。

挖掘以太币=网络安全=验证估算

目前以太坊的POW算法是Ethash,

Ethash算法包含找到一个nonce值输入到一个算法中,得到的结果是低于一个基于特定困难度的阀值。

POW算法的关键点是除了暴力枚举,没有任何办法可以找到这个nonce值,但对于验证输出的结果是非常简单容易的。如果输出结果有一个均匀分布,我们就可以保证找到一个nonce值的平均所需时间取决于那个难度阀值,因此我们可以通过调整难度阀值来控制找到一个新块的时间,这就是控制出块速度的原理。

DAG

Ethash的POW是memory-hard,支持矿机抵御。这意味着POW计算需要选择一个固定的依赖于nonce值和块头的资源的子集。

这个资源(大约1G大小)就是DAG!

一世epoch

每3万个块会花几个小时的时间生成一个有向无环图DAG。这个DAG被称为epoch,一世(为了好记,refer个秦二世)。DAG只取决于区块高度,它可以被预生成,如果没有预生成的话,客户端需要等待预生成流程结束以后才能继续出块操作。除非客户端真实的提前预缓存了DAG,否则在每个epoch的过渡期间,网络可能会经历一个巨大的区块延迟。

特例:当你从头启动一个结点时,挖矿工作只会在创建了现世DAG以后启动。

Ethash

Ethash算法路线图:

  • 存在一个种子seed,通过扫描块头为每个块计算出来那个点。
  • 根据这个种子seed,可以计算一个16MB的伪随机缓存cache,轻客户端存储这个缓存。
  • 从这个缓存cache中,我们能够生成一个1GB的数据集,该数据集中的每一项都取决于缓存中的一小部分。完整客户端和矿工存储了这个数据集,数据集随着时间线性增长。
  • 挖矿工作包含了抓取数据集的随机片以及运用哈希函数计算他们。校验工作能够在低内存的环境下完成,通过使用缓存再次生成所需的特性数据集的片段,所以你只需要存储缓存cache即可。x

以上提到的大数据集是每3万个块更新一次,所以绝大多数的矿工的工作是读取该数据集而不是改变它。

RLP


RLP(recursive length prefix):递归长度前缀。
RLP编码是以太坊中主要的序列化格式,它的使用无处不在:区块、交易、账户状态以及线路协议消息。详见RLP正式描述: https://github.com/ethereum/wiki/wiki/RLP

RLP旨在成为高度简化的序列化格式,它唯一的目的是存储嵌套的字节数组③。不同于protobuf、BSON等现有的解决方案,RLP并不定义任何指定的数据类型,如Boolean、floa、double或者integer。它仅仅是以嵌套数组的形式存储结构,并将其留给协议来确定数组的含义。RLP也没有明确支持map集合,半官方的建议是采用 [[k1, v1], [k2, v2], …] 的嵌套数组来表示键值对集合,k1,k2 … 按照字符串的标准排序。

与RLP具有相同功能的方案是protobuf或BSON,它们是一直被使用的算法。然而,以太坊中,我们更偏向于使用RLP,因为:
1.它易于实现;
2.绝对保证字节的一致性。
许多语言的Map集合没有明确的排序,并且浮点格式有很多特殊情况,这可能造成相同数据却导致不同编码和hash值。通过内部开发协议,我们能确保它是带着这些目标设计的(这是一般原则,也适用于代码的其他部分,如VM)。BitTorrent使用的编码方式bencode也许可以替代RLP。不过它采用的是十进制的编码方式,与采用二进制的RLP相比,稍微逊色了点。

压缩算法
线路协议和数据库都采用了一个自定义的压缩算法来存储数据。该算法可描述为:行程编码④值为0并同时保留其他值(除了一些特殊情况如sha3(’ ‘) ),举例如下:

>>> compress('horse')
'horse'
>>> compress('donkey dragon 1231231243')
'donkey dragon 1231231243'
>>> compress('\xf8\xaf\xf8\xab\xa0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbe{b\xd5\xcd\x8d\x87\x97')
'\xf8\xaf\xf8\xab\xa0\xfe\x9e\xbe{b\xd5\xcd\x8d\x87\x97'
>>> compress("\xc5\xd2F\x01\x86\xf7#<\x92~}\xb2\xdc\xc7\x03\xc0\xe5\x00\xb6S\xca\x82';{\xfa\xd8\x04]\x85\xa4p")
‘\xfe\x01'

压缩算法存在之前,以太坊协议的许多地方都有一些特殊情况,例如,sha3经常被覆盖,所以sha3(’ ‘)=’ ‘,这样不需要在账户中存储代码,可以节省64字节。然而,最近所有这些使得以太坊数据结构变得臃肿的特殊情况都被删除了,取而代之的是将数据保存函数添加到区块链协议之外的层,也就是将其放入线路协议以及将其插入用户数据库实现。这样增加了模块化能力,简化了共识层,使得压缩算法能持续更新以便相对容易部署。

以太访区块结构

以太访的区块头

  • parentHash:父区块头的Hash值(这也是使得区块变成区块链的原因)
  • ommerHash:当前区块ommers列表的Hash值
  • beneficiary:接收挖此区块费用的账户地址
  • stateRoot:状态树根节点的Hash值
  • transactionsRoot:包含此区块所列的所有交易的树的根节点Hash值
  • receiptsRoot:包含此区块所列的所有交易收据的树的根节点Hash值
  • logsBloom:由日志信息组成的一个Bloom过滤器 (数据结构)
  • difficulty: 此区块的难度级别
  • number:当前区块的计数(创世纪块的区块序号为0,对于每个后续区块,区块序号都增加1)
  • gasLimit:每个区块的当前gas limit
  • gasUsed: 此区块中交易所用的总gas量
  • timestamp:此区块成立时的unix的时间戳
  • extraData:与此区块相关的附加数据
  • mixHash:一个Hash值,当与nonce组合时,证明此区块已经执行了足够的计算
  • nonce:一个Hash值,当与mixHash组合时,证明此区块已经执行了足够的计算

区块链入门教程之从比特币到以太访再到智能合约从架构概念到应用实战(DAPP)(三、以太访介绍、原理、概念讲解)

注意每个区块是如何包含三个树结构的,三个树结构分别对应:

  • 状态(stateRoot) 代表访问区块后的整个状态

  • 交易(transactionsRoot)代表区块中所有交易,这些交易由index索引作为key;(例如,k0:第一个执行的交易,k1:第二个执行的交易)

  • 收据(receiptsRoot)代表每笔交易相应的收据。交易的收据是一个RLP编码的数据结构:

    [ medstate, gas_used, logbloom, logs ]

    其中:
    medstate:交易处理后,树的根的状态;
    gas_used:交易处理后,gas的使用量;
    logs:是表格[address, [topic1, topic2...], data]元素的列表。表格由交易执行期间调用的操作码LOG0LOG4 生成(包含主调用和子调用);address 是生成日志的合约的地址topics是最多4个32字节的值;data 是任意字节大小的数组;
    • logbloom:交易中所有logs的address和topic组成的布隆过滤器⑤。
    区块头中也存在一个布隆过滤器,它是区块中交易的所有布隆过滤器的逻辑或组成。这样的构造使得以太坊协议轻客户端的使用尽可能的友好。

日志

以太坊允许日志可以跟踪各种交易和信息。一个合约可以通过定义“事件”来显示的生成日志。
一个日志的实体包含:

  • 记录器的账户地址
  • 代表本次交易执行的各种事件的一系列主题以及与这些事件相关的任何数据

日志被保存在bloom过滤器 中,过滤器高效的保存了无尽的日志数据。

Uncle块(过时区块)的奖励


2013年10月,由乔森纳和特拉维夫首次提出的GHOST协议是一项不起的革新。它是加快生成区块时间的第一个认真尝试。因为区块在网络中传播需要一定时间,如果矿工A挖到一个区块并向全网广播,在广播的路上,B也挖出了区块,那么B的区块是过时的,且B的本次挖矿对网络的安全没有贡献。GHOST的目的正是要解决挖矿过时造成的安全性降低的问题。

此外,还有一个中心化问题:如果A是一个矿池,有30%的算力,B有10%的算力。A有70%的时间产生过时的区块(因为另外的30%时间会产生最新区块,所以它会立即挖到数据),而B有90%的时间产生过时区块。如果区块的产出时间间隔很短,那么过时率就会变高,则A凭借其更大的算力使挖矿效率也更高。所以,区块生成过快,容易导致网络算力大的矿池控制挖矿过程。

根据乔森纳和特拉维夫的描述,GHOST解决了在计算哪个链是最长的链的过程中,因产生过时区块而造成的网络安全性下降的问题。也就是说,不仅是父区块和更早的区块,同时Uncle区块⑥也被添加到计算哪个块具有最大的工作量证明中去。

为了解决第二个问题:中心化问题,我们采用不同的策略:对过时区块也提供区块奖励:挖到过时区块的奖励是该区块基础奖励的7/8;挖到包含过时区块的nephew区块将收到1/32的基础奖励作为赏金。但是,交易费并不奖励给Uncle区块或nephew区块。

在以太坊,过时分叉上7代内的亲属区块才能称作过时区块。之所以这样限制是因为,首先,GHOST协议若不限制过时区块数量,将会花费大量开销在计算过时区块的有效性上;其次,无限制的过时区块激励政策会让矿工失去在主链上挖矿的热情;最后,计算表明,过时区块奖励政策限制在7层内提供了大部分所需的效果,而且不会带来负面效应。

区块时间算法的设计决策包括:

区块时间12s:选择12s是因为这已经是最快的时间了,基本上比网络延迟更长。在2013年的一份关于测量比特币网络延迟的论文中,确定了12.6秒是新产生的区块传播到95%节点的时间;然而,该论文还指出传播时间与区块大小成比例,因此在更快的货币中,我们可以期待传播时间大大减少。传播间隔时间是恒定的,约为2秒。然而,为了安全起见,在我们的分析中,我们假定区块的传播需要12秒。

7个祖先块的限制:这样设计的目的是希望只保留少量区块,而将更早之前的区块清除。已经证明7个区块可以提供大部分所需的效果。

1个后裔区块的限制:如c(c(p(p(p(head))))) c=child, p = parent,就不合法,因为它有两个后裔区块。这样设计的目的是为了简单,上面的模拟结果显示它并没有构成大的中心化风险。

• **uncle块要求具有有效性: **uncle块必须是有效的header,而不是有效的区块。这样做也是为了简化,将区块链模型保持为线性数据结构。不过,要求uncle块是有效的区块也是合法的方法。

• **奖金分配:**7/8的挖矿基础奖励分配给uncle块,1/32分给nephew块,它们交易费用都是0%。如果费用占多数,从中心化的角度看,这会使uncle块激励机制无效;然而,这也是为什么只要我们继续使用PoW,以太坊就会不断发行以太币的原因。

区块难度

区块的难度是被用来在验证区块时加强一致性。创世纪区块的难度是131,072,有一个特殊的公式用来计算之后的每个块的难度。如果某个区块比前一个区块验证的更快,以太坊协议就会增加区块的难度。

区块的难度影响nonce,它是在挖矿时必须要使用proof-of-work算法来计算的一个hash值。

区块难度和nonce之间的关系用数学形式表达就是:

区块链入门教程之从比特币到以太访再到智能合约从架构概念到应用实战(DAPP)(三、以太访介绍、原理、概念讲解)

Hd代表的是难度。

找到符合难度阈值的nonce唯一方法就是使用proof-of-work算法来列举所有的可能性。找到解决方案预期时间与难度成正比—难度越高,找到nonce就越困难,因此验证一个区块也就越难,这又相应地增加了验证新块所需的时间。所以,通过调整区块难度,协议可以调整验证区块所需的时间。
另一方面,如果验证时间变的越来越慢,协议就会降低难度。这样的话,验证时间自我调节以保持恒定的速率—平均每15s一个块。

难度更新算法

目前以太坊通过以下规则进行难度更新:

diff(genesis) = 2^32
diff(block) = diff.block.parent + floor(diff.block.parent / 1024) *
    1 if block.timestamp - block.parent.timestamp < 9 else
    -1 if block.timestamp - block.parent.timestamp >= 9

难度更新规则的设计目标如下:

快速更新:区块间的时间应该随着hash算力的增减而快速调整;

低波动性:如果Hash算力恒定,那么难度不应剧烈波动;

简单:算法的实现应相对简单;

低内存:算法不应依赖于过多的历史区块,要尽可能少的使用”内存变量“。假设有最新的十个区块,将存储在这十个区块头部的内存变量相加,这些区块都可用于算法的计算;

非开发性:算法不应让矿工有过多篡改时间戳或者矿池、反复添加或删除算力的能力,以使他们的收益最大化。

我们当前的算法在低波动性和非开发性上并不理想,至少我们计划切换时间戳比较父区块和祖父区块,所以矿工只有在连续挖2个区块时,才有动力去修改时间戳。另一个更强大的模拟公式: https://github.com/ethereum/economic-modeling/blob/master/diffadjust/blkdiff.py

区块链和挖矿

虽然有一些不同,但以太坊的区块链在很多方面类似于比特币区块链。它们的区块链架构的不同在于,以太坊区块不仅包含交易记录和最近的状态,还包含区块序号和难度值。以太坊中的区块确认算法如下:

  1. 检查区块引用的上一个区块是否存在和有效。
  2. 检查区块的时间戳是否比引用的上一个区块大,而且小于15分钟。
  3. 检查区块序号、难度值、 交易根,叔根和瓦斯限额(许多以太坊特有的底层概念)是否有效。
  4. 检查区块的工作量证明是否有效。
  5. S[0]赋值为上一个区块的STATE_ROOT
  6. TX赋值为区块的交易列表,一共有n笔交易。对于属于0……n-1i,进行状态转换S[i+1] = APPLY(S[i],TX[i])。如果任何一个转换发生错误,或者程序执行到此处所花费的瓦斯(gas)超过了GASLIMIT,返回错误。
  7. S[n]S_FINAL赋值, 向矿工支付区块奖励。
  8. 检查S_FINAL是否与STATE_ROOT相同。如果相同,区块是有效的。否则,区块是无效的。

这一确认方法乍看起来似乎效率很低,因为它需要存储每个区块的所有状态,但是事实上以太坊的确认效率可以与比特币相提并论。原因是状态存储在树结构中(tree structure),每增加一个区块只需要改变树结构的一小部分。因此,一般而言,两个相邻的区块的树结构的大部分应该是相同的,因此存储一次数据,可以利用指针(即子树哈希)引用两次。一种被称为“帕特里夏树”(“Patricia Tree”)的树结构可以实现这一点,其中包括了对默克尔树概念的修改,不仅允许改变节点,而且还可以插入和删除节点。另外,因为所有的状态信息是最后一个区块的一部分,所以没有必要存储全部的区块历史-这一方法如果能够可以应用到比特币系统中,经计算可以对存储空间有10-20倍的节省。

捐助地址:

BTC:
区块链入门教程之从比特币到以太访再到智能合约从架构概念到应用实战(DAPP)(三、以太访介绍、原理、概念讲解)

36Q4ivp2bJer9fUQ6uyj4a4yLuTpp28D1T

ETH:
区块链入门教程之从比特币到以太访再到智能合约从架构概念到应用实战(DAPP)(三、以太访介绍、原理、概念讲解)

0x786fda245ff497ce18e53618369a3e730a18fc1b

ENS: alextan.eth