阅读 454

以太坊架构和组成


这篇文章的内容是《以太坊技术详解与实战》第二章抽取出来总结而成的,大体上能一窥以太坊的全貌。

一、以太坊整体架构

以太坊的整体架构为三层:底层服务、核心层、顶层应用,如图:


以太坊架构


二、区块

所谓的区块,其实可以定义为记录一段时间内发生的交易和状态结果的数据结构,是对当前账本状态的一次共识。

区块主要由区块头、交易列表和叔区块头三部分组成:

区块头

区块头包含:父块的散列值( Prev Hash )、叔区块的散列值( Uncles Hash )、状态树根散列值( stateRoot ) 、交易树根散列值( Transaction Root ) 、收据树根散列值( Receipt Root )、时间戳( Times tamp )、随机数( Nonce )等

以太坊区块链上区块数据结构相对比特币的一个重大改变就是保存了三棵Merkle树根,分别是状态树、交易树和收据树。

交易列表

交易列表是由矿工从交易池中选择收入区块中的一系列交易。

叔区块

不在主链上的且被主链上的区块通过Uncles 字段收留进区块链的孤块叫做“叔区块” 。



以太坊区块结构

三、账户

账户以地址为索引,地址由公钥衍生而来,取公钥的最后20 字节

在以太坊系统中存在两种类型的账户,分别是外部账户和合约账户。外部账户存储以太币余额状态,而合约账户除了余额还有智能合约及其变量的状态。

外部账户

外部账户( Externally Owned Account, EOA )由私钥来控制,是由用户实际控制的账户。存储以太币余额状态

合约账户

合约账户是一个包含合约代码的账户。合约账户不是由私钥文件直接控制,而是由合约代码控制。合约账户的地址是由合约创建时合约创建者的地址,以及该地址发出的交易共同计算得出的。

与外部账户相比,合约账户除了余额还有智能合约及其变量的状态

私钥和公钥

以太坊中每个外部账户都由一对密匙定义,即一个私钥和一个公钥。

目前常见的私钥有三种形态:

1 ) Private key

Private key 就是一份随机生成的256 位二进制数字。用户甚至可以用纸笔来随机地生成一个私钥,即随机写下一串256 位的仅包含“0”或“ 1”的字符串。该256 位二进制数字就是私钥最初始的状态。

2 )Keystore & Password

而在以太坊官方钱包中,私钥和公钥将会以加密的方式保存一份JSON 文件,存储在key store 子目录下。这份JSON 文件就是Keys tore ,所以用户需要同时备份Keys tore和对应的Password (创建钱包时设置的密码) 。

3 )Memonic code

Memonic code 是由BIP 39 方案提出的,目的是随机生成12 ~ 24 个比较容易记住的单词,该单词序列通过PBKDF2 与HMAC-SHA512 函数创建出随机种子,该种子通过BIP-0032 提案的方式生成确定性钱包

四、数据结构与存储

区块、交易等数据最终都是存储在Level DB数据库中。Level DB数据库是一个键值对( key-value )数据库, key一般与散列相关,value则是存储内容的RLP编码。

数据组织形式

以太坊使用了MPT树(Merkle Patricia Trie),作为数据组织形式,用来组织管理用户的账户状态、交易信息等重要数据。MPT是一种加密认证的数据结构,它融合了Merkle树和Trie树(前缀树)两种数据类型的优点。

Merkle树

Merkle树是一种树形数据结构,可以是二叉树,也可以是多叉树。它由一组叶节点、一组中间节点和一个根节点构成。最下面的叶节点包含基础数据,每个中间节点是它的子节点的散列,根节点是它的子节点的散列,代表了Merkle树的根部。

创建Merkle树的目的是允许区块的数据可以零散地传送;节点可以从一个节点下载区块头,从另外的源下载与其相关的树的其他部分,而依然能够确认所有的数据都是正确的。

Merkle树可以用来存储所有键值对

Merkle树具有下列特性:

1.每个数据集对应一个唯一合法的根散列值。

2.很容易更新、添加或者删除树节点,以及生成新的根散列值。

3.不改变根散列值的话就没有办法修改树的任何部分,所以如果根散列值被包括在签名的文档或有效区块中,就可以保证这棵树的正确性。

4.任何人可以只提供一个到特定节点的分支,并通过密码学方法证明拥有对应内容的节点确实在树里。



Merkle树

Trie树

key代表的是从树根到对应value的一条路径。即从根节点开始, key 中的每个字符(从前到后)都代表着从根节点出发寻找相应value 所要经过的子节点。value 存储在叶节点中,是每条路径的最终节点。

MPT树

结合merkle和Trie,并做了以下改进:

1.为了保证树的加密安全,每个节点通过它的散列值被引用

2.对于存储在Leve IDB 数据库中的非叶节点,key代表着节点的RLP编码的SHA3散列值, value是节点的RLP编码。

3.引入了很多节点类型来提高效率:

3.1空节点:简单的表示空,在代码中就是一个空串

3.2叶节点:键值对的一个列表,其中key是一种特殊的十六进制编码, value是RLP编码。

