以太坊的工作原理,深度知识

2020-04-15 06:31 来源:未知

前言:本文主要阐述当前以太坊的具体运作原理,有助于我们理解以太坊背后的各种概念和操作,适合初学者阅读。未来ETH2.0将会发生非常大的变化,具体可以看蓝狐笔记之前的文章《ETH2.0:它会是什么?》和《ETH2.0:它会是什么?》。本文作者是Preethi Kasireddy,来源于medium.com,由蓝狐笔记社群“Leo”翻译。

链接: does Ethereum work, anyway?参考:以太坊的黄皮书

图片 1

介绍

这篇文章主要讲解以太坊的基本原理,对技术感兴趣的朋友可以看看。

在当下数据爆炸的信息时代,凭借区块链去中心化、点对点和防篡改的特性,“区块链 大数据”已成为研究的热门,可以说,区块链与大数据的结合为今后区块链应用的大规模落地奠定了基础。

不管你是否了解它究竟是什么,也许你已经听说过以太坊区块链。因为它在新闻中经常出现。假如你对以太坊没有基本了解,阅读本文可能有点摸不着头脑。那以太坊到底是什么?它本质上是公共数据库,它保存数字交易的永久记录。重要的是,这个数据库并不要求任何中心机构来维持和保护它的安全。相反,它作为一个“去中介信任”的交易系统进行运作,这是一种框架,在其中个人可以进行对等交易,同时无需任何第三方或彼此之间的信任。

不管你们知不知道以太坊(Ethereum blockchain)是什么,但是你们大概都听说过以太坊。最近在新闻里出现过很多次,包括一些专业杂志的封面,但是如果你们对以太坊到底是什么没有一个基本的了解的话,看这些文章就会感觉跟看天书一样。 所以,什么是以太坊?本质上,就是一个保存数字交易永久记录的公共数据库。重要的是,这个数据库不需要任何中央权威机构来维持和保护它。相反的它以一个“无信任”的交易系统来运行—一个个体在不需要信任任何第三方或对方的情况下进行点对点交易的架构。

那么,区块链中的数据如何存储?不同区块链数据存储机制有何异同?以以太坊为例,在本文中,MIT 孵化初创公司 TowardsBlockChain 联合创始人 vasa 详细阐述了以太坊的数据存储机制、以太坊如何存储区块链状态与交易以及以太坊和比特币在存储机制上的异同。

还感到困惑吗?这就是本文可以发挥作用的地方。我的目标是从技术层面来解释以太坊是如何运作的,这里不会有复杂的数学或吓人的公式。即使你不是程序员,我也希望你至少能有所收获。如果有部分太过于技术化和太过于晦涩,这也没有关系。你不需要懂得所有细节。我建议只需从大的层面去理解它。

依然感到很困惑?这就是这篇文章存在的理由。我的目标是在技术层面来解释以太坊的工作原理,但是不会出现很复杂的数学问题或看起来很可怕的公式。即使你不是一个程序员,我希望你看完之后最起码对技术有个更好的认识。如果有些部分技术性太强不好理解,这是非常正常的,真的没有必要完全理解每一个小细节。我建立只要宏观的理解一下事物就行了。

此外,本文将带你深入了解 “Patricia 字典树”数据结构背后的理论基础,并通过使用 Google 的 levelDB 数据库演示以太坊字典树的具体实现。

本文中提到的主题都是以太坊黄皮书中的概念剖析。我添加了自己的解释和图表,这样更容易理解。如果你对技术挑战感兴趣,也可直接阅读黄皮书。

这篇文章中的很多议点都是以太坊黄皮书中讨论过的概念的细分。我添加了我自己的解释和图表使理解以太坊更加简单一点。那些足够勇敢的人可以挑战一下技术,去阅读一下以太坊的黄皮书。

字字行文皆重点,行行代码皆干货,请往下看!

区块链的定义

好了, 让我们开始吧!

从架构设计上来说,区块链可以简单的分为三个层次:协议层、扩展层和应用层。其中,协议层又可以分为存储层和网络层,它们相互独立但又不可分割。

区块链是一种“具有共享状态的加密安全的交易单例机。”让我们拆解一下。

区块链定义

区块链就是一个具有共享状态的密码性安全交易的单机(cryptographically secure transactional singleton machine with shared-state)。[1]

这有点长,是吧?让我们将它分开来看:

  • “密码性安全(Cryptographically secure)”是指用一个很难被解开的复杂数学机制算法来保证数字货币生产的安全性。将它想象成类似于防火墙的这种。它们使得欺骗系统近乎是一个不可能的事情(比如:构造一笔假的交易,消除一笔交易等等)。
  • “交易的单机(Transactional singleton machine)”是指只有一个权威的机器实例为系统中产生的交易负责任。换句话说,只有一个全球真相是大家所相信的。
  • “具有共享状态(With shared-state)”是指在这台机器上存储的状态是共享的,对每个人都是开放的。

以太坊实现了区块链的这个范例。

以太坊的本质就是一个基于交易的状态机(transaction-based state machine)

在计算机科学中,一个 状态机 是指可以读取一系列的输入,然后根据这些输入,会转换成一个新的状态出来的东西。

图片 2

根据以太坊的状态机,我们从创世纪状态(genesis state)开始。这差不多类似于一片空白的石板,在网络中还没有任何交易的产生状态。当交易被执行后,这个创世纪状态就会转变成最终状态。在任何时刻,这个最终状态都代表着以太坊当前的状态。

图片 3

以太坊的状态有百万个交易。这些交易都被“组团”到一个区块中。一个区块包含了一系列的交易,每个区块都与它的前一个区块链接起来。

图片 4

为了让一个状态转换成下一个状态,交易必须是有效的。为了让一个交易被认为是有效的,它必须要经过一个验证过程,此过程也就是挖矿。挖矿就是一组节点用它们的计算资源来创建一个包含有效交易的区块出来。

任何在网络上宣称自己是矿工的节点都可以尝试创建和验证区块。世界各地的很多矿工都在同一时间创建和验证区块。每个矿工在提交一个区块到区块链上的时候都会提供一个数学机制的“证明”,这个证明就像一个保证:如果这个证明存在,那么这个区块一定是有效的。

为了让一个区块添加到主链上,一个矿工必须要比其他矿工更快的提供出这个“证明”。通过矿工提供的一个数学机制的“证明”来证实每个区块的过程称之为工作量证明(proof of work)

证实了一个新区块的矿工都会被奖励一定价值的奖赏。奖赏是什么?以太坊使用一种内在数字代币—以太币作为奖赏。每次矿工证明了一个新区块,那么就会产生一个新的以太币并被奖励给矿工。

你也许会在想:什么能确保每个人都只在区块的同一条链上呢?我们怎么能确定不会存在一部分矿工创建一个他们自己的链呢?

前面,我们定义了区块链就是一个具有共享状态的交易单机。使用这个定义,我们可以知道正确的当前状态是一个全球真相,所有人都必须要接受它。拥有多个状态会摧毁这个系统,因为它在哪个是正确状态的问题上不可能得到统一结果。如果链分叉了,你有可能在一条链上拥有10个币,一条链上拥有20个币,另一条链上拥有40个币。在这种场景下,是没有办法确定哪个链才是最”有效的“。

不论什么时候只要多个路径产生了,一个”分叉“就会出现。我们通常都想避免分叉,因为它们会破坏系统,强制人们去选择哪条链是他们相信的链。

为了确定哪个路径才是最有效的以及防止多条链的产生,以太坊使用了一个叫做“GHOST协议(GHOST protocol.)”的数学机制。

GHOST = Greedy Heaviest Observed Subtree

简单来说,GHOST协议就是让我们必须选择一个在其上完成计算最多的路径。一个方法确定路径就是使用最近一个区块的区块号,区块号代表着当前路径上总的区块数。区块号越大,路径就会越长,就说明越多的挖矿算力被消耗在此路径上以达到叶子区块。使用这种推理就可以允许我们赞同当前状态的权威版本。

图片 5

现在你大概对区块链是什么有个理性的认识,让我们在再深入了地解一下以太坊系统主要组成部分:

  • 账户
  • 状态
  • 损耗和费用(gas and fees)
  • 交易(transactions)
  • 区块
  • 交易执行(transaction execution)
  • 挖矿
  • 工作量证明(proof of work)

