每个区块链都必须要有唯一标示每笔交易 (TX) 的方法,否则它将很容易受到交易重放攻击。简单来说,重放攻击指的是攻击者通过使用账本中已有的交易来欺骗系统。
原文标题:《关于唯链雷神区块链,你可能还不知道那些事儿(第一部分)- 交易唯一性》
作者:Peter Zhou,唯链首席科学家,南安普敦大学计算机视觉和步态识别博士
自 2018 年 6 月开始,唯链雷神区块链已经正式上线一年了。作为其 白皮书 的作者之一,我仍然觉得有一些为了区块链技术的大规模应用而设计的重要特性,还没有被大家所了解。本篇是「关于唯链雷神区块链,你可能还不知道的那些事儿」系列的第一篇。在这个系列里我将详细阐述这些非常有用,但尚未普及的唯链雷神区块链的特性。另外,如果你还没有了解过唯链雷神区块链的多方支付协议(MPP)和多任务交易(MTT)特性,我强烈建议你可以先读一下相关的文章。
Peter Zhou,唯链首席科学家,南安普敦大学计算机视觉和步态识别博士
交易唯一性
每个区块链都必须要有唯一标示每笔交易 (TX) 的方法,否则它将很容易受到交易重放攻击。简单来说,重放攻击指的是攻击者通过使用账本中已有的交易来欺骗系统。
对于一个类似比特币的基于 UTXO 模型的区块链, 交易之间是相互关联的,我们可通过历史花费记录来验证交易。然而,对于像太坊这种基于账户体系的区块链来说,这种验证唯一性地方法不再适用。对于这类系统,我们需要在交易中加入一些额外的信息来达到交易的唯一性。
以太坊的解决方案
在以太坊上交易的唯一性是这样实现的,以太坊在每笔交易中加入变量 AccountNonce,并且规定只有在 AccounceNonce 和发起交易的账户里的 Nonce 变量的最新值一致的情况下,交易才会被处理。请注意,Nonce 的数值记录了该账户目前已经发送的交易数量。这样一来,一笔交易就能通过这个 Nonce 的值和发起方的账户地址唯一来标示。
不过这种设计存在不足之处,那就是从同一个账户发送的交易将被迫组成一个序列。在这个序列里,任何一笔交易的失败,都将会导致后续交易不能被以太坊系统接受。这是因为后续的交易里包含的 Nonce 的数值都比交易发起账户当前的 Nonce 数值要大,所以必然会被系统排除在外。大家可以想象一下,当你有多笔交易正在等待系统确认,而你又想发新的交易的时候,你将面临这样的风险:一旦之前的任一交易失败,新交易将会被无限延迟。
唯链雷神区块链解决方案
唯链雷神区块链是一个基于账户的区块链系统,它通过以下方法来实现交易的唯一性。首先,它重新定义了交易里的 Nonce 变量,使它成为一个完全由交易发起者决定的 64 位无符号整数。对于一笔交易,我们将计算两个哈希值,一个是不带签名的交易数据 RLP 编码的哈希值,然后我们把计算出来的第一个哈希值串联上交易发起方的账户地址,并对其做哈希运算,得到第二个哈希值。第二个哈希值的长度为 256 位,在唯链雷神区块链上用来作为这笔交易的唯一标示(TXID)。值得注意的是,TXID 的计算是不需要私钥来为交易签名的。
为什么这个设计很重要?
首先,一笔交易的唯一性完全取决于它的内容。它是否会被节点处理完全取决于计算出来的 TXID 是否已经在区块链上存在,而不是取决于发起方账户的状态(例如它的 Nonce 值)以及由这个账户发起的其他待确认的交易。显然这样的改变对于 dApps 开发者是很大的利好,因为当他们编写涉及与区块链交互以及处理交易失败的代码时,他们所要考虑的问题被大大简化了。
这一设计对企业用户来说具有同样重要的意义。假设你是一个工厂的负责人,你想要在区块链上注册产品,从而实现产品的追溯以及物流信息的可被验证性。如果是基于以太坊的交易唯一性方案,当你的产品被源源不断地生产出来,并通过发送交易到区块链来试图注册它们的时候,任何一个交易的失败将导致后续所有交易被系统拒绝,产品注册失败。这对于你的工厂来说是灾难性的,是不可接受的。如果是基于唯链雷神区块链的方案,整个流程将会变的流畅而且方便管理。因为你所需要做的仅仅是在发送一笔交易之前,确保它 TXID 没有被使用过。其余的就是单独处理那些失败的交易即可(比如重新发送它们),这是因为失败的交易不会影响到其他的交易。
一个简单的演示
为了大家能够更好的理解,我编写了一小段代码来展示 TXID 是如何计算的。在展示代码之前,我首先到类似 veforge 这样的唯链雷神区块链浏览器上随机找一笔交易。我的代码会在本地重构这笔交易并计算它的 TXID。最后,我会把计算出来的 TXID 值和浏览器上的值进行对比来验证我得出的计算结果。
以下是我随机选取的一笔交易的 TXID:
0x38fac25309ab5395f1725284af46af585988983945e67b77cf9916b1ddf13d4b
这里有个问题:由于 veforge 会四舍五入交易转账值,我会需要用其他工具来找到精确的数值,来重构这笔交易。为此我打开了 Sync(一个专为唯链雷神区块链打造的功能强大的 dApp 运行环境),点击了 「Toggle Developer Tools」 以及 「Console」(如下图所示)。
然后在 console 里输入以下命令:
connex.thor.transaction('0x38fac25309ab5395f1725284af46af585988983945e67b77cf9916b1ddf13d4b').get().then(tx => console.log(tx))
下图展示了我随后得到的结果:
为了运行我的代码,你需要安装 thor-devkit.js,这是一个帮助开发者开发 dApp 的 Typescript 开源库。你可以通过这个 链接,找到我的代码。
好了,现在我来给大家讲一下代码。这里我们第一步做的是构建一个交易子句。对于那些不熟悉唯链雷声区块链交易模型的同学,我简单说一下。每个唯链雷神区块链的交易都可以包含多个子句(Clause
),每个子句包含变量 to
,value
和 data
,可以做通常我们定义的交易的同样的事情。有了多子句结构,我们可以让一笔交易做跟多的事情,只要是这些事情都是由同一个地址发起的。这样会大大提高效率,并且保证这些子句操作的原子性。
let clauses = [{ to: '0x564B08C9e249B563903E06D461824b5d6b7F2968', value: "0x2a7ee2750fca8ea00000", data: '0x'}]
在构造完子句之后,我们需要给交易的其他变量赋值。这里我们用之前在 console 中得到的系统返回值来赋值:
let body: Transaction.Body = { chainTag: 74, blockRef: '0x002e3040a9ade438', expiration: 720, clauses: clauses, gasPriceCoef: 0, gas: 21000, dependsOn: null, nonce: '0x73541be64e72817c'}
之后,我们做的是构建交易:
let tx = new Transaction(body)
并且计算交易 TXID。这个计算过程分两步:1)首先计算不带签名的交易数据 RLP 编码的哈希值:
let signingHash = cry.blake2b256(tx.encode())
2)其次把步骤一中计算出的哈希值和发送账户地址串联起来,然后再对其计算哈希值,得到最终的交易 TXID:
const origin = '0xa4d2050f24ed7EfF313B7E912D6e5BF96ce57B95'let id = '0x' + cry.blake2b256(signingHash, Buffer.from(origin.slice(2), 'hex')).toString('hex')
好了,现在我们已经计算出了该笔交易的 TXID,可以把它打印出来,与之前在 veforge 浏览器上找到的 TXID 进行比较了。
最后,我要感谢 @rogake 帮助我一起翻译了 我的英文原文。
来源链接:bbs.vechainworld.io