Hi 游客

更多精彩,请登录!

比特池塘 区块链前沿 正文
默克尔树
% [5 ?$ @1 Q% h% T
* j" G$ _5 x8 ^, z$ F在讨论以太坊的主要数据对象之前,我想先向各位简要介绍一下默尔克树到底是什么,以使得它得以发挥作用的属性特征。
$ o9 F5 W8 \5 Y) F; Q
6 Y) v3 d- ^- y+ ]黄皮书中假设由定制的默克尔-帕特里夏树维护世界状态和交易。附录 D 描述了这个数据结构。/ Z5 _# G) R+ J) }1 [/ g3 [: f
9 n8 j5 A) }0 [5 t- _: o
默克尔-帕特里夏树有许多有意思的属性,如果你想更深入地了解其在以太坊中的应用,我推荐你阅读这篇文章。
4 }& Q% n5 N- q
% K; O; T  G6 o2 [! W3 d, g6 Q在默克尔树中,由叶子节点保存区块数据的哈希,而由非叶子节点保存其子节点的哈希。2 j4 C7 p) L  `4 v% g3 C3 z9 }$ U) n
; ?$ ^* P& J7 ?' P- ~2 x
-默克尔树示意图(包括节点以及他们之间的关系)-: a( P  O( j1 s, {& w0 z5 ^

2 c0 ^* x/ b" u9 e, y默克尔树所指向数据的任何改动都会引起节点哈希的变化。由于每一个父节点中所保存的哈希值都取决于子节点所包含的数据,所以子节点中数据的变更都会引起父节点哈希的变化。并且这样的影响是连锁反应,从叶子节点直达根节点的。因此对叶子节点所指向数据的改动会引起根节点所保存哈希的变化。由上述结构特征,我们可以引申出两条重要的属性:  R# ?; {( Y/ q. T

; B, w* B: O/ e9 v在判断两棵默克尔树所指向数据是否完全相同时,我们不需要比较每个叶子节点,而只需比较根节点所保存的哈希。$ E2 q: n' |8 T5 ^
' d4 h0 P8 V7 G1 f& H$ v- z
在判断特定数据是否被树所指向时,我们可以使用 默克尔证明 技术。此处不对该技术作过多介绍,只需知道这是证明数据存在于默克尔树中的一种简单、高效的方法。* H3 l0 I, C9 J: n  @) ~) [
1 n4 D! Z/ H4 w+ o- r
第一种属性的重要之处在于,我们能够仅利用根节点的哈希值,就标示某一时刻整棵树所指向的数据。这意味着仅通过保存根节点的哈希值就能标示区块(无需储存区块链中所有的数据),且维护数据的不可篡改。
" X# x/ z8 o, Q% `2 R2 w" [" `3 {( ~. L- p+ u$ P: f6 Q& O
至此我们理清了默克尔树中根节点哈希的作用,下面来介绍以太坊中的主要对象。3 h* X3 o2 ~, v
0 [. y" A7 h1 Q
世界状态: F( g. E* \  U' e) m
7 ?  q6 L/ w& N* S# r
世界状态是地址(账户)到账户状态的映射。虽然世界状态不保存在区块链上,但在黄皮书的描述中,世界状态也由树来保存数据(此树也被称为状态数据库或者状态树)。世界状态可以被视作为随着交易的执行而持续更新的全局状态。以太坊就像一个去中心化的计算机,世界状态则是这台电脑的硬盘。+ K' @4 V. \% a+ d
" A& u; w7 Z  A$ U" C) G
以太坊中所有的账户信息都体现在世界状态之中,并由世界状态树保存。如果你想知道某一账户的余额,或者某智能合约当前的状态,就需要通过查询世界状态树来获取该账户的具体状态信息。下文中我也会简要介绍这些信息是如何存储的。
$ o. n& \3 U4 f' I5 [- ?
8 Z$ H5 n7 j8 K  P% B0 X9 U% `( D-世界状态树与账户存储-6 d3 G% x  }( l$ k- E# _9 t

& M" g! J4 b% Y0 r7 h0 e+ N9 B( A2 O-世界状态树与账户存储-
/ H7 t- m& o! P7 x, v" p) ~. ^6 A. P) i2 S/ g+ D/ z. H
账户状态
0 u$ j1 ?, n% }  R4 }: v9 X
* a; e8 v+ I8 y  G! V. d以太坊中有两种账户类型:外部所有账户(Externally Owned Accounts 简称 EOA)以及合约账户。我们用来互相收发以太币、部署智能合约的账户就是 EOA 账户,而部署智能合约时自动生成的账户则是合约账户。每一个智能合约都有其独一无二的以太坊账户。( ]3 u/ a9 d/ N' O0 E" u2 k$ m$ t