在开始之前需要注意的是:每当我说某某的hash, 我指的都是 KECCAK-256 hash, 以太坊就是使用这个hash算法。

首先了解下区块链的数据存储层,什么是区块链数据存储层?它存储了什么?它需要存储哪些数据才能保障区块链系统正常工作?

“加密安全”是指所创建的数字货币由复杂的数学算法来保证它的安全,这个算法很难被破解。想一下各种防火墙。他们几乎不可能欺骗系统,例如创造虚假交易、删除交易记录等。

账户

以太坊的全局“共享状态”是有很多小对象来组成的,这些账户可以通过消息传递架构来与对方进行交互。每个账户都有一个与之关联的状态和一个20字节的地址。在以太坊中一个地址是160位的标识符,用来识别账户的。

这是两种类型的账户:

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

图片 6

图片 7

“交易的单例机”是指有单个规范实例机负责所有在系统中创建的交易。换句话说,这里有一个所有人都相信的单一全球事实。

外部拥有账户与合约账户的比较

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

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

图片 8

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

图片 9

比如Alice向Bob转账10美元。从上图可以看出,可以通过向区块链中加入一笔交易来改变区块链当前的状态

“具有共享状态”是指存储在这个机器上的状态是共享的,向每个人公开。

账户状态

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

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

图片 10

在跟踪不同用户的账户余额和其他相关的细节的同时,也要跟踪不同用户通过区块链所引起的区块链状态转变的细节。

以太坊实现的是这种区块链的范式。

世界状态

好了,我们知道了以太坊的全局状态就是由账户地址和账户状态的一个映射组成。这个映射被保存在一个叫做Merkle Patricia树的数据结构中。Merkle Tree(也被叫做Merkle trie)是一种由一系列节点组成的二叉树,这些节点包括:

  • 在树底的包含了源数据的大量叶子节点
  • 一系列的中间的节点,这些节点是两个子节点的Hash值
  • 一个根节点,同样是两个子节点的Hash值,代表着整棵树

图片 11

树底的数据是通过分开我们想要保存到chunks的数据产生的,然后将chunks分成buckets,再然后再获取每个bucket的hash值并一直重复直到最后只剩下一个Hash:根Hash。

图片 12

这棵树要求存在里面的值都有一个对应的key。从树的根节点开始,key会告诉你顺着哪个子节点可以获得对应的值,这个值存在叶子节点。在以太坊中,key/value是地址和与地址相关联的账户之间状态的映射,包括每个账户的balance, nonce, codeHash和storageRoot(storageRoot自己就是一颗树)。

图片 13

同样的树结构也用来存储交易和收据。更具体的说,每个块都有一个,保存了三个不同Merkle trie结构的根节点的Hash,包括:状态树、交易树、收据树。

图片 14

在Merkle tries中存储所有信息的高效性在以太坊中的“轻客户端”和“轻节点”相当的有用。记住区块链就是一群节点来维持的。广泛的说,有两种节点类型:全节点和轻节点。

全节点通过下载整条链来进行同步,从创世纪块到当前块,执行其中包含的所有交易。通常,矿工会存储全节点,因为他们在挖矿过程中需要全节点。也有可能下载一个全节点而不用执行所有的交易。无论如何,一个全节点包含了整个链。

不过除非一个节点需要执行所有的交易或轻松访问历史数据,不然没必要保存整条链。这就是轻节点概念的来源。比起下载和存储整个链以及执行其中所有的交易,轻节点仅仅下载链的头,从创世纪块到当前块的头,不执行任何的交易或检索任何相关联的状态。由于轻节点可以访问块的头,而头中包含了3个tries的Hash,所有轻节点依然可以很容易生成和接收关于交易、事件、余额等可验证的答案。

这个可以行的通是因为在Merkle树中hash值是向上传播的—如果一个恶意用户试图用一个假交易来交换Merkle树底的交易,这个会改变它上面节点的hash值,而它上面节点的值的改变也会导致上上一个节点Hash值的改变,以此类推,一直到树的根节点。

图片 15

任何节点想要验证一些数据都可以通过Merkle证明来进行验证,Merkle 证明的组成:

  • 一块需要验证的数据
  • 树的根节点Hash
  • 一个“分支”(从 chunk到根这个路径上所有的hash值)

图片 16

任何可以读取证明的人都可以验证分支的hash是连贯的,因此给出的块在树中实际的位置就是在此处。

总之,使用Merkle Patricia树的好处就是该结构的根节点加密取决于存储在树中的数据,而且根据点的hash还可以作为该数据的安全标识。由于块的头包含了状态、交易、收据树的根hash,所有任何节点都可以验证以太坊的一小部分状态而不用保存整个状态,这整个状态的的大小可能是非常大的。

不同的区块链,比如比特币和以太坊,实现上述功能所使用的方法是不同的。

以太坊区块链的范式阐述

Gas和费用

在以太坊中一个比较重要的概念就是费用由以太坊网络上的交易而产生的每一次计算,都会产生费用—没有免费的午餐。这个费用是以称之为”gas”的来支付。

gas就是用来衡量在一个具体计算中要求的费用单位。gas price就是你愿意在每个gas上花费Ether的数量,以“gwei”进行衡量。“Wei”是Ether的最小单位,1Ether表示10^18Wei. 1gwei是1,000,000,000 Wei。

对每个交易,发送者设置gas limit和gas price。gas limit和gas price就代表着发送者愿意为执行交易支付的Wei的最大值。

例如,假设发送者设置gas limit为50,000,gas price为20gwei。这就表示发送者愿意最多支付50,000*20gwei = 1,000,000,000,000,000 Wei = 0.001 Ether来执行此交易。

图片 17

记住gas limit代表用户愿意花费在gas上的钱的最大值。如果在他们的账户余额中有足够的Ether来支付这个最大值费用,那么就没问题。在交易结束时任何未使用的gas都会被返回给发送者,以原始费率兑换。

图片 18

在发送者没有提供足够的gas来执行交易,那么交易执行就会出现“gas不足”然后被认为是无效的。在这种情况下,交易处理就会被终止以及所有已改变的状态将会被恢复,最后我们就又回到了交易之前的状态—完完全全的之前状态就像这笔交易从来没有发生。因为机器在耗尽gas之前还是为计算做出了努力,所以理论上,将不会有任何的gas被返回给发送者

图片 19

这些gas的钱到底去了哪里?发送者在gas上花费的所有钱都发送给了“受益人”地址,通常情况下就是矿工的地址。因为矿工为了计算和验证交易做出了努力,所以矿工接收gas的费用作为奖励。

图片 20

通常,发送者愿意支付更高的gas price,矿工从这笔交易总就能获得更多的价值。因此,矿工也就更加愿意选择这笔交易。这样的话,矿工可以自由的选择一笔交易自己愿意验证或忽略。为了引导发送者应该设置gas price为多少,矿工可以选择建议一个最小的gas值他们愿意执行一个交易。

1、比特币的“状态”

比特币的“状态”由其全网络未使用的交易输出UTXO(Unspent Transaction Output)来表示。 比特币的价值转移是通过交易来实现的。更具体地说,比特币用户可以通过创建一笔交易并将其一个或多个UTXO添加为交易的输入来花掉这一个或多个UTXO。

比特币的UTXO模型,是其区别于以太坊的主要特征,为更好地理解二者之间的差异,先来看一些例子。

首先,比特币中的UTXO不能只花费一部分,必须全部花完

如果一个比特币用户要花费0.5个比特币,而他只有一个价值1比特币的UTXO,那么在交易时他必须将自己的比特币地址也加入到交易的输出中,即发给自己0.5个比特币作为找零

如果他不给自己发送找零,他将失去这0.5个比特币,这0.5个比特币将会被当作交易费付给挖出此区块的矿工。

图片 21

UTXO交易

其次,从本质上讲,比特币的区块链并不会存储和更新用户的账户余额。在比特币网络中,用户只需持有一个或多个 UTXO 的私钥。

数字钱包的使用使得比特币的区块链看起来像是在自动存储和更新用户的帐户余额,但其实并不是这样。

图片 22

图解比特币钱包工作过程

