论文作者 Pingcheng Ruan, Gang Chen, Tien Tuan Anh Dinh, Qian Lin, Beng Chin Ooi
安全的溯源存储
这一章节我们将讨论LineageChain是如何提升现有的区块链存储以支持更高效的溯源和篡改追踪。根本性的变化主要在于,我们将普通的Merkle树的叶子节点重新组合成了Merkle DAG。首先,将介绍Merkle DAG的结构,然后会讨论其特性。最后,我们会解释如何将其应用于区块链的执行模型并支持向后溯源追踪。
Merkle DAG
我们用k来表示一个区块链状态的独一无二的标识符,我们想要追踪它的所有的历史状态。我们用v来表示其演变历史中标识状态的唯一版本号。注意,状态版本号是严格递增的。当v更新后,v'一定会比v大。在LineageChain中,我们直接用区块号来表示它的版本号v。s_{k,v}表示状态的值(标识符为k,版本号为v)。s_{k}^{b}表示标识符为k的,并且在区块b前的最新的版本的状态值。在下面的例子中,对于k=Addr1,v=M,s_{k,v}=90。
定义1:一个标识为tid并且严格递增的交易,读取一系列的输入状态S_{tid}^{i},并且更新了一些列输出状态S_{tid}^{o}。一笔有效的交易需要满足下列属性:
属性一:一笔交易的所有输出状态的版本号需要一致,因为他们都是被在同样区块里的同样的交易所更新的。
属性二:输入状态的版本号应该严格小鱼输出状态的版本号。因为区块链构成了一个有序的交易组合,输入状态只能被之前的交易所更新。
属性三:对于所有有相同标识符的状态,未来交易的输入版本号不能有更早的版本。这样可以保证任何交易的输入状态必须在执行时保持最新的版本。
属性四:所有的状态更新都是独一无二的。
定义2:状态s的依存关系是输出s的交易的输入状态的子集。
注意:被prov_helper返回的dep是一读集合的一个子集合。
定义3:状态项E_{s_{k,v}}是一个元组,其中包含当前版本,状态值及其从属状态项的哈希。
状态项可以独一无二地标识一个状态。在LineageChain中,我们用其对应的哈希来表征每个状态条目。
定义4:在区块b中一些列的最新状态S_{latest,b}被定义为:
令U_{b}为在区块b中更新的状态。我们可以递归地有U_{b}和S_{latest,b-1}\U_{b}计算出S_{latest,b}。
定义5:X_{b}是基于映射map S_{b}来构建的Merkle 树,这里的S_{b}:
LineageChain将X_{b}作为状态摘要存储在区块头中。
(s_{k2,v4}和s_{k3,v4}都是被同一笔交易tid4更新的,但他们的依赖集合是不同的。b区块包含两笔交易tid3,tid4。它的最新的状态包含s_{k1,v3}, s_{k2,v4}, s_{k3,v4},Merkle树就是基于这三个状态而构建的)。
讨论
新的Merkle DAG可以很容易的被整合到现有的区块链索引结构中。具体的,现有的Merkle索引例如MPT把状态直接存储在叶子节点上,但LineageChain中的Merkle DAG将最新版本状态的条目哈希存储在叶子节点上。通过增加一个间接级别,我们维护了三个关系:tamper evidence (篡改证据), incremental update (增量式更新), snapshot(快照),但有增强了遍历DAG的能力,以便于抽取细粒度的溯源信息。
状态项的哈希可以描述状态的整个演化历史。因为这个哈希是被Merkle树索引保护的,用来实现留下篡改证据的功能,状态的历史也会被保护,从而实现篡改留证据的功能。换句话说,我们对溯源增加了完整性保护,但没有对索引结构增加额外的开销。举个例子,假设一个客户想要读取一个特定版本的一个状态,它可以首先读取状态条目爱最新区块的值。这样的读操作可以被执行防篡改验证。接着,用户可以用这个哈希遍历DAG来读取对应的版本值。因为DAG是防篡改的,读取对应版本的值的完整性也被保护。
支持向后追溯
上述DAG的一个问题就是它不支持向后追踪,因为哈希指针只能引用向前的依赖集合。当一个状态更新的时候,这些向前的依赖集合就会永久地建立,这样他们就会属于同一个不变的值的后代。但是,状态可以被未来的交易所读取,向后的依赖集合不能在更新的时候被确定。
我们发现,只有最新状态的前向依赖集合是可变的。一旦状态更新,根据始终读取最新状态的区块链智能合约的执行模型,先前状态版本的前向依赖关系将变为永久性。最后,它们可以被包括在推演历史中。下图为一个例子
s_{k1,v1}的后向的依赖在状态被更新至s_{k1,v3}的时候确定。这是因为当输出s_{k0,v4}的交易执行的时候,它读取s_{k1,v3}而不是s_{k1,v1}。在LineageChain中,对于每个在最新版本的状态s_{k,v},我们都会为包含s_{k,v}作为依赖的条目的指针建立一个列表。我们把这个列表称为F_{s_{k,v}}。
当状态被更新至s_{k,v'},v'>v,我们在s_{k,v'}条目中存储F_{s_{k,v}}。