3.3扩展节点:键值对的列表,但是这里的value 是其他节点的散列值,通过这个散列值可以链接到其他节点。

3.4分支节点:一个长度为17 的列表。MPT 中的key 被编码成一种特殊的十六进制的表示,再加上最后的value ,前16 个元素对应key 中的16 个可能的十六进制字符,如果有一个键值对在这个分支节点终止,则最后一个元素代表一个值,即分支节点既可以是搜索路径的终止,也可以是路径的中间节点。

4.用于对key 进行编码的特殊十六进制前缀编码( HP )


状态树树根

三棵树

如图


以太坊区块头结构

1.状态树

状态树包含一个键值映射,其中键是账户地址,值是账户内容,主要是{ nonce, balance,codeHash, storageRoot } 。nonce 是账户交易的序数, balance 是账户余额, codeHash 是代码的散列值, storageRoot 是另一棵树的根节点。状态树代表访问区块后的整个状态。

以太坊是一个以账户为基础的区块链应用平台,账户的状态不是直接存储在每个区块中,所有的账户状态都是以“状态数据”的形式存储在以太坊的节点中。

2.交易树

每个区块都有一棵独立的交易树。

区块中交易的顺序主要由“矿工”决定,在这个块被挖出前这些数据都是未知的。不过“矿工” 一般会根据交易的GasPrice 和nonce 对交易进行排序。首先会将交易列表中的交易划分到各个发送账户,每个账户的交易根据这些交易的nonce 来排序。每个账户的交易排序完成后,再通过比较每个账户的第一条交易,选出最高价格的交易,这些是通过一个堆(heap)来实现的

在交易树包含的键值对中,其中每个键是交易的编号,值是交易内容

3.收据树

每个区块都有自己的收据树,收据树不需要更新,收据树代表每笔交易相应的收据。

交易的收据是一个RLP 编码的数据结构:[medstate, Gas_ used, logbloom,logs ] 。其中, medstate 是交易处理后树根的状态; Gas_used 是交易处理后Gas 的使用量;logs 是表格[ address, [topicl, topic2 ,…], data ]元素的列表,表格由交易执行期间调用的操作码LOGO …LOG4 生成(包含主调用和子调用), address 是生成日志的合约地址, topicn是最多4 个32 字节的值, data 是任意字节大小的数组; logbloom 是交易中所有logs 的address 和topic 组成的布隆过滤器.

数据库支持一一Level DB

LeveI DB 是Goog le 实现的一个非常高效的键值对数据库,其中键值都是二进制的,目前能够支持十亿级别的数据量,在这个数据量下还有着非常高的性能。

以太坊中共有三个Leve IDB 数据库,分别是BlockDB 、StateDB 和ExtrasDB。

BlockDB保存了块的主体内容,包括块头和交易;StateDB保存了账户的状态数据;ExtrasDB保存了收据信息和其他辅助信息。

五、共识机制

共识机制是区块链事务达成分布式共识的算法。由于点对点网络下存在着或高或低的网络延迟,所以各个节点接收到的事务的先后顺序可能不一样,因此区块链系统需要设计一种机制让节点对在差不多时间内发生的事务的先后顺序实现共识,这就是共识机制。

Pow

PoW 即通过工作结果来证明你完成了相应的工作

哈希函数的特征:

1.免碰撞,即不存在输入值不同,经过散列变换,而散列值相同的情况。

2.隐匿性,即给定一个散列值,想要反向逆推出输入值,在计算上是不可行的。

3.不存在比穷举更好的方法,以使得散列值落在特定的范围。

POW算法原理:节点通过不断地更换随机数来探寻合适的哈希值,当节点最先计算出合适的哈希值,它所打包的块如果通过其他共识节点的验证,则会被加入到区块链中。

Ethash (以太坊专门的POW算法 )

为了解决挖矿中心化问题,专门设计了一个能抵制ASIC、轻客户端可快速验证的PoW 算法

算法流程:

1.对于每一个区块,都能通过扫描区块头的方式计算出一个种子( seed ),该种子只与当前区块有关。

2.使用种子能产生一个16MB 的伪随机缓存,轻客户端会存储缓存。

3.基于缓存再生成一个1GB 的数据集,称其为DAG 。数据集中的每一个元素都只依赖于缓存中的某几个元素,也就是说,只要有缓存,就可以快速地计算出DAG 中指定位置的元素。挖矿者存储数据集,数据集随时间线性增长。

4.挖矿可以概括为“矿工”从DAG 中随机选择元素并对其进行散列的过程, DAG 也可以理解为一个完整的搜索空间,挖矿的过程就是从DAG 中随机选择元素(类似比特币挖矿中试探合适nonce 的过程)进行散列运算。

5.验证者只需要花费少量的内存存储缓存就可以了,因为验证者能够基于缓存计算得到DAG 中自己需要的指定位置的元素,然后验证这些指定元素的散列是不是小于某个散列值,也就是验证“矿工”的工作是否符合要求。

Ethash 算法的特点是挖矿的效率基本与CPU 无关,而与内存大小、带宽正相关,目的是去除专用硬件的优势,抵抗ASIC 。