比特币的 UTXO 模型运行良好,一部分原因是数字钱包能够执行与交易相关的大多数任务,包括但不局限于:

  • 处理 UTXO

  • 存储密钥

  • 设定交易费用

  • 提供交易找零地址

  • 汇总 UTXO(显示可用的、交易进行中的和总余额)

如何来描述 UTXO 模型中的交易行为?钞票是一个绝佳的类比。

用户通过将钱包(类比比特币地址或者数字钱包)中的钞票相加来计算自己的资金,想要花钱时,就使用一张或者多张钞票。

每张钞票只能使用一次,因为一旦花费,它就不属于你了。

因此,可以得出这样的结论:

  • 比特币区块链并不存储和更新账户余额

  • 比特币钱包持有UTXO对应的私钥

  • 如果UTXO包含在交易中,那么它会被全部花完(在 UTXO 大于支出金额时,会收到一个全新 UTXO 的“找零”)

与上述比特币的区块链不同,以太坊区块链中的状态能够存储和更新用户的账户余额等信息。

以太坊的状态不是一个抽象的概念,它是以太坊底层协议的一部分。

正如以太坊黄皮书所提到的,以太坊是一个基于交易的“状态机”是一个可以构建所有基于交易的“状态机”的技术

与所有其他区块链一样,以太坊的区块链由创世区块开始延伸。

从创世区块开始,诸如交易,部署智能合约和挖矿等行为将不断改变以太坊区块链的状态。在以太坊中,每当有与该帐户相关的交易发生时,帐户余额(存储在状态字典树中)就会发生变化。

帐户余额等数据并不直接存储在以太坊区块链的区块中, 只有交易字典树、状态字典树和收款字典树的根节点哈希直接存储在区块链中。如下图:

图片 23

存储字典树(保存所有智能合约数据的地方)的根节点哈希实际上指向状态字典树,而状态字典树又指向区块链。

以太坊中存储着两种截然不同的数据:永久数据和临时数据。

交易信息为永久数据,一笔交易在得到完全确认后,将被记录在交易字典树中,它永远不会改变;账户余额则为临时数据,地址对应的账户余额存储在状态字典树中,并且每当出现与该指定帐户相关的交易时账户余额就会更改。

因此,永久数据和临时数据应单独、分别存储,以太坊使用字典树的数据结构来管理数据

以太坊的记录保存机制与银行一样,一个类比就是使用ATM /借记卡。

银行跟踪每张借记卡的余额,当用户需要花钱时,银行会检查交易记录,以判断用户是否有足够的余额来进行交易。

以太坊区块链本质上是基于交易的状态机。在计算机科学中,状态机指的是读取一系列的输入的东西,基于这些输入,会转换到新的状态。

存储也有费用

gas不仅仅是用来支付计算这一步的费用,而且也用来支付存储的费用。存储的总费用与所使用的32位字节的最小倍数成比例。

存储费用有一些比较细微的方面。比如,由于增加了的存储增加了所有节点上的以太坊状态数据库的大小,所以激励保持数据存储量小。为了这个原因,如果一个交易的执行有一步是清除一个存储实体,那么为执行这个操作的费用就会被放弃,并且由于释放存储空间的退款就会被返回给发送者。

3、比特币 UTXO 模型与以太坊账户/余额模型的比较

比特币 UTXO 模型的优点:

  • 可扩展性:由于可以同时处理多个 UTXO,因此可以实现并行交易并可促进在可扩展性上的创新。

  • 隐私保护:即使比特币不是一个完全匿名的系统,但只要用户每笔交易都使用新地址,UTXO 模型就能提供更高级别的隐私保护。如果需要增强隐私保护,可以考虑使用更复杂的方案,例如环签名。

以太坊账户/余额模型的优点:

  • 简单性:以太坊选择了更简单直观的模型,便于开发人员实现复杂的智能合约,特别是那些需要以太坊网络状态信息或涉及多个参与方的智能合约。

    比如基于以太坊网络的不同状态执行不同任务的智能合约,若使用 UTXO 的无状态模型,需要强制在每笔交易中加入状态信息,这会使智能合约的设计复杂化。

  • 高效性:除了简单性之外,以太坊账户/余额模型更加高效,因为每笔交易只需要验证发送方账户是否有足够的余额来支付交易。

为防止以太坊账户/余额模型遭到双重支付攻击,可以用一个递增的随机数来防范这种类型的攻击。

在以太坊中,每个帐户都有一个公共可见的随机数,每次进行交易时,这个随机数增加1,这种机制可以防止同一笔交易被多次提交。

这个随机数与以太坊工作量证明的随机数不同,后者是一个挖矿过程的随机值

在计算机体系架构中,有时需要在不同模型之间进行折衷。一些区块链技术,比如 Hyperledger,就采用了 UTXO 机制,因为这样可以从比特币区块链所衍生的创新中受益。

接下来简要分析更多基于这两种记录保存模型构建的技术。

图片 24

费用的作用是什么?

以太坊可以运作的一个重要方面就是每个网络执行的操作同时也被全节点所影响。然而,计算的操作在以太坊虚拟机上是非常昂贵的。因此,以太坊智能合约最好是用来执行最简单的任务,比如运行一个简单的业务逻辑或者验证签名和其他密码对象,而不是用于复杂的操作,比如文件存储,电子邮件,或机器学习,这些会给网络造成压力。施加费用防止用户使网络超负荷

以太坊是一个"图灵完备语言",这就允许有循环,并使以太坊受到停止问题 的影响,这个问题让你无法确定程序是否无限制的运行。如果没有费用的话,恶意的执行者通过执行一个包含无限循环的交易就可以很容易的让网络瘫痪而不会产生任何反响。因此,费用保护网络不受蓄意攻击。

短而言之,图灵机器就是一个可以模拟任何电脑算法的机器。对于图灵机器不太熟悉的人可以看看 这个 和 这个 。

你也许会想,“为什么我们还需要为存储付费?”其实就像计算一样,以太坊网络上的存储是整个网络都必须要负担的成本。

以太坊字典树数据结构

以太坊字典树数据结构主要包括状态字典树、存储字典树和交易字典树。

1、状态字典树——独一无二的存在

在以太坊网络中有一个唯一的全网络状态字典树

这个全网络状态字典树不断在更新。

这个全网络状态字典树中包含以太坊网络中每个账户所对应的键值对(key and value pair)。

全网络状态字典树中的“键”是一个的160位标识符。

全网络状态字典树中的“值”是通过对以太坊账户的以下详细信息进行编码(使用递归长度字典编码(Recursive-Length Prefix encoding,RLP)方法)生成的:

  • Nonce:一个公共可见的随机数。如果帐户是一个外部帐户,这个数字代表从帐户地址发送的交易数量;如果帐户是一个合约帐户,Nonce 是帐户创建的合约数量。

  • balance:这个地址拥有的 Wei数量,每个以太币有1e 18 Wei。

  • storageRoot :一个Merkle Patricia 树根节点的哈希,它对帐户的存储内容的哈希值进行编码,并默认为空。

  • codeHash:EVM的哈希值代码。 对于合约帐户,这是一个被哈希计算后并存储为codeHash的代码;对于外部帐户,codeHash字段是空字符串的哈希值。

状态字典树的根节点(在给定时间点整个状态字典树的哈希值)被用作状态字典树的安全且唯一的标识符;状态字典树的根节点在密码学上取决于状态字典树所有内部的数据。

图片 25

状态字典树(Merkle Patricia 字典树的levelDB实现)和以太坊区块之间的关系

图片 26

状态字典树:在给定的区块中,状态字典树根节点的 Keccak-256位哈希值被存储为“stateRoot”值stateRoot: ‘0x8c77785e3e9171715dd34117b047dffe44575c32ede59bde39fbf5dc074f2976’

有了以太坊的状态机,我们从“创世状态”开始。这类似于空白平板,没有任何交易在网络上发生过。当交易执行,创世状态转换为某种最终状态。在任何时间点,最终状态代表以太坊的当前状态。

交易和消息

之前说过以太坊是一个基于交易的状态机。换句话说,在两个不同账户之间发生的交易才让以太坊全球状态从一个状态转换成另一个状态。

最基本的概念,一个交易就是被外部拥有账户生成的加密签名的一段指令,序列化,然后提交给区块链。

