单个区块链节点的链存储设计重点:交易,区块,状态。
存储示意图:
一个区块中有很多条交易,根据区块状态能得知块内交易的处理状态。
交易( Transaction)
从行为的角度解释,交易等同于操作(Operation)。
我们向区块链网络提交一笔交易实质上是发起了一个操作,而操作的具体内容与特定区块链协议有关。比如在以太坊中,一个操作可能就是执行了智能合约中的一个方法。
从计算机技术的角度分析,交易实质就是原子事物。
交易是区块链网络中数据的最小的组成部分。一笔交易提交后,它只能有两种状态,要么成功要么失败,不可能存在成功了一半的情况。
不同区块链对交易的定义虽然一致,属性字段却有所差异,但却不影响我们抽象出一个通用的交易属性模板。需要注意的是,并不是所有的区块链都遵循下图中的规则,此处只是方便你理解交易主要有哪些属性。
可以从etherscan网络中随意找到一个交易详情,对比抽象出来的交易属性参考:
From跟 **To **分别指向交易的发起方及接收方,这个很好理解,比如一笔货币的转账当然需要有转钱的跟收钱的。
智能合约标识的是当前这笔交易要执行智能合约的名称,随后附带了执行智能合约对应的方法以及执行该方法时应该附带的参数列表,不同方法可能有着不同长度不同数据类型的参数,此处统一用参数列表表示。
这是一种自动执行合同条款的计算机协议。智能合约在区块链上运行,当预设的条件得到满足时,它们会自动执行合同中规定的操作,无需第三方介入。
时间戳字段表示该交易在客户端构建的时间。这个时间是客户端独立添加的,但是我们也不用担心该时间与标准时间的差异,因为区块链网络在接收这笔交易的时候,会有交易时间的校验,过早与过晚的交易并不会被网络接受,这在一定程度上限制了作假的可能性。
签名,一般情况下是 From 字段的账户进行签发的,用于向网络证明这笔交易确实是这个账户构建的,而不是其他人伪造的,主要是使用账户拥有者手中的私钥对交易进行签名,而私钥只有账户拥有者持有。
有一点需要注意的是,区块链中所有的交易基本上都是从区块链网络外发起的,区块链网络只接收交易而不生产交易,且不对交易做任何改动。
Q: 如果每笔交易都是客户端独立构建的,并没有与网络中的其他参与方进行协商,那这个交易哈希不会重复吗?
A: 哈希重复意味着产生了哈希碰撞,哈希碰撞的概率跟使用的哈希算法有关,且几乎不可能碰撞。
区块
要用什么“容器”来存储这些交易数据。其实这个容器就是区块,你可以这样理解交易和区块的关系,交易相当于是货物,而区块则是能够容纳多个交易的集装箱。
讲可追溯性的时候,我们提到区块链是时间段的数据前后关联依次串联整合成的信息链条,而每一时间段的数据们就称之为区块(Block)。 可以理解为存储某个时间段内所有交易数据的集合。
区块是指将节点一段时间内收到的所有(有效)交易打包而形成的一种数据结构,之所以将有效扩起来,是因为有些区块链的设计也包括了无效交易。直接理解概念有些抽象,我们可以参照区块示意图去理解区块的数据结构。
区块结构
区块分成了区块头跟区块体。
区块头:
包含该区块的基础属性,重要的属性主要有 4 个:前置区块哈希用于区块间的关联,交易根哈希用于区块与交易的关联,区块高度用于标记当前区块在区块链中的位置,方便定位,而时间戳记录了区块打包的时间。区块体:
则只有交易,且交易是有先后顺序的,一般是按照交易的时间戳字段进行排序。
区块间关联
每一个区块都会包含前置区块哈希作为逻辑关联两个区块的那个锚点。
区块与交易
默克尔树是一种树状结构,一般情况下至少有三层,分别是叶子节点,中间节点以及根节点,中间节点的层数取决于叶子节点的数量,叶子节点的数量越多,默克尔树的深度就越高。
默克尔树构建逻辑:(两两相邻节点从下往上进行hash,最终到根节点)
相邻的叶子节点进行哈希运算,得到的哈希值作为这两个叶子节点的父节点。然后同样的逻辑依次往上,最终倒数第二层仅剩的两个中间节点经过一次哈希运算得到他们的父节点,也就是整棵树的根节点,这样由哈希值构成的默克尔树就构建完成了。
区块体中包含的交易所对应的交易哈希,可以作为默克尔树的叶子节点,然后依次往上进行哈希计算,最后得到的根哈希就是区块体所有交易的交易根哈希,这个数据将会记录在区块头中。
Q: 为什么要这么麻烦地引入默克尔树呢?为什么不直接把所有的交易揉在一起,取一个哈希就够了呢?
A : (快速检索) 叶子节点哈希的任何变化都会传递至其父节点,一层层向上直到根节点,这就意味着根节点的值其实包含了所有叶子节点的哈希,但是却将可能被篡改的交易分开处理了,这样一旦出现问题,我们很容易就能判断出出错的分支。这提高了数据校验的灵活性,减少了很多不必要的资源浪费。
状态(State)
在区块链中执行的每一笔交易都有一个输出,而状态就是交易执行后输出的累积。怎么理
解呢?举个简单的例子:
2 + 3 + ( 4 * 7 ) + ( 8 - 9 / 3 ) + 23 = 61
“=”左边的每一个加法旁边的表达式我们可以认为是一笔交易记录,“=”右边的 61 则是交易执行后累积后的状态。
有限状态机
即便丢失了表达式的结果,但是只要我们还记着表达式,我们就可以再重新计算出对应的值,而这就是有限状态机的概念,即在一个封闭的系统中,如果状态起始条件一致,状态改变条件顺序一致,最终一定会得到一致的结果。
区块链就恰恰按时间顺序记录了所有的交易,因此即便丢失了状态,我们也可以很容易地重放状态,只要我们按照顺序再执行一次交易即可。所以从某种角度来讲,区块链也是一种有限状态机。
Q: 既然可以重放状态,为什么还要保留状态呢?
A: 如果你现在正在执行一笔交易需要某个输入,而这个输入跟之前中间某个区块里某笔交易的输出有关。
如果不保留状态,你在执行这笔交易之前,就得重新执行相关联的交易,而那笔交易可能又与更早的另一笔交易有关,因此得一直回溯,直到找到源头为止。
区块链跟数据库都保存了历史操作记录跟状态数据集合。只不过数据库更看重状态,而区块链却以记录历史区块为主,状态为辅。正所谓一个活在当下,一个怀念过去。二者的逻辑并没有什么本质上的区别,只是侧重点不一样。
状态模型
根据区块链的定位的不同,我们大致可以将状态模型的设计归纳为三种模型。
- UTXO 模型:(以比特币为首的专注于数字货币的区块链)
utxo(Unspent Transaction Outputs), 每一笔交易都应该有 N 个交易输入,同时产生 M 个交易输出(N 与 M 可以不等)。交易输入是前序任意交易的未花费的交易输出。如果当前交易成交,该前序交易的输出也就变成了成交的交易输入,也就失去了再次成为交易输入的资格。
UTXO 模型能够追踪数字货币的流向:未花费的交易输入告知货币是从哪里来的,未花费的交易输出告知货币往哪里去。
账户模型: (以太坊区块链采用)
账户模型,通过数字的加减表示账户余额的变化。
每一笔交易的执行,都会实现不同账户间余额的动态平衡,你我之间转账,你支出了 1 块钱,你的账户余额减 1,同时我的账户余额加 1,这种模型更符合我们日常生活的认知。
同时,账户模型除表示余额以外,也支持自定义数据的存储,可以在基础账户之上衍生出智能合约数据存储。通用模型:
在账户模型的基础上更进一步,没有内置状态属性,可存储任意自定义数据,被广泛应用在联盟链中。联盟链的定位是支持企业级应用的区块链平台,而企业业务的种类及模式是无法预知的,因此无法在设计中内定状态模型。
状态模型的选型没有唯一解,只要是能满足应用场景的模型设计就是好的模型。