POS

PoS 即基于网络参与者目前所持有的数字货币的数量和时间进行利益分配,是一种对货币所有权的证明

共识算法类型:

基于链的PoS 和BFT (Byzantine Fault Tolerant ,拜占庭容错)风格的PoS 。

在基于链的PoS 中,该算法在每个时隙内伪随机地从验证者集合中选择一个验证者(比如,设置每l0s 一个周期,每个周期都是一个时隙),给予验证者创建新区块的权利,但是验证者要确保该块指向最多的块(指向的上一个块通常是最长链的最后一个块) 。因此,随着时间的推移,大多数的块都收敛到一条链上。

在BFT 风格的PoS 中,分配给验证者相对的权利,让他们有权提出块并且给被提出的块投票,从而决定哪个块是新块,并在每一轮选出一个新块加入区块链。在每一轮中,每一个验证者都为某一特定的块进行“投票”,最后所有在线和诚实的验证者都将“商量”被给定的块是否可以添加到区块链中,并且意见不能改变。

六、交易

以太坊的交易主要是指一条外部账户发送到区块链上另一账户的消息的签名数据包,其主要包含发送者的签名、接收者的地址以及发送者转移给接收者的以太币数量等内容。

交易是以太坊整体架构中的重要部分,它将以太坊的账户连接起来,起到价值的传递作用。

交易内容

from :交易发送者的地址,必填;

to :交易接收者的地址,如果为空则意味这是一个创建智能合约的交易;

value :发送者要转移给接收者的以太币数量;

data (也写作input):存在的数据字段,如果存在,则是表明该交易是一个创建或者调用智能合约交易;

Gas Limit (也写作Gas, StartGas ):表示这个交易允许消耗的最大Gas 数量;

GasPrice :表示发送者愿意支付给矿工的Gas 价格;

nonce :用来区别同一用户发出的不同交易的标记;

hash :由以上信息生成的散列值(哈希值),作为交易的ID;

r 、s 、v :交易签名的三个部分,由发送者的私钥对交易hash 进行签名生成。

交易费用

为了防止用户在区块链公有链中发送太多的无意义交易,浪费矿工的计算资源,要求交易的发送方为每笔交易付出一定的代价,便是交易费用。

由于比特币中只存在转账交易,每笔交易所需的计算开销大体一致,因此每笔交易的发送者会以比特币的形式,付出相对固定的手续费。而以太坊中引入了智能合约,涉及智能合约创建和调用的交易所消耗的计算差别巨大,因此引入了相对复杂的Gas 、Gas Price 对交易所需的手续费进行定价。

1.Gas

Gas (汽油)是用来衡量一笔交易所消耗的计算资源的基本单位。当以太坊节点执行一笔交易所需的计算步骤越多、越复杂,那么就会说这笔交易消耗的Gas 越多。

2.Gas Price

Gas Price ( Gas 价格)是一单位Gas 所需的手续费(以太币,即Ether )。矿工会对接受到的交易按照Gas Price 或者按照Gas * Gas Price 从大到小进行排序,以便决定哪个交易会先纳入到区块中。当以太坊公有链上某个时段交易量激增的情况下,为了尽早让矿工接受一笔交易,交易发送者可以提高这笔交易的Gas Price ,以激励矿工

3.Gas Limit

有两种:

对于单个交易, Gas Limit (有时也会称作StartGas )表示交易发送者愿意为这笔交易执行所支付的最大Gas 数量,需要发送者在发送交易时设置。可以保护用户免受错误代码影响以致消耗过多的交易费。GasPrice * Gas Limit 表示用户愿意为一笔交易支付的最高金额。

而对于区块来说, Gas Limit 是单个区块所允许包含的最大Gas 总量,由矿工决定它的大小。防止矿工的资源消耗过大,造成挖出的区块无法形成最长的交易链。不过矿工也不能任意地更改区块的Gas Limit ,根据以太坊协议,当前区块的Gas Limit 只能基于上一个区块的Gas Limit 上下波动1/1024。


交易类型

1.转账交易

web3.eth.sendTransaction({

from :"Oxb60e8dd6lc5d32be8058bb8eb970870f07233155",

to:"Oxd46e8dd67c5d32be8058bb8eb970870f07244567",

value: 10000000000000000

});

2.创建智能合约的交易

web3.eth.sendTransaction({

from:"Oxb60e8dd6lc5d32be8058bb8eb970870f07233155",

data :"contract binary code"

});

3.执行智能合约的交易

web3.eth.sendTransaction({

from:"Oxb60e8dd6lc5d32be8058bb8eb970870f07233155",

to:"Oxb4259e5d9bc67a0f2ce3ed372ffc5lbe46c33c4d",

data :"hash of the invoked method signature and encoded parameters"

});

交易执行流程












七、数据编码与压缩

RLP ( Recursive Length Prefix )是一种编码算法,用于编码任意的具有嵌套结构的二进制数据,是以太坊数据序列化的主要方法。


总结

除了EVM和区块同步协议外,其他基本都覆盖了,至于一些细节之处最好通过分析以太坊的源码去厘清。