有两种类型的交易:消息通信合约创建(也就是交易产生一个新的以太坊合约)

不管什么类型的交易,都包含:

  • nonce:发送者发送交易数的计数
  • gasPrice:发送者愿意支付执行交易所需的每个gas的Wei数量
  • gasLimit:发送者愿意为执行交易支付gas数量的最大值。这个数量被设置之后在任何计算完成之前就会被提前扣掉
  • to:接收者的地址。在合约创建交易中,合约账户的地址还没有存在,所以值先空着
  • value:从发送者转移到接收者的Wei数量。在合约创建交易中,value作为新建合约账户的开始余额
  • v,r,s:用于产生标识交易发生着的签名
  • init(只有在合约创建交易中存在):用来初始化新合约账户的EVM代码片段。init值会执行一次,然后就会被丢弃。当init第一次执行的时候,它返回一个账户代码体,也就是永久与合约账户关联的一段代码。
  • data(可选域,只有在消息通信中存在):消息通话中的输入数据。例如,如果智能合约就是一个域名注册服务,那么调用合约可能就会期待输入域例如域名和IP地址

图片 27

在“账户”这个章节中我们学到交易—消息通信和合约创建交易两者都总是被外部拥有账户触发并提交到区块链的。换种思维思考就是,交易是外部世界和以太坊内部状态的桥梁。

图片 28

但是这也并不代表一个合约与另一个合约无法通信。在以太坊状态全局范围内的合约可以与在相同范围内的合约进行通信。他们是通过“消息”或者“内部交易”进行通信的。我们可以认为消息或内部交易类似于交易,不过与交易有着最大的不同点—它们不是由外部拥有账户产生的。相反,他们是被合约产生的。它们是虚拟对象,与交易不同,没有被序列化而且只存在与以太坊执行环境。

当一个合约发送一个内部交易给另一个合约,存在于接收者合约账户相关联的代码就会被执行。

图片 29

一个重要需要注意的事情是内部交易或者消息不包含gasLimit。因为gas limit是由原始交易的外部创建者决定的(也就是外部拥有账户)。外部拥有账户设置的gas limit必须要高到足够将交易完成,包括由于此交易而长生的任何”子执行”,例如合约到合约的消息。如果,在一个交易或者信息链中,其中一个消息执行使gas已不足,那么这个消息的执行会被还原,包括任何被此执行触发的子消息。不过,父执行没必要被还原。

2、存储字典树——存储智能合约数据的地方

存储字典树存储所有智能合约数据,每个以太坊帐户都有自己的存储字典树。存储字典树根节点的256位哈希值作为“storageRoot”值存储在全局状态字典树中。

图片 30

图片 31

区块

所有的交易都被组成一个”块”。一个区块链包含了一系列这样的链在一起区块。在以太坊中,一个区块包含:

  • 区块头
  • 关于包含在此区块中交易集的信息
  • 与当前块的ommers相关的一系列其他区块头
3、交易字典树——每个区块一个

每个以太坊区块都有自己独立的交易字典树。

一个区块中包含许多交易,区块中交易的顺序由挖出该区块的矿工决定。

交易字典树中到特定交易的路径经RLP编码后得到交易在区块中的索引。

由于区块链的防篡改性,已经被挖出的区块不会再改变,所以区块中交易的位置永远不会改变

一旦在区块的交易字典树中找到这笔交易,即使你反复返回相同的路径,检索的结果也是相同的。

图片 32

主流的以太坊客户端使用两种不同的数据库软件解决方案来存储字典树。以太坊的 Rust 语言客户端 Parity 使用 rocksDB 数据库,而以太坊的 Go 语言,C 语言和 Python 语言客户端都使用 levelDB 数据库

本文中,主要带你了解 levelDB 数据库。

以太坊的状态有数百万的交易。这些交易组成区块。一个区块包含一系列的交易,并且每个区块与其前一个区块链接在一起。

Ommers解释

“ommer”到底是什么? ommer就是一个区块的父区块与当前区块父区块的父区块是相同的。让我们快速了解一下ommers是用来干嘛的,并且为什么一个区块需要为ommers包含区块头。

由于以太坊的构造,它的区块生产时间比其他的区块链例如Bitcoin要快很多。这使得交易的处理更快。但是,更短的区块生产时间的一个缺点就是:更多的竞争区块会被矿工发现。这些竞争区块同样也被称为“孤区块”(也就是被挖出来但是不会被添加到主链上的区块)。

Ommers的目的就是为了帮助奖励矿工纳入这些孤区块。矿工包含的ommers必须是有效的,也就是ommers必须在父区块的第6个子区块之内或更小范围内。在第6个子区块之后,陈旧的孤区块将不会再被引用(因为包含老旧的交易会使事情变得复杂一点)。

Ommer区块会收到比全区块少一点的奖励。不管怎样,依然存在激励来让矿工们纳入孤区块并能从中获得一些报酬。

1. 以太坊和 levelDB 数据库

LevelDB 是一个开源的谷歌键值存储程序库,除了常规功能外,它还提供对数据的前向和后向迭代,从字符串键到字符串值的有序映射,自定义比较函数和自动压缩。

自动压缩功能使用开源 Google 压缩/解压缩程序库 “Snappy”。Snappy 程序库的设计目标并不是追求最大压缩率,而是追求非常高的压缩速度。

LevelDB 数据库是一种重要的存储和检索机制,用于管理以太坊网络的状态。因此,levelDB 是主流以太坊客户端,比如 go-ethereum,cpp-ethereum 和 pyethereum 的底层数据库。

虽然可以在磁盘上完成字典树数据结构的实现(使用诸如 levelDB 之类的数据库软件),但重要的是要注意遍历字典树和简单地查看键/值数据库之间存在的差异。

为了更详细说明这些差异,可以使用 Patricia 字典树的程序库来访问数据库levelDB 中的数据。

在以太坊客户端上,执行交易、部署智能合约和挖矿等网络操作,并观察它们如何影响以太坊的“状态”。

图片 33

区块头

让我们再回到区块的问题上。我们前面提到每个区块都有一个“区块头”,但这究竟是什么?

区块头是一个区块的一部分,包含了:

  • 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组合时,证明此区块已经执行了足够的计算

图片 34

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

  • 状态(stateRoot)
  • 交易(transactionsRoot)
  • 收据(receiptsRoot)这三个树结构就是我们前面讨论的Merkle Patricia树。

另外,上面描述的有几个术语值得说明一下,下面来看一下。

2. 分析以太坊数据库

以太坊区块链中每个区块都包含许多 Merkle Patricia 字典树:

  • 状态字典树

  • 存储字典树

  • 交易字典树

  • 收款字典树

要在特定区块中引用特定的 Merkle Patricia 字典树,需要获取其根节点哈希值作为索引。

使用以下命令,获取创世区块中状态字典树、交易字典树和收款字典树的根节点哈希值:

1web3.eth.getBlock.stateRoot2web3.eth.getBlock.transactionsRoot3web3.eth.getBlock.receiptsRoot

图片 35

如果想得到最新挖出区块的根节点哈希,使用以下命令:

web3.eth.getBlock(web3.eth.blockNumber).stateRoot

获取根节点哈希值之后,需要配置网络环境。

1、安装npm、Node、Level 和 EthereumJS

使用 Node.jsLevelEthereumJS(使用 JavaScript 语言编写的以太坊虚拟机)三个程序来进行 levelDB 数据库的实验。

通过以下命令配置实验环境。

 1cd ~ 2 3sudo apt-get update 4 5sudo apt-get upgrade 6 7curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash - sudo apt-get install -y nodejs 8 9sudo apt-get install nodejs1011npm -v1213nodejs -v1415npm install levelup leveldown rlp merkle-patricia-tree --save1617git clone https://github.com/ethereumjs/ethereumjs-vm.git1819cd ethereumjs-vm2021npm install ethereumjs-account ethereumjs-util –save

