关于唯链雷神区块链,你可能还不知道的那些事儿(第二篇)- 强制交易依赖性



  • 关于唯链雷神区块链,你可能还不知道的那些事儿(第二篇)- 强制交易依赖性

    这是“关于唯链雷神区块链,你可能还不知道那些事儿”系列的第二篇,你可以在文章最后找到上一篇的链接。

    唯链雷神区块链提供了一个安全的机制,使用户可以强制让一笔交易的发生建立在另一笔交易成功的基础之上。换言之,一旦启用了这个功能,系统将会检查当前交易所依赖的上一笔交易的状态。只有依赖的上一笔交易状态显示为成功,当前的交易才会被系统接受并处理。

    交易成功的条件有两个:

    1. 交易被记录在了账本里;
    2. 交易被已经成功执行,或者用区块链的语言来说交易没有被撤销。

    第二个要求尤其重要,因为看到一笔交易被记录在账本里并不能保证它已经被成功执行了。一笔交易可以在被记录的同时状态显示为“被撤销”,这意味着系统其实没有执行交易的内容。在实际操作中,一笔交易可能因为交易发起者提供的gas不足而被撤销。此外,交易还可能被调用的智能合约函数所撤销。比方说,如果交易发起者的通证余额不足,那么这笔交易就会被执行通证转账的合约函数所撤销。

    在雷神区块链上,你只要提供所依赖的交易的TXID,就可以让你的交易依赖于该笔交易。我们没有对于被依赖的交易设置任何限制,比如“谁是发起者”、“交易什么时候执行”或者“交易的内容是什么”。这为开发者提供了很多开发灵活性。值得注意的是,正是因为唯链雷神区块链的TXID设计,我们才能实现以上功能。你可以通过我的上一篇文章来了解更多关于TXID的内容。

    DependsOn

    接下来让我介绍一下这个机制是怎样在唯链雷神区块链上实现的。

    这个机制是由交易模型中的DependsOn字段(请查看在$THORDIR/tx/transaction.go中定义的body结构部分)和一些额外的验证逻辑实现的,这些验证逻辑被实现在以下用于验证一个区块中的交易的代码中(具体请查看$THORDIR/consensus/validator.go中的verifyBlock函数)。

    // check depended tx
    if dep := tx.DependsOn(); dep != nil {
    	found, reverted, err := findTx(*dep)
    	if err != nil {
    	    return nil, nil, err
    	}
    	if !found {
    		return nil, nil, consensusError("tx dep broken")
    	}
    	if reverted {
    		return nil, nil, consensusError("tx dep reverted")
    	}
    }
    

    现在我们来仔细看一下以上这段代码。根据定义,DependsOn字段是一个指向它所依赖的交易的TXID的指针。这段代码首先做的是通过dep := tx.DependsOn()获取当前交易的DependsOn字段值。如果该字段值不为空,我们执行found, reverted, err := findTx(*dep)来检查所依赖的交易的状态。剩下的代码先检查搜以来的交易是否存在,然后再检查它是否被撤销。一旦所依赖的交易不符合这两个条件中的任何一个,当前交易就不会被系统执行。

    代码演示

    我做了一个简单的演示,来让大家能够更好理解这个强制交易依赖性机制。这段演示代码是用Typescript写的,你可以通过这个 链接 找到并下载代码。我在这段代码中用到了两个最新开发的工具:connex-frameworkconnex.driver-nodejs。这两个工具能够让开发者在NodeJS环境中方便地通过Connex接口与唯链雷神区块链进行交互。

    我在代码中硬编码了两个账户地址,分别叫做acc1acc2以及acc1的私钥,并且用了测试网水龙头来获取足够的数字资产来运行这段演示代码。

    const sk = '0x29a9...1a7';
    const acc1 = '0x858...2c4';
    const acc2 = '0x914...85d';
    

    这个演示做了什么?

    这个演示程序做了下列几件事:

    1. 发起交易TX1,从acc1转 1 VET 到acc2。提供足够的gas来确保这笔交易能顺利执行;
    2. 发起交易TX2,从acc1转 1 VTHO 到acc2。不提供足够的gas使得这笔交易被撤销;
    3. 发起交易TX3,从acc1转 1 VET 到acc2,让这笔交易依赖于一笔不存在的交易;
    4. 发起交易TX4,从acc1转 1 VET 到acc2,让这笔交易依赖于TX1
    5. 发起交易TX5,从acc1转 1 VET 到acc2,让这笔交易依赖于TX2
    6. 在发送完所有交易随后的连续五个新区块里,查看以上五笔交易的状态。你会看到TX3TX5无法被找到,因为它们依赖的交易没有满足条件。

    请注意,出于方便的考虑,我在演示中创建的交易都依赖于同一个地址发起的交易。在实际操作中,你可以让你的交易依赖任何交易,你只需要提供所依赖交易的TXID即可。

    运行结果

    现在让我们来看一下这段演示代码输出的结果。

    Sent TX1 with ID = 0x6f7874438429ce31d89b68ceb1dacf3fba012149b57f9781f6d9b5c707eedde5
    ...
    Sent TX2 with ID = 0x9f8f9bec9593f74d9353ac7f1087afa32646638155e6a448dc35d60788d958b5
    ...
    Sent TX3 with ID = 0x319b70009a8234107a2d08c3dbf9e93396330892badb03b5c4cebac391d68c2d
    TX3 depends on an nonexisting TXID
    ...
    Sent TX4 with ID = TX1 found! If reverted: false
    0x1c30f07aae653711ab3284062afe557f1674c56d625114326230b041d79f9931
    TX4 depends on TX1
    ...
    Sent TX5 with ID = 0xd92e5ffcf3e9ff11f33c59b814869c23df75c7cc49773fcb95ccc8b9bd420a43
    TX5 depends on TX2
    ...
    

    最先输出的几行告诉了我们一些在演示代码中创建并发送到测试网上的五笔交易的相关信息。我们可以看到它们的TXID以及依赖的交易。请注意这个演示故意把TX3DependsOn字段值设为0x1,这意味着TX3依赖于一个TXID为0x1的交易。目前这样的一笔交易还不存在。

    --------------------------
    Block Number = 3170840
    --------------------------
    ...
    Checking TX1 with ID = 0x6f7874438429ce31d89b68ceb1dacf3fba012149b57f9781f6d9b5c707eedde5
    ...
    TX1 found! If reverted: false
    ...
    Checking TX2 with ID = 0x9f8f9bec9593f74d9353ac7f1087afa32646638155e6a448dc35d60788d958b5
    ...
    TX2 found! If reverted: true
    ...
    Checking TX3 with ID = 0x319b70009a8234107a2d08c3dbf9e93396330892badb03b5c4cebac391d68c2d
    ...
    TX3 not found!
    ...
    Checking TX4 with ID = 0x1c30f07aae653711ab3284062afe557f1674c56d625114326230b041d79f9931
    ...
    TX4 not found!
    ...
    Checking TX5 with ID = 0xd92e5ffcf3e9ff11f33c59b814869c23df75c7cc49773fcb95ccc8b9bd420a43
    ...
    TX5 not found!
    ...
    

    在发送完这些交易后,每当程序检测到一个新的区块诞生,它就会开始检查这些交易的状态。这样的操作会持续五个连续的新区块。在我运行这段代码的时候,第一个新区块的高度为3170840。其对应的结果显示TX1TX2已经被记录在了账本上。与此同时,我们可以看到TX2被系统所撤销。

    --------------------------
    Block Number = 3170841
    --------------------------
    ...
    Checking TX3 with ID = 0x319b70009a8234107a2d08c3dbf9e93396330892badb03b5c4cebac391d68c2d
    ...
    TX3 not found!
    ...
    Checking TX4 with ID = 0x1c30f07aae653711ab3284062afe557f1674c56d625114326230b041d79f9931
    ...
    TX4 found! If reverted: false
    ...
    Checking TX5 with ID = 0xd92e5ffcf3e9ff11f33c59b814869c23df75c7cc49773fcb95ccc8b9bd420a43
    ...
    TX5 not found!
    ...
    

    在下一个区块中(区块高度3170841),TX4被记录进了账本上,而TX3TX5没有出现。这个结果是和我们的预期一致的。TX3TX5在随后的区块高度也没有出现。

    小结

    在这篇文章中,我阐述并演示了唯链雷神区块链一个特有的功能,这个功能使用户能够建立一个强制性的交易依赖关系。具体来说,如果一笔交易的执行依赖另一笔交易,只有在被依赖的交易出现在账本上且没有被撤销的情况下,这笔交易才会被接受并处理。

    文章链接

    关于唯链雷神区块链,你可能还不知道那些事儿(第一部分)- 交易唯一性


Log in to reply