0 f1 q) m. a- r5 q) `. O账户状态反映了一个以太坊账户的各项信息。例如,它存储了当前账户以太币的余额信息、当前账户发送过的交易数量…每一个账户都有账户状态。
' m: ~- }  W0 M3 P1 ?
' L1 u$ b4 T0 Q/ f- s下面就来看看账户状态中都包括什么:3 k0 u9 h" T! Q! ^" ]; z2 g

" H7 F, F5 m5 x! knonce
: s+ J4 k$ T* x/ Z, o1 H/ i- {" t; F
从此地址发送出去的交易数量(如果当前为 EOA 账户)或者此账号产生的合约创建操作(现在先别管合约创建操作是什么)。' _" P8 H! U3 O% Q1 C$ c6 @4 B
. G% I. Q  ?% x2 }( _' z
balance
: {2 s# |8 H8 e' [. V0 K" k" M& ^6 m: O" F0 }
此账号所拥有的以太币数量(以 Wei 计量)。
  \4 n5 }& W* h9 ^0 e9 U
& }- U- Z$ D0 b5 }1 W& t; zstorageRoot, G% P# x' F: b9 b: k

- v1 A% v8 r/ s# s/ I4 B& {/ ^0 Y账户存储树的根节点哈希值(稍后介绍账户存储是什么)。" ?- e4 @, y# b& e' Z0 H

% {9 x4 P% O! S5 gcodeHash) v  q; @! x- g* u5 ?& \

" p" e, b$ x- c) {3 h0 x: z# b对于合约账户,就是此账户存储 EVM 代码的哈希值。对于 EOA 账户,此处留空。
4 c1 _8 D9 b' W/ \$ E+ o" v; h2 O/ x+ [2 P  [: v8 Y, C) V
账户状态中不容忽视的一个细节是,上述对象在内的所有对象都可变(除了 codeHash)。举例来说,当一个账户向其他账户发送以太币时,除了 nonce 会增加,账户的余额也会相应改变。- g) j* E7 s' S* F
3 I; a4 `$ L5 t/ i( K  R! d4 ~; f; z
而 codeHash 的不可变性使得,如果部署了有漏洞的智能合约,也无法修复更新此合约。对应的,只能部署一个新合约(而有漏洞的版本会一直存在于区块链上)。这也是为什么使用 Truffle 进行智能合约的开发和部署十分必要,并且用 Solidity 编程时要遵循 最佳实践 的要求。* u* d! t$ C5 }8 Z
2 }2 C  G" d, x, W& \
账户存储树是保存与账户相关联数据的结构。该项只有合约账户才有,而在 EOA 中, storageRoot 留空、 codeHash 则是一串空字符串的哈希值。所有智能合约的数据都以 32 字节映射的形式保存在账户存储树中。此处不再赘述账户状态树如何维持合约数据。如果读者对其内部实现感兴趣,强烈建议阅读这篇文章。账户状态中的 storageRoot 区域负责维持账户存储树根节点哈希值。
+ S* x$ ?; ~! t3 r, _+ n( I* {
5 y$ F! g9 ?& I8 j- _-账户状态与账户存储树-
" C* b( e& {* k) ?$ s- ~. d* a9 u8 r- X' g% O) y
交易# j% b6 L/ @0 c3 R6 Y# f' d
' f% L# p3 T- E6 {
交易推动当前状态到下一状态的转变。在以太坊中有三种交易:- o/ j' H6 @) K' y& z( j
: x" {: _; v# a
EOA 之间传输值的交易(例如,改变发送方和接收方余额大小)。
) K8 x7 G  n' V1 d& x. m, `$ s  Q  W9 _, Y' E( I) }2 L
发送消息来调用合约的交易(例如,通过发送消息调用来触发 setter 方法,以设置合约中的值)。$ v/ I+ a  @4 L$ i+ x
! W6 ~: c8 p2 Q9 Q, E1 t
用于部署合约的交易(由此创建了合约账户)。
8 D# F9 F; `1 l4 m0 L% k5 J
! ^+ O% D% ?. O0 K(从技术角度来讲,前两种交易是一样的…它们都是通过消息调用来改变账户状态的交易,只不过一个是 EOA 账户,一个是合约账户。此处将交易分为三种是为了方便读者的理解。)8 E, _8 _: T7 I. g8 [
- K% u9 v( q. y. R( v) f# m
交易由以下部分组成:
9 }4 J/ P6 @5 |; q4 q0 q
' L+ G3 p6 Z( E1 W/ Nnonce
8 o; T+ L  ?" z3 F) U6 d4 o4 ]  p' Z% F: E. Z* ~' y7 f3 M
此账户发出的交易序号数(校对注:可以粗略理解为“这是该账户的第几笔交易”)。
$ ~% T# q0 R8 X* w9 z; G8 \
2 u( Y' t: v$ P$ ?  kgasPrice
. }6 M, _3 g3 ?" s, ]
+ L" I0 f2 r; N执行此交易、进行计算时为每单位 gas 所支付的费用(以 Wei 计量)。
# V4 j7 e6 t" {0 V6 U: v& b0 N
; [5 T% j' E3 ^, |gasLimit
! c" w6 G1 y2 s: ~% x2 i
% P3 q$ p6 X& a2 \+ l  }执行此交易时可以使用的最大 gas 数量。# m4 \6 p$ D% D" M( t" L: n
$ ]( f" }5 @2 l! D- \) \, q+ f
to( |7 h6 M5 _' }8 [
2 L8 R4 N' K4 w, s- h
如果此交易用于传送以太币,此处为接收以太币的 EOA 地址。) A6 D, i) U5 C# F" A/ N

) i! a" ~  L& _' g4 d  e如果此交易用于向合约发送消息(例如,调用智能合约中的方法),此处为合约的地址。' B/ u6 M' Z. a