实验环境配置完毕后,运行以下代码将打印出一个以太坊帐户和对应密钥的列表(存储在以太坊专用网络的状态根目录中),连接以太坊的 levelDB 数据库,进入以太坊专用网络的状态(使用区块链中区块的 stateRoot 值),然后访问以太坊专用网络上所有帐户的密钥

 1//Just importing the requirements 2 3var Trie = require('merkle-patricia-tree/secure'); 4 5var levelup = require('levelup'); 6 7var leveldown = require('leveldown'); 8 9var RLP = require;1011var assert = require;121314//Connecting to the leveldb database1516var db = levelup(leveldown('/home/timothymccallum/gethDataDir/geth/chaindata'));171819//Adding the "stateRoot" value from the block so that we can inspect the state root at that block height.2021var root = '0x8c77785e3e9171715dd34117b047dffe44575c32ede59bde39fbf5dc074f2976';222324//Creating a trie object of the merkle-patricia-tree library2526var trie = new Trie;272829//Creating a nodejs stream object so that we can access the data3031var stream = trie.createReadStream()323334//Turning on the stream (because the node js stream is set to pause by default)3536stream.on('data', function {3738 //printing out the keys of the "state trie"3940 console.log;4142});

图片 36以上代码的输出

以太坊网络中的账户只有在交易(与该特定账户相关的交易)发生时才会被加入到状态字典树中

例如,仅使用命令 “geth account new” 创建的新帐户将不会被加入到状态字典树中;如果一笔成功的交易(一笔消耗了以太坊燃料并被加入到已挖出的区块中的交易)与这个账户产生关联,那么这时该账户才会出现在状态字典树里。

这可以防止恶意攻击者不断创建新帐户,从而维持状态字典树的正常数据量

2、解码数据

以太坊在与 levelDB 数据库交互时使用了“改进的 Merkle Patricia 字典树(Modified Merkle Patricia Trie)”,扩展了字典树数据结构。

例如,改进的 Merkle Patricia 包含一种方法,该方法可以通过使用“扩展”节点来实现快速遍历。

在以太坊中,一个改进的的 Merkle Patricia trie 节点可以是:

  • 一个空字符串

  • 一个包含17个项目的数组

  • 一个包含2个项目的数组

  • 一个包含2个项目的数组

由于以太坊的字典树是根据严格的规则进行设计和构建的,因此检查它们的最佳方法是使用计算机代码进行测试。

以下示例使用了 EthereumJS,当提供特定区块的 stateRoot 以及以太坊帐户地址时,运行下面代码返回该帐户的余额。

图片 37 以下代码的输出(以太坊地址0xccc6b46fa5606826ce8c18fece6f519064e6130b的帐户余额)

 1//Mozilla Public License 2.0 2 3//As per https://github.com/ethereumjs/ethereumjs-vm/blob/master/LICENSE 4 5//Requires the following packages to run as nodejs file https://gist.github.com/tpmccallum/0e58fc4ba9061a2e634b7a877e60143a 6 7 8//Getting the requirements 910var Trie = require('merkle-patricia-tree/secure');1112var levelup = require('levelup');1314var leveldown = require('leveldown');1516var utils = require('ethereumjs-util');1718var BN = utils.BN;1920var Account = require('ethereumjs-account');212223//Connecting to the leveldb database2425var db = levelup(leveldown('/home/timothymccallum/gethDataDir/geth/chaindata'));262728//Adding the "stateRoot" value from the block so that we can inspect the state root at that block height.2930var root = '0x9369577baeb7c4e971ebe76f5d5daddba44c2aa42193248245cf686d20a73028';313233//Creating a trie object of the merkle-patricia-tree library3435var trie = new Trie;363738var address = '0xccc6b46fa5606826ce8c18fece6f519064e6130b';3940trie.get(address, function  {4142 if  return cb4344 //Using ethereumjs-account to create an instance of an account4546 var account = new Account4748 console.log('Account Address: '   address);4950 //Using ethereumjs-util to decode and present the account balance 5152 console.log('Balance: '   (new BN(account.balance)).toString

要让一个状态转换到下一个状态,交易必须是有效的。一个交易要被认可为有效,必须经过验证的过程,这个过程称之为挖矿。一群节点花费它们的计算资源来创建包含有效交易的区块,这就是挖矿。

日志

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

一个日志的实体包含:

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

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

1、移动性

如今移动设备和物联网设备无处不在,而电子商务的未来建立在安全、强大和快速的移动应用上。

图片 38image

可以说区块链在移动性上取得了巨大进步,但我们也必须承认区块链大小的不断增加是不可避免的。因此在日常移动设备上存储整个区块链是不切实际的

网络上的任何节点都可以声明自己是矿工节点,都可以试图创建和验证区块。全世界有很多矿工试图在同一时间创建和验证区块。当向区块链提交区块时,每个矿工都会提供一个数学“证明”,该证明作为一个保证:如果证明存在,区块必须有效。

交易收据

自于被包含在交易收据中的日志信息存储在头中。就像你在商店买东西时收到的收据一样,以太坊为每笔交易都产生一个收据。像你期望的那样,每个收据包含关于交易的特定信息。这些收据包含着:

  • 区块序号
  • 区块Hash
  • 交易Hash
  • 当前交易使用了的gas
  • 在当前交易执行完之后当前块使用的累计gas
  • 执行当前交易时创建的日志
  • 等等
2、速度快,不会影响安全性

以太坊网络状态的设计及其对改进的 Merkle Patricia 字典树的使用为其应用提供了更多的可能性。

在以太坊中字典树上执行的每个操作都使用了确定性的密码学哈希值。

此外,字典树根节点的密码学哈希值可以用作字典树未被篡改的证据。例如,对字典树数据的任何更改(例如增加 levelDB 数据库中的帐户余额)都将完全改变根节点哈希值。

这种密码学特性为轻客户端(不存储整个区块链的设备)带来了快速、可靠查询的可能性,比如查询账户 “0x ... 4857” 在区块高度为 “5044866” 的区块上是否有足够的资金完成此次交易等

Merkle 证明的空间复杂度与存储数据量呈对数关系。这意味着,即使整个状态字典树的大小为几千兆字节,如果一个节点从受信任的源接收一个状态,该节点只需下载一个几千字节的证明数据就能够完全确定该字典树上任何信息的有效性。”

对于要添入主链的区块,矿工必须比其他竞争对手更快地证明。通过让矿工提供数学证明的方法来验证每个区块的过程也就是所谓的“工作量证明”。

区块难度

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

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

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

图片 39

Hd代表的是难度。

找到符合难度阈值的nonce唯一方法就是使用proof-of-work算法来列举所有的可能性。找到解决方案预期时间与难度成正比—难度越高,找到nonce就越困难,因此验证一个区块也就越难,这又相应地增加了验证新块所需的时间。所以,通过调整区块难度,协议可以调整验证区块所需的时间

另一方面,如果验证时间变的越来越慢,协议就会降低难度。这样的话,验证时间自我调节以保持恒定的速率—平均每15s一个块。

3、额度限制

在以太坊白皮书中,有一个关于活期储蓄账户的概念。在这种情景下,两个用户(可能是丈夫和妻子,或着商业伙伴之间)每人每天最多只能提取帐户总余额的1%。

虽然这个想法仅仅在白皮书中“进一步发展方向”的部分中被提到,但无疑它会引起人们广泛的兴趣,因为理论上它可以作为以太坊底层协议的一部分(而不是被当作为第二层协议或者是第三方钱包的一部分)。

UTXO对区块链数据是不可见的,实际上比特币区块链并不存储用户的账户余额。因此,比特币的底层协议不太可能实现任何类型的每日额度限制

验证新区块的矿工会被奖励一定数量的价值,以激励他们完成此项工作。什么样的价值?以太坊区块链使用原生数字代币“Ether”。每次矿工证明了一个区块,新的Ether代币就会产生并给予矿工奖励。

交易执行

我们已经到了以太坊协议最复杂的部分:交易的执行。假设你发送了一笔交易给以太坊网络处理,将以太坊状态转换成包含你的交易这个过程到底发生了什么?

图片 40

首先,为了可以被执行所有的交易必须都要符合最基础的一系列要求,包括:

  • 交易必须是正确格式化的RLP。”RLP”代表Recursive Length Prefix,它是一种数据格式,用来编码二进制数据嵌套数组。以太坊就是使用RLP格式序列化对象。
  • 有效的交易签名。
  • 有效的交易序号。回忆一下账户中的nonce就是从此账户发送出去交易的计数。如果有效,那么交易序号一定等于发送账户中的nonce。
  • 交易的gas limit 一定要等于或者大于交易使用的intrinsic gasintrinsic gas包括:——-1.执行交易预订费用为21,000gas——-2.随交易发送的数据的gas费用(每字节数据或代码为0的费用为4gas,每个非零字节的数据或代码费用为68gas)——-3.如果交易是合约创建交易,还需要额外的32,000gas

