这是「关于唯链雷神区块链,你可能还不知道那些事儿」系列的第三篇,文末附有前两篇链接。在本篇唯链首席科学家 Peter Zhou 阐述并演示了唯链雷神区块链的「指定交易费代付协议」(VIP-191),该机制目标是让普通用户在使用去中心化应用的过程中,无需直接支付交易费用的机制。

原文标题:《关于唯链雷神区块链,你可能还不知道的那些事儿(第三篇) - 指定交易费用代付(VIP-191)》
作者:Peter Zhou,唯链首席科学家,英国南安普顿大学计算机博士,于 2017 年加入唯链,主要负责唯链雷神区块链的科研研发及知识产权保护等工作

区块链普及尝试:技术详解唯链指定交易费代付协议 VIP-191Peter Zhou,唯链首席科学家,英国南安普顿大学计算机博士,先后以研究员和资深研究员的身份任职于英国肯特大学和芬兰奥卢大学,参与了欧盟和芬兰科学院重要科研项目,拥有十年计算机科研研发以及在国际一流学术杂志和会议上发表科研成果之经验,于 2017 年加入唯链,主要负责唯链雷神区块链的科研研发及知识产权保护等工作

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

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

MPP 是如何工作的

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

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

区块链普及尝试:技术详解唯链指定交易费代付协议 VIP-191

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

区块链普及尝试:技术详解唯链指定交易费代付协议 VIP-191

假设现在 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 允许交易发送方寻找任意费用代付方,而不必非是交易指向的合约拥有者。 就像在文章 「VIP-191:实现大规模落地的关键」 里阐述的那样,该协议可以使像 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 协议的交易新增了判断交易费用支付方的逻辑

交易模型的扩展

我们重新定义了在原交易模型中的 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 协议,确实由 代付者 支付的。

附:「关于唯链雷神区块链,你可能还不知道那些事儿」系列

第一篇《区块链如何对抗重放攻击?硬核详解唯链 TXID 方案
第二篇《区块链如何保证交易安全?硬核详解唯链「强制交易依赖性」功能

来源链接:bbs.vechainworld.io