关于唯链雷神区块链,你可能还不知道的那些事儿(第三篇) - 指定交易费用代付(VIP-191)



  • 关于唯链雷神区块链,你可能还不知道的那些事儿(第三篇) - 指定交易费用代付(VIP-191)

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

    本文将着重讨论"交易费代付"机制,特别是最近在唯链雷神区块链上新实现的“指定的交易费支付者”(即VIP191)协议。简单来说,交易费用代付机制是一种能够让普通用户在使用去中心化应用的过程中,无需直接支付交易费用的机制。通过这种方式,用户可以像使用普通手机或者网页应用那样,使用基于区块链的去中心化应用。这对于区块链技术的大规模普及和推广会起到至关重要的作用。

    目前,唯链雷神区块链上有两种协议支持交易费代付机制:多方支付协议(MPP)和指定交易费代付协议(VIP191)。前者是唯链雷神区块链的内置协议,而后者是由唯链生态里的Totient Lab提出的。在接下来的文章中,我将费别介绍MPP和VIP-191的工作原理。然后对它们做一个比较。接着是实现VIP-191的技术细节,最后是用例演示。

    MPP是如何工作的

    MPP允许唯链用户为其控制的账户指定一些特殊的交易发送方,然后为这些发送发发给其账户的交易支付交易费用。

    为了更方便的表述,我们把用户控制的账户(即交易的目的账户)称之为PAYER,把发送交易的账户称之为USER,把实际支付交易费用的账户称之为MASTER。按照MPP的规则,如果PAYER本身是一个普通账户,那么对应的MASTERPAYER重合。如果PAYER是一个合约账户,那么MASTER被设置为部署该合约的账户。另外,MPP还允许当前的MASTER将其身份转移给另一个账户。下图说明了PAYERUSER以及MASTER之间的关系。





    本质上,我们可以认为唯链雷神区块链为MPP维护了两张虚拟表单(如下图所示)。左边的表单记录了PAYERUSER之间的对应关系,而右边的表格记录了PAYER最终愿意支付的费用额度。表格内每列的详细说明请参见MPP的文档,此处不再赘述。





    假设现在PAYER 0x76c3要为USER 0x9D6D支付交易费用,我们来具体看一下MPP是如何实现这个需求的。首先,对应的MASTER需要调用内置合约Prototype的函数在上述两个表格内各添加一条记录(在图中用红色方框标出)。在左边表格添加的记录将PAYER 0x76c3USER 0x9D6D关联起来,这告知系统前者愿意为发起自后者的交易支付交易费用。同时,在右边表格添加的记录显示PAYER 0x76c3愿意为USER 0x9D6D支付的交易费用额度。一旦MPP协议被激活,在额度允许的情况下,任何USER 0x9D6D发起的交易的交易费用将由PAYER 0x76c3MASTER来支付。当然MASTER需要有足够的VTHO。

    为什么我们要引入VIP-191

    我们可以明显的看出,MPP协议是站在dApp所有者的角度去设计的。在协议里,只有这些人有责任去为他们的合约设置MPP协议,并且MPP协议只对于发送去他们合约的交易有作用。此外,由于MPP协议需要在链上记录关联信息,会产生一定的间接成本。因此从费用的角度来看,MPP协议对于用户与dApp之间有相对稳定的关系的情况会比较划算,相反这种关系如果是临时性的话则不是很划算。

    VIP-191作为MPP协议的补充,为在唯链雷神区块链上实现交易费代付,提供更大的灵活性。具体来说,VIP-191允许交易发送方寻找任意费用代付方,而不必非是交易指向的合约拥有者。 就像在文章“VIP191:实现大规模落地的关键”里阐述的那样,该协议可以使像dApp或钱包等第三方,为交易发起方支付交易费用。同时,交易发起方仍然能够完全控制交易的签名和发送周期。

    VIP-191是如何运作的

    VIP-191协议的工作原理其实很简单,它其实就是要求交易发起方和交易费代付者同时对该交易签名。另外,交易发起方需要开启VIP-191特性(这部分将在稍后进行讨论)来告知系统这是一笔使用VIP-191协议的交易。

    MPP vs VIP-191

    与MPP相比,VIP-191协议将触发协议的控制权交还给交易发起方,并不要求发送方支付任何间接成本。一个值得注意的地方是,VIP-191协议要求交易的发送方交易费代付方均在发送交易的时候都保持在线状态,这是使用MPP协议不需要的。如果考虑费用支付的透明度的话,MPP是一个更好的选择。这是因为MPP要求交易费代付方明确地把其代付交易费的意愿记录在区块链上。

    VIP-191的实现

    VIP191协议已在最新发布的唯链雷神主网 v1.1.2上实现。为了实现该协议,我们对于原有的代码做了以下两个重要变动:

    1. 扩展了交易模型
    2. 为使用VIP191协议的交易新增了判断交易费用支付方的逻辑

    交易模型的扩展

    Field Reserved in the TX body structure has been re-defined to be of type reserved as shown below:
    我们重新定义了在原交易模型中的Reserved字段:

    type reserved struct {
    	Features Features
    	Unused   []rlp.RawValue
    }
    

    在此结构里,我们将Features字段定义为32位无符号整数。我们可以把它想象成一个位图(bitmap)。每一位代表了一个特定特性(feature)的状态(1表示开启,0表示关闭)。VIP-191这个特性对应的是这个位图的最后一位。

    我刚刚提到,VIP191协议要求在交易中包含两个有效签名。实际操作中,我们把交易发送方的签名和同代付方的签名连在一起,赋值给Signature字段。此外,协议要求费用代付方对交易TXID进行签名。我们知道,TXID是一笔交易的唯一标识符,详细信息请参见我之前的文章

    判断交易费代付者的逻辑

    和VIP-191相关的判断交易代付者的逻辑可以在Go源文件THORDIR/runtime/resolved_tx.go中的函数BuyGas中找到。为了读者了解,我把相关代码复制在下面:

    if r.Delegator != nil {
    	if energy.Sub(*r.Delegator, prepaid) {
    		return baseGasPrice, gasPrice, *r.Delegator, func(rgas uint64) { doReturnGas(rgas) }, nil
    	}
    	return nil, nil, thor.Address{}, nil, errors.New("insufficient energy")
    }
    

    我们可以看到,系统首先检查是否有指定的交易代付者(r.Delegator)。如果有,它将尝试从代付者的VTHO余额中扣除交易的初始成本。如果余额不足,系统将停止处理交易并返回一个错误值。如果扣费成功,它将把代付者记录到和交易相关的运行上下文中,并将这个上下文传递给执行各个交易子句的代码。本文不会列出所有与VIP-191相关的变,感兴趣的读者可以自行查看源代码。

    演示

    和以往一样,我做一个交易费用代付机制(包括VIP-191和MPP)的演示。其中中用到了用到了两个将Connex接口运行在NodeJS环境中的开发工具:connex-framework和connex.driver-nodejs,大家可以在官方的github上找到。

    演示内容

    演示中使用了以下三个账号:

    1. 地址以0xd551开头的交易发送者SENDER
    2. 地址以0xe466开头的交易费代付者DELEGATOR
    3. 地址以0x9143开头的交易接收者RECIPIENT

    为了演示VIP-191,我创建了一个从发送者接收者的交易。其中,发送者代付者都对改笔交易签名,从而允许交易费由代付者来支付。

    为了演示MPP,我创建了两个从代付者发送的交易来调用内置合约Prototype,用以添加交易费代付关系和代付限额。然后,我创建一个由发送者代付者的交易来演示MPP的代付效果。这里需要注意的是,由于代付者是一个普通(非合约)账户,它的MASTER就是代付者本身。另外,交易只能由发送者发送至代付者,否则将无法使用MPP来代付费用。

    VIP-191的connex接口

    为支持VIP-191,connex的接口已更新。通过Connex接口来构建VIP-191交易是非常容易的。在正常构建TX的基础上,你仅需要做两件额外的事情:

    1. 创建自己符合以下规范的函数:

      function (unsignedTx: { raw: string, origin: string }): Promise
      

      该函数负责将数据发送给代付者,然后等待其响应,并最终返回一个Promise。如果“Resolved”了,该Promise会携带支付者对于TXID的有效签名。

    2. 然后将构造好的函数通过delegate方法,传递给用以构造交易的Connex.Vendor.TxSigningService的对象。例如,你可以添加这样一行代码:

      signingService.delegate(MyFunc);
      

    完成了,就是这样简单!

    演示结果

    我粘贴了演示代码的输出结果。

    --------------------------
    VIP-191
    --------------------------
    TXID       = 0xb58e1d1bf9da3414c24df51926003ebcbac7eb10246dd25548ccfd9202d4276e
    From       = 0xd55100eedb61f1e553a38c33a234ce07952c43f2
    To         = 0x91436f1E5008B2E6093E114A25842F060012685d
    GasPayer   = 0xe4660c72dea1d9fc2a0dc2b3a42107d37edc6327
    GasUsed    = 21000
    ...
    

    第一部分显示的是VIP-191交易的信息。可以看出实际的交易费的确用是由代付者来支付的。

    --------------------------
    MPP - Add User
    --------------------------
    TXID       = 0x508f50692c88054c5f8df982937b2871cae3cde002a4bc58e975277acca53c87
    From       = 0xe4660c72dea1d9fc2a0dc2b3a42107d37edc6327
    To         = 0x000000000000000000000050726f746f74797065
    GasPayer   = 0xe4660c72dea1d9fc2a0dc2b3a42107d37edc6327
    GasUsed    = 25074
    ...
    --------------------------
    MPP - Add Credit Plan
    --------------------------
    TXID       = 0x57329507daadb47a8ea68375577f9699fce3f1329df4703451be36d3804aa853
    From       = 0xe4660c72dea1d9fc2a0dc2b3a42107d37edc6327
    To         = 0x000000000000000000000050726f746f74797065
    GasPayer   = 0xe4660c72dea1d9fc2a0dc2b3a42107d37edc6327
    GasUsed    = 44811
    ...
    

    第二部分显示的是调用合约Prototype来激活MPP的两条交易信息。可以看到添加一个用户和一个代付限额的操作分别需要约25k和45k的Gas。

    --------------------------
    MPP - User Sends TX
    --------------------------
    TXID       = 0x87b68a65105f2cc5746b88e1e0fd5e1cf4f6d2dbf8459bb8ef2599957ffcb655
    From       = 0xd55100eedb61f1e553a38c33a234ce07952c43f2
    To         = 0xe4660c72dea1d9fc2a0dc2b3a42107d37edc6327
    GasPayer   = 0xe4660c72dea1d9fc2a0dc2b3a42107d37edc6327
    GasUsed    = 21000
    

    最后一部分显示了发送者发送给代付者的交易,通过MPP协议,确实由代付者支付的。

    文章链接

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

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


Log in to reply