图片 41

  • 发送账户余额必须有足够的Ether来支付”前期”gas费用。前期gas费用的计算比较简单:首先,交易的gas limit乘以交易的gas价格得到最大的gas费用。然后,这个最大gas费用被加到从发送方传送给接收方的总值。

图片 42

如何交易符合上面所说的所有要求,那么我们进行下面步骤。

第一步,我们从发送者的余额中扣除执行的前期费用,并为当前交易将发送者账户中的nonce增加1。此时,我们可以计算剩余的gas,将交易的总gas减去使用的intrinsic gas

图片 43

第二步,开始执行交易。在交易执行的整个过程中,以太坊保持跟踪“子状态”。子状态是记录在交易中生成的信息的一种方式,当交易完成时会立即需要这些信息。具体来说,它包含:

  • 自毁集:在交易完成之后会被丢弃的账户集
  • 日志系列:虚拟机的代码执行的归档和可检索的检查点
  • 退款余额:交易完成之后需要退还给发送账户的总额。回忆一下我们之前提到的以太坊中的存储需要付费,发送者要是清理了内存就会有退款。以太坊使用退款计数进行跟踪退款余额。退款计数从0开始并且每当合约删除了一些存储中的东西都会进行增加。

第三步,交易所需的各种计算开始被处理。

当交易所需的步骤全部处理完成,并假设没有无效状态,通过确定退还给发送者的未使用的gas量,最终的状态也被确定。除了未使用的gas,发送者还会得到上面所说的“退款余额”中退还的一些津贴。

一旦发送者得到退款之后:

  • gas的Ether就会矿工
  • 交易使用的gas会被添加到区块的gas计数中(计数一直记录当前区块中所有交易使用的gas总量,这对于验证区块时是非常有用的)
  • 所有在自毁集中的账户都会被删除

最后,我们就有了一个新的状态以及交易创建的一系列日志。

现在我们已经介绍了交易执行的基本知识,让我们再看看合约创建交易和消息通信的一些区别。

4、消费者信心

相信随着区块链开发者的不断努力,我们会目睹轻量级客户端的快速发展,目睹可以与区块链技术交互的安全、强大和快速的移动应用程序的大规模落地

图片 44image

在电子商务领域想要实现区块链技术的落地必须提高速度安全性可用性。通过巧妙的设计提供优异的可用性,安全性和性能,这将提高消费者信心同时增加大众的采用率

数据的存储机制也是当下区块链应用落地面临的一大问题,它决定了区块链的运行效率。

只有解决有关区块链应用落地的痛点,区块链才能真正走进人们的生活,给人们带来便利!

看到这里,相信你对以太坊的数据存储机制已有了深入的了解。

本文转载自《0.166666667小时,教会你深挖以太坊数据层》,作者 | Vasa 企业家、TowardsBlockChain 联合创始人,编译 | kou、Guoxi,版权属于原作者。

你可能会想:用什么来保证大家都在同一条链上?我们怎么能肯定不会存在一部分矿工决定创建自己的链?

合约创建(Contract creation)

回忆一下在以太坊中,有两种账户类型:合约账户和外部拥有账户。当我们说一个交易是“合约创建”,是指交易的目的是创建一个新的合约账户。

为了创建一个新的合约账户,我们使用一个特殊的公式来声明新账户的地址。然后我们使用下面的方法来初始化一个账户:

  • 设置nonce为0
  • 如果发送者通过交易发送了一定量的Ether作为value,那么设置账户的余额为value
  • 将存储设置为0
  • 设置合约的codeHash为一个空字符串的Hash值

一旦我们完成了账户的初始化,使用交易发送过来的init code(查看”交易和信息”章节来复习一下init code),实际上就创造了一个账户。init code的执行过程是各种各样的。取决于合约的构造器,可能是更新账户的存储,也可能是创建另一个合约账户,或者发起另一个消息通信等等。

当初始化合约的代码被执行之后,会使用gas。交易不允许使用的gas超过剩余gas。如果它使用的gas超过剩余gas,那么就会发生gas不足异常并退出。如果一个交易由于gas不足异常而退出,那么状态会立刻恢复到交易前的一个点。发送者也不会获得在gas用完之前所花费的gas

不过,如果发送者随着交易发送了Ether,即使合约创建失败Ether也会被退回来。

如果初始化代码成功的执行完成,最后的合约创建的花费会被支付。这些是存储成本,与创建的合约代码大小成正比(再一次,没有免费的午餐)。如果没有足够的剩余gas来支付最后的花费,那么交易就会再次宣布gas不足异常并中断退出。

如果所有的都正常进行没有任何异常出现,那么任何剩余的未使用gas都会被退回给原始的交易发送者,现在改变的状态才被允许永久保存。

在上文也提到,我们把区块链定义为一个具有共享状态的交易单例机。使用此定义,我们可以理解,正确的当前状态是一个单一的全球事实,每个人都必须接受。如有多种状态会毁掉整个系统,因为它不可能就哪个状态才是正确状态方面达成一致。如果链有多条,你可能在一条链上有10个代币,另外一条上有20个代币,还有一条上有40个代币。在这种情况下,无法确定那条链是最“有效”的。

消息通信(Message calls)

消息通信的执行与合约创建比较类似,只不过有一点点区别。

由于没有新账户被创建,所以消息通信的执行不包含任何的init code。不过,它可以包含输入数据,如果交易发送者提供了此数据的话。一旦执行,消息通信同样会有一个额外的组件来包含输出数据,如果后续执行需要此数据的话就组件就会被使用。

就像合约创建一样,如果消息通信执行退出是因为gas不足或交易无效(例如栈溢出,无效跳转目的地或无效指令),那么已使用的gas是不会被退回给原始触发者的。相反,所有剩余的未使用gas也会被消耗掉,并且状态会被立刻重置为余额转移之前的那个点。

没有任何方法停止或恢复交易的执行而不让系统消耗你提供的所有gas,直到最新的以太坊更新。例如,假设你编写了一个合约,当调用者没有授权来执行这些交易的时候抛出一个错误。在以太坊的前一个版本中,剩余的gas也会被消耗掉,并且没有任何gas退回给发送者。但是拜占庭更新包括了一个新的“恢复”代码,允许合约停止执行并且恢复状态改变而不消耗剩余的gas,此代码还拥有返回交易失败原因的能力。如果一个交易是由于恢复而退出,那么未使用的gas就会被返回给发送者。

不管什么时候,只要有多条路径,就会产生“分叉”。我们通常希望避免分叉,因为它们会破坏系统并强迫人们选择相信那一条链。

执行模式

到目前为止,我们了解了从开始到结束执行的交易必须经历的一系列的步骤。现在,我们来看看交易究竟是如何在虚拟机中执行的。

协议实际操作交易处理的部分是以太坊自己的虚拟机,称之为以太坊虚拟机

  • 像之前定义的那样,EVM是图灵完备虚拟机器。EVM存在而典型图灵完备机器不存在的唯一限制就是EVM本质上是被gas束缚。因此,可以完成的计算总量本质上是被提供的gas总量限制的。

图片 45

  • 此外,EVM具有基于堆栈的架构。堆栈机器 就是使用后进先出来保存临时值的计算机。
  • EVM中每个堆栈项的大小为256位,堆栈有一个最大的大小,为1024位。
  • EVM有内存,项目按照可寻址字节数组来存储。内存是易失性的,也就是数据是不持久的。
  • EVM也有一个存储器。不像内存,存储器是非易失性的,并作为系统状态的一部分进行维护。EVM分开保存程序代码,在虚拟ROM 中只能通过特殊指令来访问。这样的话,EVM就与典型的冯·诺依曼架构 不同,此架构将程序的代码存储在内存或存储器中。

图片 46

  • EVM同样有属于它自己的语言:“EVM字节码”,当一个程序员比如你或我写一个在以太坊上运行的智能合约时,我们通常都是用高级语言例如Solidity来编写代码。然后我们可以将它编译成EVM可以理解的EVM字节码。

