EasyGas

 

 

问题背景

任何想提交 Starcoin 交易的人都需要有 STC 来支付 Gas 费用。这需要使新用户在开始使用任何 DApp 之前,先购买 STC。 这对于没有加密货币经验或者只有其他 Starcoin token 的用户来说非常不方便。从想在 Starcoin 上开发应用的开发者团队角度来看,他们也必须先教育用户使用 STC,才能使用他们的应用,也造成了成本。这里讨论几种不同的方案来实现 Starcoin 中用任意 Token 支付 Gas。

在 Starcoin 中,所有 Token 都是通过同一套标准通过合约来定义的,它们的地位是相同的,不存在类似于 Ethereum 上的原生资产和 Token 资产有区别的问题。同时 Starcoin 的交易中给 Gas Token 预留了字段,为实现这个目标降低了难度,但是依然有几个难题需要克服。

下面,我们通过交易执行的生命周期的四个阶段以及给矿工分 Gas 费的几个角度,分别分析这些难题。

 

  1. 交易验证:用户在提交交易到交易池的时候,节点需要先校验交易。其中重要的一项是检查用户的资产余额是否够支付 Gas。这项检查可以防止攻击者低成本制造大量的垃圾交易堵塞网络。但执行交易验证的时候,并不能修改状态,不能进行交换,只能进行无状态计算,所以如何验证以非 STC 的 Token 来支付 Gas 的交易的合法性是个难题。这部分的代码在:TransactionManager::prologue 中。

  2. 交易排序:交易通过验证,提交到交易池后,需要通过 Gas Price 进行排序,而这个排序必须基于一个同样的计价单位,也就是说,排序时需要将其他的 Token 的换算成 STC 来进行排序。

  3. 交易执行:执行部分理论上没有影响,因为执行交易的时候,只关心交易耗费的 gas 数,并不关心 gas price。

  4. 交易收尾:增加用户的 sequence_number ,执行扣除 Gas 费用等操作。需要在这里确定,是要将 Token 兑换成 STC 来支付 Gas,还是直接扣除用户指定的 Token。这部分的代码在 TransactionManager::epilogue 中。

  5. 矿工分 Gas 费:用户支付的 Gas 费会统一收集在一个 Gas 池中,给矿工延迟 7 个区块发放。这里遇到的难题是,如果要求矿工自己接受其他 Token,则会有多个 Gas 池子需要做分配。这部分代码在 BlockReward::process_block_reward 中。

 

整体上可能方案可以分为两种,一种是在交易执行的过程中将 Token 置换为 STC,最后扣除 Gas 费的时候实际上扣除的是 STC,后续流程无影响,简称“兑换方案”。另外一种是给矿工分配的 Gas 费就是用户支付的 Gas 费 Token,不做兑换,简称“矿工方案”。这两种方案下,交易验证和排序阶段的方案是独立的,因为交易验证和排序阶段并不能预先执行兑换操作,必须依赖一种纯计算的方案,所以先描述一下这两个阶段的方案。

 

前置方案

交易验证

交易验证时,实际上只是执行了一个检查:

let max_transaction_fee = txn_gas_price * txn_max_gas_units; assert!(balance<TokenType>(txn_sender)>max_transaction_fee);

max_transaction_fee 是 gas price 乘以该交易可能耗费的最大 gas 数的结果,用户的余额要大于这个数。当前的逻辑本身是支持任意 Token 的。但存在一个可能的漏洞,如果该 Token 是恶意用户随意发行的,没有交换价格,依然存在恶意攻击的可能。所以需要一个 Oracle,获取该 Token 和 STC 的交换价格,这里的验证只要确认它有交换价格即可。

交易排序

交易排序的执行并不在合约中完成,是节点逻辑中执行的。这时候需要将交易中的 gas price 换算成以 STC 计费的 gas price,换算方式依然可以使用前面的 Oracle 来进行。

Gas Token 的价格 Oracle 方案

需要 token 兑换的流动性提供方或者在stdlib中实现一个价格的 Oracle, 从而验证该Token是否是有价值的。

兑换方案

兑换方案有两个关键点,什么时候兑换以及如何兑换。

用户在执行过程中自己兑换

比如用户正好执行的就是一个 XToken → STC 的兑换交易,执行后用户恰好就有 STC,这时候扣除 Gas 费的时候就直接可以用 STC。这种方案比较简单,只需要解决前置方案的难题即可实现,并且用户可以通过任意的 swap 进行兑换。但缺点是用户需要构造特殊的交易来进行兑换。

系统在交易收尾阶段自动兑换

这里兑换遇到的关键难题是在哪里兑换。starcoin-framework 系统合约中,不能反向依赖 swap 的代码,所以无法直接调用 swap 进行兑换。有以下可能的方案:

  1. starcoin-framework 内置一个专门针对 gas 的 swap,由项目方注入资金,通过 Oracle 价格刚性兑换。(TODO 需要进一步推演)

  2. 通过 native 方法实现 epilogue 对 swap 的调用,解决反向依赖的问题。(这种调用是否可以在同一个虚拟机执行的上下文里完成,需要调研)

矿工方案

矿工方案下,关键点变成了如何分发 Gas,以及考虑到矿工如果不愿意的情况,如何处理。

  1. BlockReward 中维护多个 Gas 交易费池。

  2. 分发 Gas 时需要遍历 Gas 交易费池,所以将交易费池实现为数组?考虑到当前 Gas 费时延迟发放给矿工的,已经是一个数组了,所以需要评估下复杂度。

  3. 如果矿工不愿意接受其他 Token?

    1. 矿工可以拒绝打包非 STC 作为交易费的交易,但这个不利于整个生态和用户体验,是我们不希望看到的,但矿工可以通过修改节点代码实现整个逻辑,所以整个方案要考虑矿工的接受度。

    2. 矿工接受到 Token 后可以立刻发起一个交易进行兑换,但由于 Gas 分发延迟,无法保证兑换的比例。

    3. 提供链上设置,让矿工标志是否接受其他 Token 作为 Gas,如果不接受就使用上面的兑换方案?这种相当于同时需要实现两套方案,感觉复杂度更高。

 

其他方案

其他链也考虑过类似的方案,比如 Ethereum 的 Gas proxy 方案等。

  1. Gas proxy

    用户的原始 tx (meta transaction)在提交到链上之前, 先经过一个 gas proxy, 其负责支付交易的 gas 费用,并把用户的 script 组装为一个真正的 transaction 并提交上链 用户的gas token 可以 gas proxy 上兑换为stc。
    gas proxy 相当于一个gas 支付中转服务。