区块链百讲:10分钟教你深挖以太坊数据层
在当前数据爆炸的信息时代,凭借区块链去中心化、点对点、不可篡改等特点,“区块链+大数据”成为研究热点。 可以说,区块链与大数据的结合,为未来区块链应用的大规模落地奠定了基础。
那么,数据是如何存储在区块链中的呢? 不同的区块链数据存储机制有哪些异同? 以以太坊为例,MIT孵化初创公司TowardsBlockChain的联合创始人vasa在本文中详细阐述了以太坊的数据存储机制,以太坊如何存储区块链状态和交易,以及以太坊和区块链的存储机制。比特币。 共同点和不同点。
此外,本文将带你了解“Patricia trie”数据结构背后的理论基础,并通过使用谷歌的levelDB数据库演示以太坊trie的具体实现。
字字句句都很重要,每一行代码都是干货,请继续往下看!
从架构设计的角度来看,区块链可以简单分为三层,协议层、扩展层和应用层。 其中,协议层又可分为存储层和网络层,两者相互独立又密不可分。
1 数据存储层存储什么?
先了解一下区块链的数据存储层,什么是区块链的数据存储层? 它存储什么? 它需要存储哪些数据才能使区块链系统正常工作?
例如,Alice 将 10 美元转给 Bob。 从上图可以看出,可以通过在区块链中添加交易来改变区块链的当前状态。
在跟踪不同用户(状态)的账户余额等相关细节的同时,还需要跟踪不同用户通过区块链(交易)引起的区块链状态转换的细节。
不同的区块链,如比特币和以太坊,使用不同的方法来实现上述功能。
1.1 比特币的“状态”
比特币的“状态”由其全网未使用的交易输出 UTXO(Unspent Transaction Output)表示。 比特币的价值转移是通过交易来实现的。 更具体地说,比特币用户可以通过创建交易并添加一个或多个 UTXO 作为交易的输入来花费一个或多个 UTXO。
比特币的 UTXO 模型是它区别于以太坊的主要特征。 为了更好地理解两者之间的区别,让我们先看一些例子。
首先,比特币中的UTXO不能只花掉一部分,必须全部花掉。
如果一个比特币用户想要花费 0.5 个比特币,但他只有一个价值 1 个比特币的 UTXO,那么他必须在交易的输出中添加自己的比特币地址,即给自己发送 0.5 个比特币作为零钱。
如果他不给自己找零,他将失去这 0.5 个比特币,这 0.5 个比特币将作为交易费支付给挖出这个区块的矿工。
UTXO交易
其次,比特币的区块链本质上不会存储和更新用户的账户余额。 在比特币网络中,用户只需要持有一个或多个 UTXO 的私钥即可。
数字钱包的使用使比特币的区块链看起来似乎在自动存储和更新用户的账户余额,但事实并非如此。
比特币钱包工作流程示意图
比特币的 UTXO 模型运作良好,部分原因是数字钱包能够执行与交易相关的大部分任务,包括但不限于:
UTXO模型中的交易行为如何描述? 钞票是一个很好的类比。
用户通过在钱包(模拟比特币地址或数字钱包)中添加纸币(模拟UTXO)来计算自己的资金以太坊区块信息存储在哪儿,并在想要花钱时使用一张或多张纸币。
每张账单只能使用一次,因为一旦用完,就不属于你了。
因此,可以得出结论:
1.2 以太坊的“状态”
与上述比特币区块链不同,以太坊区块链中的状态可以存储和更新用户账户余额等信息。
以太坊的状态不是一个抽象的概念,它是以太坊底层协议的一部分。
以太坊黄皮书提到,以太坊是一个基于交易的“状态机”,是一种可以构建所有基于交易的“状态机”的技术。
与所有其他区块链一样,以太坊的区块链是从创世块扩展而来的。
从创世块开始,交易、部署智能合约和挖矿等动作将不断改变以太坊区块链的状态。 在以太坊中,每次发生与该帐户相关的交易时,帐户余额(存储在状态字典树中)都会发生变化。
账户余额等数据并不直接存储在以太坊区块链的区块中,只有交易字典树、状态字典树和收藏字典树的根节点哈希直接存储在区块链中。 如下所示:
存储 trie(保存所有智能合约数据的地方)的根节点的哈希实际上指向状态 trie,而状态 trie 又指向区块链。
以太坊中存储了两种不同类型的数据:永久数据和临时数据。
交易信息是永久数据。 一笔交易被完全确认后,会被记录在交易字典树中,永远不会改变; 账户余额是临时数据,地址对应的账户余额存储在状态字典树中,每当有与该指定账户相关的交易时,账户余额就会发生变化。
因此,永久数据和临时数据应该分开单独存储,以太坊采用字典树的数据结构来管理数据。
以太坊具有与银行相同的记录保存机制,类比是使用 ATM/借记卡。
银行跟踪每张借记卡上的余额,当用户需要花钱时,银行会检查交易历史,看看用户是否有足够的余额进行交易。
1.3 比特币UTXO模型与以太坊账户/余额模型对比
比特币 UTXO 模型的优点:
以太坊账户/余额模型的优势:
为了防止对以太坊账户/余额模型的双花攻击,可以使用递增的随机数来防范此类攻击。
在以太坊中,每个账户都有一个公开可见的随机数,每次交易时该随机数增加 1。 这种机制可以防止同一个事务被多次提交。
这个随机数不同于以太坊工作量证明的随机数,是挖矿过程的随机值
在计算机体系结构中,有时需要在不同模型之间做出折衷。 一些区块链技术,例如 Hyperledger,使用 UTXO 机制是因为它们可以从比特币区块链衍生的创新中获益。
以下是对建立在这两种记录保存模型之上的更多技术的简要分析。
2 以太坊字典树数据结构
以太坊 trie 数据结构主要包括状态 trie、存储 trie 和交易 trie。
2.1 状态字典树——唯一存在
以太坊网络中有一个独特的全网状态字典树。
整个网络状态字典树都在不断更新。
这个网络范围的状态字典树包含对应于以太坊网络中每个帐户的键和值对。
全网状态 trie 中的“密钥”是一个 160 位标识符(以太坊帐户的地址)。
全网状态 trie 中的“值”是通过编码(使用递归长度前缀编码 (RLP) 方法)以太坊帐户的以下详细信息生成的:
状态树的根节点(整个状态树在给定时间点的哈希值)作为状态树的安全唯一标识; state trie 的根节点在密码学上依赖于 state trie 的所有内部数据。
状态树(Merkle Patricia 树的 levelDB 实现)和以太坊区块之间的关系
状态树:在给定的块中,状态树根节点的 Keccak-256 位哈希存储为“stateRoot”值
stateRoot: '0x8c77785e3e9171715dd34117b047dffe44575c32ede59bde39fbf5dc074f2976'
2.2 存储字典树——存储智能合约数据的地方
存储字典树存储所有智能合约数据,每个以太坊账户都有自己的存储字典树。 存储 trie 根节点的 256 位哈希作为“storageRoot”值存储在全局状态 trie 中。
2.3 交易字典树——每块一个
每个以太坊区块都有自己独立的交易字典树。
一个区块包含许多交易,一个区块中交易的顺序由挖出该区块的矿工决定。
交易字典树中特定交易的路径通过RLP编码得到交易在区块中的索引。
由于区块链的不可篡改特性,已经开采的区块不会发生变化,因此交易在区块中的位置永远不会改变。
一旦在区块的交易字典树中找到交易,即使重复返回同一条路径,检索结果也是一样的。
3 以太坊字典树实例分析
主流以太坊客户端使用两种不同的数据库软件解决方案来存储尝试。 以太坊的Rust语言客户端Parity使用的是rocksDB数据库,而以太坊的Go语言、C++语言、Python语言客户端均使用levelDB数据库。
在这篇文章中,我将主要向大家介绍levelDB数据库。
以太坊和 levelDB 数据库
LevelDB 是一个开源的 Google 键值存储库,除了常规功能外,它还提供对数据的前向和后向迭代、从字符串键到字符串值的有序映射、自定义比较函数和自动压缩。
自动压缩功能使用开源的谷歌压缩/解压库“Snappy”。 Snappy库的设计目标不是追求最大的压缩比,而是追求非常高的压缩速度。
LevelDB 数据库是管理以太坊网络状态的重要存储和检索机制。 因此levelDB是go-ethereum、cpp-ethereum、pyethereum等主流以太坊客户端(节点)的底层数据库。
虽然可以在磁盘上实现 trie 数据结构(使用 levelDB 等数据库软件),但需要注意的是,遍历 trie 和简单地查看键/值数据库之间存在差异。
为了更详细地说明这些差异,可以使用 Patricia Trie 库来访问数据库 levelDB 中的数据。
在以太坊客户端,进行交易、部署智能合约、挖矿等网络操作,观察它们如何影响以太坊的“状态”。
4 分析以太坊数据库
以太坊区块链中的每个区块都包含许多 Merkle Patricia 尝试:
要引用特定块中的特定Merkle Patricia trie,需要获取其根节点哈希值作为索引。
使用以下命令获取创世块中状态trie、交易trie和支付trie的根节点哈希值:
web3.eth.getBlock(0).stateRoot
web3.eth.getBlock(0).transactionsRoot
web3.eth.getBlock(0).receiptsRoot
如果你想获取最新挖出的块(而不是创世块)的根节点哈希,使用以下命令:
web3.eth.getBlock(web3.eth.blockNumber).stateRoot
获取根节点的哈希值后,需要配置网络环境。
4.1 安装 npm、Node、Level 和 EthereumJS
使用Node.js、Level和EthereumJS(用JavaScript语言编写的以太坊虚拟机)三个程序进行levelDB数据库实验。
使用以下命令配置实验环境。
cd ~
3sudo apt-get update
5sudo apt-get upgrade
curl -sL https://deb.nodesource.com/setup_9.x | sudo -E bash - sudo apt-get install -y nodejs
sudo apt-get install nodejs
npm -v
nodejs -v
npm install levelup leveldown rlp merkle-patricia-tree --save
git clone https://github.com/ethereumjs/ethereumjs-vm.git
cd ethereumjs-vm
npm install ethereumjs-account ethereumjs-util –save
实验环境配置完成后,运行如下代码,打印出以太坊账户列表和对应的密钥(存放在以太坊私网状态根目录下),连接以太坊的levelDB数据库,进入状态以太坊私有网络(使用区块链中区块的stateRoot值),然后访问以太坊私有网络上所有账户的密钥。
//Just importing the requirements
var Trie = require('merkle-patricia-tree/secure');
var levelup = require('levelup');
var leveldown = require('leveldown');
var RLP = require('rlp');
var assert = require('assert');
//Connecting to the leveldb database
var db = levelup(leveldown('/home/timothymccallum/gethDataDir/geth/chaindata'));
//Adding the "stateRoot" value from the block so that we can inspect the state root at that block height.
var root = '0x8c77785e3e9171715dd34117b047dffe44575c32ede59bde39fbf5dc074f2976';
//Creating a trie object of the merkle-patricia-tree library
var trie = new Trie(db, root);
//Creating a nodejs stream object so that we can access the data
var stream = trie.createReadStream()
//Turning on the stream (because the node js stream is set to pause by default)
stream.on('data', function (data){
//printing out the keys of the "state trie"
console.log(data.key);
});
以上代码的输出
以太坊网络中的账户只有在交易(与该特定账户相关的交易)发生时才会被添加到状态字典树中。
例如,仅使用命令“geth account new”创建的新帐户将不会添加到状态树中; 如果一个成功的交易(一个消耗以太坊 gas 并被添加到挖掘的区块交易中的交易)与这个账户相关联,那么这个账户将出现在状态字典树中。
这可以防止恶意攻击者不断创建新帐户,从而保持状态树的正常数据量。
4.2 解码数据
以太坊在与 levelDB 数据库交互时使用了“改进的默克尔帕特里夏树(Modified Merkle Patricia Trie)”,扩展了 Trie 数据结构。
例如,改进的 Merkle Patricia 包括一种通过使用“扩展”节点实现快速遍历的方法。
在以太坊中以太坊区块信息存储在哪儿,修改后的 Merkle Patricia trie 节点可以是:
由于以太坊的 Trires 是根据严格的规则设计和构建的,因此检查它们的最佳方法是使用计算机代码对其进行测试。
以下示例使用 EthereumJS。 当提供特定区块的 stateRoot 和以太坊账户地址时,运行以下代码返回账户余额。
以下代码的输出(以太坊地址0xccc6b46fa5606826ce8c18fece6f519064e6130b的账户余额)
//Mozilla Public License 2.0
//As per https://github.com/ethereumjs/ethereumjs-vm/blob/master/LICENSE
//Requires the following packages to run as nodejs file https://gist.github.com/tpmccallum/0e58fc4ba9061a2e634b7a877e60143a
//Getting the requirements
var Trie = require('merkle-patricia-tree/secure');
var levelup = require('levelup');
var leveldown = require('leveldown');
var utils = require('ethereumjs-util');
var BN = utils.BN;
var Account = require('ethereumjs-account');
//Connecting to the leveldb database
var db = levelup(leveldown('/home/timothymccallum/gethDataDir/geth/chaindata'));
//Adding the "stateRoot" value from the block so that we can inspect the state root at that block height.
var root = '0x9369577baeb7c4e971ebe76f5d5daddba44c2aa42193248245cf686d20a73028';
//Creating a trie object of the merkle-patricia-tree library
var trie = new Trie(db, root);
var address = '0xccc6b46fa5606826ce8c18fece6f519064e6130b';
trie.get(address, function (err, raw) {
if (err) return cb(err)
//Using ethereumjs-account to create an instance of an account
var account = new Account(raw)
console.log('Account Address: ' + address);
//Using ethereumjs-util to decode and present the account balance
console.log('Balance: ' + (new BN(account.balance)).toString());
})
5 独特的设计带来了哪些优势?
5.1 机动性
移动和物联网 (IoT) 设备如今无处不在,电子商务的未来建立在安全、强大和快速的移动应用程序之上。
可以说区块链在移动性上有了长足的进步,但我们也必须承认,区块链规模的不断增大是必然的。 因此,将整个区块链存储在日常移动设备上是不切实际的。
5.2 速度快而不影响安全性
以太坊网络状态的设计及其使用修改后的 Merkle Patricia trie 为其应用开辟了更多可能性。
在以太坊中对 Trie 执行的每个操作(添加、更新或删除)都使用确定性加密哈希。
另外,trie树根节点的密码哈希值可以作为trie树未被篡改的证据。 例如,对 Trie 数据的任何更改,例如增加 levelDB 数据库中的帐户余额,都会完全更改根节点哈希。
这种密码学特性为轻客户端(不存储整个区块链的设备)带来了快速可靠查询的可能性,例如查询区块高度“5044866”的账户“0x ... 4857”是否有足够的资金来完成交易, ETC。?
“Merkle 证明的空间复杂度与存储的数据量成对数关系。 这意味着,即使整个状态树有几千兆字节的大小,如果一个节点从可信来源接收到一个状态,该节点只需要下载几千字节的证明数据就可以充分确认该树上任何信息的有效性。”
5.3 配额限制
在以太坊白皮书中,有一个支票储蓄账户的概念。 在这种情况下,两个用户(可能是夫妻,或者商业伙伴之间)每天最多可以提取账户总余额的 1%。
虽然这个想法只是在白皮书的“进一步方向”部分提到,但它无疑会引起广泛的兴趣,因为它理论上可以作为以太坊底层协议的一部分(而不是作为第二层协议或一部分)第三方钱包)。
UTXO 对区块链数据是不可见的,事实上比特币区块链不存储用户账户余额。 因此,比特币的底层协议不太可能实施任何类型的每日配额限制。
5.4 消费者信心
相信在区块链开发者的不断努力下,我们将见证轻量级客户端的快速发展,以及能够与区块链技术交互的安全、强大、快速的移动应用的大规模落地。
为实现区块链技术在电子商务领域的落地,必须提高速度、安全性和易用性。 通过巧妙的设计提供卓越的可用性、安全性和性能将提高消费者的信心并增加大规模采用。
数据存储机制也是当前区块链应用落地面临的一大难题,决定了区块链的运行效率。
只有解决了区块链应用的痛点,区块链才能真正走进人们的生活,为人们带来便利!