好了,现在来说执行。

在执行特定的计算之前,处理器会确定下面所说的信息是有效和是否可获取:

  • 系统状态
  • 用于计算的剩余gas
  • 拥有执行代码的账户地址
  • 原始触发此次执行的交易发送者的地址
  • 触发代码执行的账户地址(可能与原始发送者不同)
  • 触发此次执行的交易gas价格
  • 此次执行的输入数据
  • Value作为当前执行的一部分传递给该账户
  • 待执行的机器码
  • 当前区块的区块头
  • 当前消息通信或合约创建堆栈的深度

执行刚开始时,内存和堆栈都是空的,程序计数器为0。

PC: 0 STACK: [] MEM: [], STORAGE: {}

然后EVM开始递归的执行交易,为每个循环计算系统状态机器状态。系统状态也就是以太坊的全局状态(global state)。机器状态包含:

  • 可获取的gas
  • 程序计数器
  • 内存的内容
  • 内存中字的活跃数
  • 堆栈的内容

堆栈中的项从系列的最左边被删除或者添加。

每个循环,剩余的gas都会被减少相应的量,程序计数器也会增加。在每个循环的结束,都有三种可能性:

  • 机器到达异常状态(例如 gas不足,无效指令,堆栈项不足,堆栈项会溢出1024,无效的JUMP/JUMPI目的地等等)因此停止,并丢弃任何的更改
  • 进入后续处理下一个循环
  • 机器到达了受控停止(到达执行过程的终点)

假设执行没有遇到异常状态,达到一个“可控的”或正常的停止,机器就会产生一个合成状态,执行之后的剩余gas、产生的子状态、以及组合输出。

呼。我们终于过了一遍以太坊最难的部分了。如果你不能完全理解这个部分,也没关系。除非你在理解非常深层次的东西,否则你真的没有必要去理解执行的每个细节。

图片 47

一个块是如何完成的?

最后,让我们看看一个包含许多交易的块是如何完成的。

当我们说“完成”,取决于此块是新的还是已存在的,可以指两个不同的事情。如果是个新块,就是指挖这个块所需的处理。如果是已存在的块,就是指验证此块的处理。不论哪种情况,一个块的“完成”都有4个要求:

  1. 验证(或者,如果是挖矿的话,就是确定)ommers在区块头中的每个ommer都必须是有效的头并且必须在当前块的6代之内
  2. 验证(或者,如果是挖矿的话,就是确定)交易区块中的gasUsed数量必须与区块中所列交易使用的累积gas量相等。(回忆一下,当执行一个交易的时候,我们会跟踪区块的gas计数器,也就跟踪了区块中所有交易使用的gas总数量)
  3. 申请奖励受益人的地址会因为挖矿而获得5Ether(在以太坊EIP-649 提案中,5ETH很快将会被减少为3ETH)。另外,对于每个ommer,当前块的受益人会获得额外的1/32当前块奖励金的奖励。最近,每个ommer区块的受益人能够得到一定量的奖励(有个特殊公式可以进行计算)。
  4. 校验(或者,如果是挖矿的话,就是计算一个有效的)状态和nonce确保所有的交易和改变的结果状态都被应用了,然后在区块奖励被应用于最终交易结果状态之后定义一个新块为状态。通过检查最终状态与存储在头中的状态树来进行验证。

为了决定那条路径是最有效的,并且防止多条链产生,以太坊使用称为“GHOST协议”的机制。“GHOST”=“Greedy Heaviest Observed Subtree”

工作量证明挖矿

在“区块”这个章节简短的说明了一下区块难度这个概念。给予区块难度意义的算法叫做工作量证明。

以太坊的工作量证明算法称之为“Ethash” (之前叫做Dagger-Hashimoto)。

算法正式定义为:

图片 48m代表的是mixHashn代表的是nonceHn代表的是新区块的头(不包含需要计算的noncemixHash),Hn是区块头的nonce,d是DAG ,就是一个大数据集。

在”区块”章节,我们讨论了存在于区块头中的多项。其中两项叫做mixHashnonce。也许你会回忆起:

  • mixHash:一个Hash值,当与nonce组合时,证明此区块已经执行了足够的计算
  • nonce:一个Hash值,当与mixHash组合时,证明此区块已经执行了足够的计算

PoW函数就是用来估算这两项的。mixHashnonce到底是如何使用PoW函数来计算出来的有点复杂,如果深入了解的话,我们可以另写一篇文章来讲解了。但是在一个高层面上,它大致就是这样计算的:会为每个区块计算一个”种子”。每个“时期”的种子都不一样,每个时期是30,000个区块长度。对于第一时期,种子就是32位0的hash值。对于后续的每个时期,种子就是前一个种子hash值的hash值。使用这个种子,节点可以计算一个伪随机“缓存”。

这个缓存是非常有用的,因为它可以使“轻节点”的概念变成现实,轻节点概念在这篇文章的前面讨论过。轻节点的目的就是让某个节点有能力高效的校验交易而用不着存储整个区块链的数据集。一个轻节点可以仅基于缓存来校验一个交易的有效性,因为缓存可以重新生成需要校验的特定块。

使用这个缓存,节点可以生成DAG“数据集”,数据集中的每项取决于缓存中少量伪随机选择项。为了成为矿工,你需要要生成全数据集,所有全客户端和矿工都保存这个数据集,并且这个数据集随着时间线性增长。

然后矿工可以随机抽取数据集中的部分并将它们放入一个数学函数中Hash出一个”mixHash”。矿工会重复生成mixHash直到输出的值小于想要的目标值nonce。当输出的值符合这个条件的时候,nonce就被认为是有效的,然后区块就被添加到链中。

简单来说,GHOST协议说我们必须挑选耗费最多算力的那条路径。决定那条路径的一个方法是使用最新区块的区块编号,它表示当前该路径的全部区块总数。区块编号越高,路径就越长,抵达“叶区块”所需的挖矿工作量就越大。使用这种推理方法,我们能够就当前状态的规范版本达成一致。

挖矿作为安全机制

总的来说,PoW的目的就是以加密安全的方式证明生成的一些输出(也就是nonce)是经过了一定量的计算的。因为除了列举所有的可能性,没有更好的其他方法来找到一个低于要求阈值的nonce。重复应用Hash函数的输出均匀分布,所以我们可以确保,在平均值上,找到满足要求的nonce所需时间取决于难度阈值。难度系数越大,所需时间越长。这样的话,PoW算法就给予难度这个概念的意义了:用来加强区块链的安全

我们所说的区块链的安全又是什么意思?这非常简单:我们想要创造一个每个人都信任的区块链。像我们之前在这篇文章中讨论的那样,如果存在超过1条以上的链,用户的信任就会消失,因为他们没有能力合理的确认哪条链才是“有效的”。为了让一群用户接受存储在区块链中的潜在状态,我们需要有一群人信任的一个权威区块链。

这完完全全就是Pow算法所做的事情:它确保特定的区块链直到未来都一直保持着权威性,让攻击者创造一个新区块来重写某个历史部分(例如清除一个交易或者创建一个假的交易)或者保持一个分叉变得非常困难。为了首先让他们的区块被验证,攻击者需要总是比网络上的其他人要更快的解决掉nonce问题,这样网络就会相信他们的链是最重的链(基于我们之前提到的GHOST协议原则)。除非攻击者拥有超过一半的网络挖矿能力(这种场景也被称为大多数51%攻击 ),要不然这基本上是不可能的。

图片 49

图片 50

挖矿作为财富分配机制

除了提供一个安全的区块链,PoW同样也是分配财富给那些为提供这个安全而花费自己计算力的人的一种方法。回忆一下,一个矿工挖出一个区块的时候会获得奖励,包括:

  • 为“获胜”区块提供的5 ether静态区块奖励(马上就会变成3 ether )
  • 区块中的交易在区块内所消耗的gas
  • 纳入ommers作为区块的一部分的额外奖励