3 A+ E5 j8 J& M如果此交易用于创建合约,此处值为空。4 J' S' H! ~7 t0 R2 [5 y% G" w
0 T3 R9 |6 l, G, K
value. ?- Z8 u1 w. J, P4 M# j5 O% b- r

  R' Q0 k! z4 O* I如果此交易用于收发以太币,此处为接收账户以 Wei 计量的代币数量。
2 Y7 ~' Y4 p5 G0 z; Q2 z* d% M$ L- P) c4 C  s3 g. ~2 c- {
如果此交易用于发送对合约的消息调用,此处为向接收此消息智能合约所给付的 Wei 数量。
5 m: S4 }; b+ L$ }* k) r
) L2 S5 j: W" u如果此交易用于创建合约,此处为合约初始化时账户存放的以 Wei 计量的以太币数量。1 u6 ^: o5 S: x) k  ]9 a" Y5 H' s6 R
9 [+ p  l! u2 ]! Z9 t
v, r, s& d, K8 z8 @0 m& ^- @
( Q! p; U+ o; G3 h0 G6 U
在交易的密码学签名中用到的值,可以用于确定交易的发送方。
1 T% Z/ T& M: L/ l% l; i8 `% ?: v, c! ]; w" O9 H4 m! ~* a
data(只用于价值传输以及向智能合约发送消息调用)
# g( u: x3 p9 `" Y; B* Z9 l' t8 k- W
发送消息调用时附带的输入数据(例如,假设你想要执行智能合约中的 setter 方法,数据区就应该包括 setter 方法的标识符,以及你想要设定的参数值)。) I$ S5 H( j% U- R# x0 s# Z* U* Q
9 i- r3 t$ j; B
init(只用于合约创建)# p4 A1 I# j: ]" ^& a9 K% U) \
( A$ V- n; I1 R. t  [
用于初始化合约的 EVM 代码。- ?8 |  w4 a  {3 G3 _- z  e# A. _( d! @
% l. L  `6 ~. t
别想着一下子就把这些概念消化完… 必须对以太坊的内部机理有更深的认识才真正理解、使用像 data 区、init 区这样的概念。
$ c9 R7 {9 w# f5 m# f0 s: ^& l' l% `/ F$ ^. ^, }, P
相信不出你的意料,区块中所有的交易也是存储在默克尔树中的。并且这棵树的根节点哈希值由区块头保存!下面我们就来剖析一下以太坊区块结构。6 X' T- ?/ W: Q0 t; _, D+ ?
& Q0 q: w8 `7 t% p* d9 n& {( }9 R
区块  N- w" o) [8 \4 u/ B
3 v- f8 e2 S9 V$ F2 h5 {6 `
区块分为两部分,即区块头和区块体。3 X" p1 M+ V/ t+ }! P  M

9 {5 j( k- F3 U6 _/ y( \! C区块头就是以太坊中的区块链部分。它保存了前一个区块(也可称为父区块)的哈希值,通过区块头的连接形成了一条由密码学背书的链。
+ M/ e" O: O8 t3 f
7 A. R9 F. j; A: S; p0 ?区块体包含了此区块中记录的一系列交易,以及叔块(ommer)区块头列表。如果想要进一步了解叔块,我推荐阅读这篇文章。, {5 U& |# u! V) l- `
- W) P& Z( R% X
-以太坊区块的抽象示意图-* R* ~( h4 }- o* F

+ q0 M' a& D0 y( |' D% _! T下面就来介绍区块头包括哪些部分。- c# F. m$ L7 @% y

9 j+ ~$ w2 P' e' ?9 g1 z! k" X2 WparentHash
- N: e5 m/ X" b& {7 V! |* ^
1 c2 L8 V4 @0 J- t0 n/ u; o前一个区块的区块头哈希值。每个区块都包含前序区块的哈希值,一路可回溯至链上的创世块。这也就是维护数据不会被篡改的结构设计(任何对前序区块的篡改都会影响后续所有区块的哈希值)。
9 }3 B# V7 @7 Z: g3 A# m
9 g. s; C- E* x8 r( E* `ommersHash
2 X( a5 r# q( r" A, C1 c& q) c' A+ }% D' Z+ M3 N
叔块头以及部分区块体的哈希值。/ h2 Z8 Q$ i& `6 ?( p

/ a: Z, @( }$ ebeneficiary: e* f; ^( N5 M( G
2 ]' o1 K  ?. F8 A+ F) O% M
因为挖到此区块而获得收益的以太坊账户。
* y0 b/ X8 t" ^4 j3 |$ c) \8 r) Q. u  |* Q6 h* I5 Q% v
stateRoot
8 _* I) n, H+ T; r; U4 ?( d- j' @- _+ G) r+ E) }5 c' d' r$ A$ h& w
世界状态树的根节点哈希值(在所有交易被执行后)。
6 C2 b' K" o/ B, ~  H/ e* y3 A! f: l& g/ l) f. ~& |
transactionsRoot4 V+ L8 _. O7 m8 w
0 L( X% r* L6 k
交易树根节点的哈希值。这棵树包含了区块体的所有交易。& }. |( u- _$ \. a7 N

/ U. |' |+ L) u/ z6 G8 L6 QreceiptsRoot
* d, o) L6 f* F
9 u5 O: _# K) ~% Y% h每当交易执行时,以太坊都会生成对应结果的交易收据。此处就是这个交易收据树的根节点哈希。% ~6 c1 k1 J3 V+ a
1 D4 r7 f5 L* x
logsBloom
( S* e4 @. h8 @1 t0 s4 N+ n( I- M1 J- f! X4 m
布隆过滤器,用于判断某区块的交易是否产生了某日志(如果对这方面感兴趣,可以查阅 Stack Overflow 的这个答案)。这避免了在区块中存储日志信息(节省了大量空间)。
8 f. R8 Y1 a; T$ c" k$ q9 t3 T* D3 G
" e3 u8 C3 R/ }- _0 B5 h9 Qdifficulty
! j) y5 d: \7 v* m
5 N" D- q9 T3 o% ~8 @9 U: }8 C2 n此区块的难度值。这是当前区块挖矿难度的度量值(此处不对此概念的细节和计算作介绍)。
% k8 S1 O; ~& Z; |5 v/ t
3 X$ ^) V8 J4 @3 W% U+ }number
( @* x5 A! B3 ^# L# ^  W
, v( Q& ^& T# X3 a6 R, }, v3 t前序区块的总数。这标示了区块链的高度(即区块链上有多少区块)。创世区块的 number 为 0 。% Y* B1 Z( O3 ~2 s' @4 T2 s

/ j6 k# n! p6 ]/ w% vgasLimit
2 ]( V3 Y$ I+ P0 R& n6 r3 q1 o9 y2 y, T$ }3 u1 s  Q4 D; x
每一个交易都需要消耗 gas 。gas limit 标示了该区块所记录的所有交易可以使用的 gas 总量。这是限制区块内交易数量的一种手段。
/ l8 H+ Z; T, u7 o$ y1 S6 u4 N
0 `; K# b/ s! M" H- y, f( T8 ngasUsed1 [& X; x, B8 {& I9 L  R2 T3 T2 R# z

7 [$ v1 }! Z: T, f区块中各条交易所实际消耗的 gas 总量。0 }0 P/ T# J& G0 Y
3 ~1 A: v4 q8 z; F! p- @- {: `
timestamp( [/ K% _9 R' `) e1 p

" o. `* F  e; |- o$ \  K9 N区块创建时的 Unix 时间戳。谨记由于以太坊网络去中心化的特性,我们不能信任这个值,特别是撰写智能合约、涉及到时间相关的商业逻辑时不能依靠这个值。+ r& W3 t/ k/ z  T' ]- @  t' t

( ]+ i5 A# w6 L: z& fextraData
/ k4 K' A- U$ o" w9 X" C- }. a! p& ~& z$ a" Z. B8 D0 p) l8 v
能输入任何东西的不定长字节数组。当矿工创建区块时,可以在这个区域添加任何东西。
- F( }' e4 D- H; T  p) K. W
5 F7 |$ T* U/ I1 C+ ^# z6 VmixHash, Q, w- ^+ ]8 ]* z" @

: L7 b% M. L$ S# r$ O用于验证一个区块是否被真正记录到链上的哈希值(如果想要真正理解这个概念,建议阅读这篇文章 Ethash proof-of-work function )。
+ m) C* T" N( f% U4 s" r1 b3 I# H, V# B9 n# C) d% G  I% [  U
nonce* D* d* P8 H8 U0 g" e/ P
* G+ `8 R$ }2 b- q& f$ ~' u& K& z" y: v
和 mixHash 一样,用于验证区块是否被真正记录到链上的值。
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

成为第一个吐槽的人

olwww 小学生
  • 粉丝

    0

  • 关注

    0

  • 主题

    1