Loading...

Substrate 入门(3)- 具备状态的链

Substrate 入门(3)- 具备状态的链

电报联系方式

状态区块链

对于没有接触过状态区块链的开发者而言需要记住以下基础概念:

  1. 当前状态是从genesis(即第0块,初始状态)开始,通过交易或其他方式产生了状态变更,不断累计出来的。
  2. 状态并不是储存块中,而是节点自身独立维护的。
  3. 块中记录的是“状态迁移”的方式
Substrate 入门(3)- 具备状态的链

上图所示即是一个链的状态变化的过程。

比如在创世纪区块genesis的时候,状态为A:1 B:2 而经过块1的过程后,通过交易(tx)或其他因素,将A的状态修改成了2,因此在块1下的“世界状态”就变为了A:2 B:2。块2以此类推。

因此实际上块中并不是记录当前的状态,而是节点自己本地维护了一个“世界状态”。这个状态就是存在本地的数据库中。而块中保存的时“状态迁移”,“状态迁移”就是交易(或其他因素),节点同步/执行了一个区块后,通过区块中含有的迁移状态的方式(即执行这个区块),修改自己的本地状态,从而当一个块执行完毕后,本地当前的状态即为这个块下的状态。

而对于当前状态模型的链而言,一般情况下会具备以下特性:

  1. 最新块(最高块)下的状态即为当前的状态,在这个状态下可以获取当前所有对象的状态
  2. 块中含有对这个块下的状态的证明(即状态的统一性经过了节点间共识)
  3. 可以通过任意一个历史的区块,取到在这个区块下的状态(如当前最高块已经是2,而通过1块中的相关信息可以取到A:2而不是最高块下的A:4

这3个条件中只有1是必须满足的,往后越达成一个条件,需要付出的代价就要更多。

牺牲第3个条件,保留状态证明与只保留最新状态是一种权衡。而第3点就不再交由链来维护,而交给第三方的附属设施维护,如中心化数据库区块链浏览器等等。

若连第2个条件也不用,则是状态模型的另一种极端,这种极端一定情况下牺牲了共识状态安全性。这种方式带来的好处是实现上比较简单。这即是eos的模型。

MPT 实现的世界状态

由于Substrate采用了和以太坊一样的模型,因此满足上述的3个条件。在Substrate中MPT简称为trie

对于MPT实现的原理这里不进行详细描述(编辑注:更多了解MPT 可查看详解以太坊默克尔压缩前缀树)。简单来说MPT的实现和git,IPFS中的IPLD模型等原理上都是一致的,用一句话描述就是:

使用DAG的方式,只记录每次变更后的索引(hash)。

如图所示:

Substrate 入门(3)- 具备状态的链

例如在上文提到的状态A:1 B:2,将A,B分别看做两个key,在MPT中key就是树的路径,而1,2是key对应的值,在MPT中就是叶子节点。因此A:1 B:2 变更到A:2 B:2的这个过程中,对应到上图相当于:

  • 从 root -> 2 -> value2 相当于记录了 A:1,从root->3->value3的过程相当于记录了B:2
  • 从genesis到 block1 的过程中,A的状态发生了变化,从1变成了2
  • 在MPT 中由于A的值发生了变化,因此MPT生成了一个新的叶子节点代表A的新状态,然后从叶子节点重新生成一个新的索引路径,即图中的 value2' -> 2'-> root'的过程
  • 而在生成新路径的过程中,由于B的状态没有发生变化,因此在生成新的路径的过程中,直接索引了B的老路径(即图中的虚线)。
  • 因此如上图所示,通过新的树根root' 索引到的A和B的值分别是A:2(即新的A的状态)与B:2(即老的B的状态)

如以上过程所示,每一个新的树跟记录的这个树根下状态的索引,因此每一个树跟即是每一个状态的DAG的起点,通过这个起点,可以获取到所有的状态。

每个区块都会含有状态变更,而每次变更即是通过以上类似过程生成一个新的树根,这个根root在以太坊及Substrate中被称为状态根 state_root放入每一个区块的区块头中,作为当前这个区块进行共识的状态证明。而通过以上方式也可以看出,只要从任意区块中取出状态根,那么就可以获取到这个状态根下那个时刻的所有状态的值。(对于相当同的key A,通过 root 索引出 value2,通过 root’ 索引出 value2’)

因此,MPT这种数据结构描述的世界状态,满足上述所说的3个条件。

另外一个角度

我们也可以将trie看成一个K-V数据库,这个数据库通过给予的Key能够获取到对应的Value。只不过这个k-v数据库通过给予一个root可以索引出在这个root的那个时刻下的对应数据。

也就是说trie实现的链上状态就是一个带快照的k/v数据库。每一个块就是对当前数据库全部数据的快照,块的时间戳代表了那个时刻下的数据状态,通过块中的状态根可以获取那个时刻下的数据。

而在打包执行当前区块时使用的root即是上一个区块的root,也就是打包区块时取当前最新的状态。

在Substrate中对于Runtime层而言,提供的接口即是

  • get(key) -> value
  • set(key, value) / remove(key)

这样的接口对于Runtime而言,可以直接将trie树看做一个key/value数据库即可,屏蔽了所有的trie树细节。

因此对于初学者而言,若目前还不是很容易搞清楚trie树的实现细节,那么就不用关心,只需要记住Substrate的读写数据模型是key/value数据库即可。

开发联系:DEXDAO

 

 

 

 

 

 

© 版权声明

相关文章

暂无评论

暂无评论...