为了保证PoW共识算法机制对安全和财富分配的使用是长期可持续的,以太坊努力灌输这两个特性:

  • 尽可能的让更多的人可访问。换句话说,人们不需要特殊的或者与众不同的硬件来运行这个算法。这样做的目的是为了让财富分配模式变的尽可能的开放,以便任何人都可以提供一些算力而获得Ether作为回报。
  • 降低任何单个节点能够创造与其不成比例的利润可能性。任何可以创造不成比例的利润的节点拥有比较大的影响力来决定权威区块链。这是件麻烦的事情,因为这降低了网络的安全性。

在区块链网络中,一个与上面两个特性有关的一个问题是PoW算法是一个SHA256哈希函数。这种函数的缺点就是它使用特殊的硬件(也被称之为ASCIs)可以更加快速高效的解决nonce问题。

为了减轻这个问题,以太坊选择让PoW算法 提高内存级别难度。意思是此算法被设计为计算出要求的nonce需要大量的内存和带宽。大量内存的需求让电脑平行的使用内存同时计算多个nonce变得极其困难,高带宽的需求让即使是超级电脑同时计算多个nonce也变得十分艰难。这种方式降低了中心化的风险,并为正在进行验证的几点提供了更加公平的竞争环境。

有一件值得注意的事情是以太坊正在从PoW共识机制渐渐转换为一个叫做“权益证明”。这就是一个比较野心的话题了,我们希望可以在未来的文章中探索这个话题。

现在你已经多少了解一些区块链的大概,让我们更加深入了解以太坊系统的主要组件:

总结

呼! 你终于坚持到最后了。我希望如此?

这篇文章中有很多的地方需要消化。如果需要你阅读好几遍才能理解怎么回事,这完全正常。我个人重复阅读了好几次以太坊黄皮书,白皮书,以及代码的不同部分才渐渐明白是怎么回事。

无论如何,我希望你觉得这篇文章对你有帮助。如果你发现了任何的错误或失误,我很乐意你给我写个私人消息或者直接在评论区评论(我保证我会查看所有评论)。

记住,我是个人类,我会犯错误。为了社区的利益,我花时间免费写了这篇文章。所以请你在反馈时不要带着没必要的攻击性,尽量是建设性的反馈。

本文标题:[以太坊的工作原理](https://lilymoana.github.io/ethereum_theory.html)文章作者:[XU LI](https://lilymoana.github.io/)发布时间:2017年09月28日 - 12时09分最后更新:2017年10月10日 - 21时17分原始链接: https://lilymoana.github.io/ethereum_theory.html许可协议: "署名-非商用-相同方式共享 3.0" 许可协议: http://creativecommons.org/licenses/by-nc-sa/3.0/cn/转载请保留原文链接及作者。

l 账户

l 状态

l gas和费用

l 交易

l 区块

l 交易执行

l 挖矿

l PoW

开始前要注意一个事情:本文说的X的“哈希”时,通常是指以太坊使用的KECCAK-256哈希。

账户

以太坊的全球“共享状态”是由很多小的对象组成,这些账户通过消息传递框架实现彼此交互。每个账户都有一个与之关联的状态以及一个20字节的地址。以太坊中的地址是160位比特的标识符,用于标识任何账户。

有两种类型的账户:

l 外部账户,由私钥控制,没有与之关联的代码。

l 合约账户,由合约代码控制,有与之关联的代码。

图片 51

外部账户Vs.合约账户

理解外部账户和合约账户之间的根本区别是非常重要的。通过创建及使用其私钥签名一个交易,外部账户能够给其他外部账户或其他合约账户发送消息。两个外部账户之间的消息只是简单的价值传输。但从外部账户发送到合约账户的消息可以激活合约账户的代码,允许它执行各种操作(例如,转移代币、写入内部存储、发新币、执行计算、创建新合约等。)

与外部账户不同,合约账户无法自行启动新的交易。相反,合约账户仅能够通过响应其他它们收到的交易来触发自身的交易。比如从外部账户或从其他的合约账户的交易来触发。我们会在“交易和消息”章节中了解到更多合约到合约的请求。

图片 52

因此,任何以太坊区块链上发生的操作始终由外部账户所触发的交易来启动。

图片 53

账户状态

账户状态由四个部分组成,无论账户类型是什么,它们都存在:

Nonce:如果该账户是外部账户,这个数代表从这个账户地址发出来的交易数。如果该账户是合约账户,则该nonce是该账户创建的合约数。

余额:该地址拥有的Wei数。每个Ether有1e 18Wei。

StorageRoot:Merkle Patricia树的根节点的哈希值(后续会解释Merkle tree)。Merkle tree对该账户的存储内容的哈希进行编码,默认情况下为空。

CodeHash:该账户EVM代码的哈希。对合约账户而言,这是被哈希后并存储为CodeHash的代码。对于外部账户而言,codehash字段是空字符串的哈希。

图片 54

世界状态

我们知道以太坊的全球状态包括账户地址和账户状态之间的映射。该映射存储在Merkle Patricia tree树的数据结构中。

Merkle树是一种由一组节点组成的二叉树,其中:

l 树底部的大量叶节点包含底层数据

l 一组中间节点,其中每个节点都是两个子节点的哈希

l 一个根节点,也是由两个子节点的哈希形成,表示树的顶部

图片 55

树底部的数据是通过拆分数据产生,这些数据我们希望存储进入块中,然后把块拆分进入桶中,之后取每个桶的哈希并重复相同的过程,直到剩余的哈希总数变为只有一个:根哈希。

图片 56

Merkle树要求每个存储其中的值都有一个键。从树的根节点开始,键应该告诉你要遵循哪个子节点,以获取相应的值,值是存储在叶节点上的。在以太坊的案例中,状态树的键/值映射是在地址和它们相关的账户之间的,包括了每个账户的余额、nonce、codeHash、StorageRoot(其中StorageRoot本身就是一颗树)。

图片 57

相同的trie结构也被用于存储交易和收据。更具体来说,每个区块都有一个“块头”,块头存储三种不同Merkle trie结构的根节点哈希,包括:

l 状态trie

l 交易trie

l 收据trie

图片 58

把所有这些信息有效地存入Merkle tries的能力在以太坊中非常有用,因为我们有“轻客户端”和“轻节点”。请记住,区块链由一堆节点维护。广义上讲,有两类节点:全节点和轻节点。

完整节点通过下载整个链数据来同步区块链,从创世区块到当前区块,执行其中包含的所有交易。通常,矿工存储完整到归档节点,因为它们要去挖矿必须执行该操作。无须执行任一操作也可以下载完整节点。无论如何,任何全节点包括所有链。

但,除非一个节点有必要执行每个交易或为方便查询历史数据,否则,没有必要存储整条链的数据。这也是轻节点概念的由来。无须下载和存储完整链的数据以及执行所有交易,轻节点仅下载链头,从创世区块到当前区块的块头,而无须执行任何交易或检索任何相关的状态。由于轻节点可以访问区块头,区块头包含了三种tries的哈希,它们依然能够轻易生成和接收可验证的答案,如关于交易、事件、余额等。

这样做能行的原因是Merkle树中的哈希是向上传播的——如果一个恶意用户试图把虚假交易置入Merkle树的底部,这样的改变会导致上面节点的哈希发生变化,然后会继续改变上面节点的哈希,如此传递下去,最终改变了树根的哈希。

图片 59

想要验证数据的任何节点都能使用“Merkle证明”来达成目的。Merkle证明包含:

l 要证明的一堆数据和它的哈希

l 树的根哈希

l “分支”(所有伙伴哈希从块到根的路径顺延而上)

图片 60

所有读取证明的人都能验证分支的哈希是沿着树向上一致的,因此,给定的区块实际上是在树中的某个位置上。

总之,使用Merkle Patricia树的好处是该结构的根节点在加密方面依赖于存储在树上的数据,因此根节点哈希能够用于该数据的安全标识。既然区块头包含状态、交易以及收据树三者的根哈希,因此,任何节点都可以验证一小部分的以太坊状态,而无需存储所有状态,完整节点可能会非常大。

--未完待续---


风险警示:蓝狐笔记所有文章都不构成投资推荐,投资有风险,投资应该考虑个人风险承受能力,建议对项目进行深入考察,慎重做好自己的投资决策。

TAG标签:
版权声明:本文由vnsr威尼斯城官网发布于威尼斯科技,转载请注明出处:以太坊的工作原理,深度知识