再聊 token 标准,我们为什么要信任 SNS 下发行的 token ?
同质化 token 标准是任何生态网络的基础,据我们所知,有很多开发者正在因为标准的不确定性而做无谓的等待,这正在慢慢的毒死 IC 的生态。
在社区中我们也看到了一些关于 token 标准的讨论,但我们已经厌倦了对于实现细节不痛不痒的 BULLSHIT,我们应该直视一些核心问题:
我们凭什么相信一个 token 合约:IC 没有主链数据,对余额状态、交易历史的等状态值,如何保证不被篡改?从而实现密码学上的可验证?跨容器间的调用由合约发起,如何防止容器伪造用户发起交易?
可用性问题:ICP 的账本容器的设计为什么要沿用在 SNS 中?在没有approve/transferFrom 等功能的 token 标准下,开发者如何使用 token ?
IC 基础设施提出了挑战
众所周知,以太坊的区块链结构中会有一个区块链表,区块中包含着交易,是区块链中比较经典的结构。但IC完全不同,IC没有严格意义上的主链数据。
从社区的 token 标准的设计中,开发者们从以太坊中吸收了不同的 token 标准的经验,比如一些非常基础的东西,比如 name、symbol、decimals、totalSupply 这些基本要素,以及balanceOf、allowance、approve、transferFrom、transfer 这些非常基本的接口。这让开发者能更简单地将 token 引入业务,不然任何的生态都无从谈起。
而另一个很重要的部分就是如何做调整以适合 IC 网络的特性,比如 IC 没有历史日志,如果开发者自制实现日志,而 IC 的智能合约又可以被更新,那么如何保证 token 的交易是基于密码学去信任的,而不会被攻击者修改历史?并且 IC 的智能合约间的相互调用由合约本身发起,如果一个 token 合约与另一个 DeFi 合约交互,攻击者甚至可以无需用户签名,就发起一起假交易盗取价值。
主链数据的缺乏导致用户很难去信任一个 token 合约,因为这与加密货币最核心的价值-TrustLess相违背。除非我们重新建立一个能够轻松地被验证的链式结构。 token 标准下就需要包含交易记录等结构。
异步架构导致了 IC 没有传统的原子性,不过这由底层框架的特点,而不是 token 标准应该去解决的问题。所以在 token 标准的设计中不应该去考虑解决IC的原子性问题。
但是原子性这个问题有没有解决方案呢?当然,在分布式世界中有两种解决方案:分布式事务和sagas。我们认为 sagas 是较好的方能,因为其对开发没有侵入性。侵入性指用代码去适配分布式的事务来解决原子性的问题,而 sagas 完全不需要开发者的代码去关注原子性本身,是通过外部的流程协调解决的。
SNS 并无法解决问题
目前我们还没有看到具体的实现代码,但在论坛讨论与对 ICP 账本的理解中,我们可以初步理解 SNS 的当前方案:
NNS 计划发布专用与发行 token 的 SNS 专用子网,一个子网最多能运行 15 个 token 系统(包含治理、账本、配置三个合约),允许第三方开发者自行部署,可以看成是 NNS 的社区版本。基金会似乎准备在 SNS 中让开发者沿用 ICP 账本合约的方式发行 token 。
在 IC 的浏览器中已经可以看到 SNS 子网 x33ed,当前有 34 个节点在运行,但目前还没有容器部署。当然这个子网和其他的子网在基本架构上一致。
因为 token 合约沿用了 ICP 的账本实现方式,而 ICP 的账本设计并没有很好地考虑到合作做交易,这将带来更大的易用性问题:在 ICP 的这类账本合约中,甚至没有 approve、transferFrom 这类接口,因此无法被其他合约直接使用。如果开发者使用 SNS 发行 token ,他必须像使用 wicp 一样,被迫为自己发行的通证建立 wrap token 合约,然后再用于 dapp。
通过 sns 发行的 token 可能只能被直接用于中心化交易所中交易(中心化下的黑色笑话),而想在 swap 或 dapp 中使用这个 token ,就必须使用它的 warp token。而 Warp token 合约本身又必须是类似 ERC20 的 token 合约,本身就存在状态被篡改等问题。而在SNS 的加入反而为 token 带来了来自 SNS 网络本身与 wrap token 合约的双层风险,这反而让 token 变得更加不可信(严格来说,我们甚至很难信任 ICP 的账本)。开发者还不如直接使用类 ERC20 标准发行 token ,SNS 与账本合约的沿用只不过是 Shit Mountain 上的另一层 Bullshit。
实现密码学自证
我们并不想单纯的抱怨,而是想让 IC 的生态更好的发展,因此我们尝试提出一些思路与社区讨论。
IC 的 token 标准的核心问题是,IC 没法提供像以太坊那样的链式数据结构,来证明交易是可信的、是经过用户签名的。因此 token 的发行方没办法向用户和开发者提供密码学级别的证明。
容器升级时 token 的历史记录可以被篡改,攻击者在无需用户签名的情况下通过升级容器发送假交易等等,这带来了巨大的风险。
而回顾 crypto 的发展历史,密码学级别自证与去信任,才是价值的基础,不然一切都是建立在流沙上。
链式证明结构
如何解决这个问题?也许我们可以在 token 合约内,模仿 BTC 的设计,实现一个区块链式结构的数据库。通过内置实现了一个区块链数据结构来完成 token 标准所有交易记录的证明,通过ledger blockchain 来完成自证。
在经典的 bitcoin 链式证明结构中,后一个区块会包含前一个区块的哈希值,链上任何一处发生改动,后续所有区块的哈希都会发生变化,从而实现对整个交易历史记录的可信证明。
我们可以为 token 合约添加一个 Trust Machine 层,比如我们创建一个Trust Machine的canister,并移除它的 controller, 它只用来存储 token 的block height 和 blockhash,来作为 token 不可篡改的三方存证 canister 。
在合约没有 Controller 且代码开源的情况下,数据只能存而不能删除和修改,trust machine 容器本身就可以取信于社区与开发者。如果把 token 的 blockhash 和 blockheight 存在 trust machine 合约中,token 就可以实现密码学自证性,从而解决信任危机(因为有一个三方存证trust machine 不可篡改)。
时间证明
trust machine 需要提供一个额外的时间证明。这里可以利用 ICP 的 Ledger 容器,在任何时刻都读取都可以得到其 last block 的 last blockhash 和 last blockheight。
在 trust machine 中,当一个 token 提交 blockhash 时,trust machine自动取当前 ICP Ledger 的 last block的 blockhash 和 blockheight。这就可以就可以完成对 token 的时间、数据两个层次的证明结构。
额外的好处
实际上我们是在一个合约中构建了一条附带状态证明的区块链。
trust machine,这是一条链中链。如果我们能将多个 token 的对应同一个 trust machine 合约,能否实现这些 token 之间交易的原子性?这种思路其实与 NEAR 上的 EVM 项目 Aurora 很像。
token的自描述
在以太坊上需要在很多中心化地方提交token的信息,会带来不一致性。而IC由于有内置的存储,就可以在链上维护blockchain的更新,在Dfinity上就可以把这个设计融入进来。这些是对 token 标准接口层次的考虑。
接口设计
我们不应该继续沿用 ICP 账本的 token 接口设计方式,不然开发者只能去使用 wrap 过的 token 进行交易,这又添加了一层风险。基于此,我们建议有下列设计:
approve/transferFrom
approve/transferFrom对于防止重复支付有很大的作用。如果有 approve/transferFrom 这样的接口,只要 approve 给市场一定的额度(比如订单是100 ICP,就 approve 给市场100 ICP 的额度),创建订单的同时执行一个 transferFrom,只要 transferFrom 成功了,订单的创建和发送商品给用户就一步结束了,对于开发者来更简单。
Nonce
这个不需要了,实现了 blockchain 之后的 token,对相同的交易-相同的收发人、相同的金额、相同的时间戳,会得到相同的 tx hash,会被 blockchain 拒绝,因此仅仅需要一个时间戳(因此删除了原来token的update方法中的nonce参数,用一个可选的时间戳来替代-这种方式更加简单易用),client 调用就可以避免重复发送交易以及被 replay 攻击。
数据的储存
因为没有主链数据,容器仅维护自己的数据,因此我们需要存储 token 的交易历史与状态,这很好理解。
IC 支持容器内置的存储,可以用这部分存储来完成自己记录的存储来替代 EVM 的 event。
一个容器的存储空间分为运行时的 4G 内存和 8G 的 stable memory,但对 USDT 这样有大量交易历史的 token 来说,一个容器的存储空间远远不够,需要考虑扩容。
在这里我们提出了一个思路,在 token 里面记录一个 index 的映射,通过一个额外的归档容器,在 token 内部通过一个索引指向历史记录的存储。比如,历史交易记录已经大于 2000 条时,会每次迁移 1000 条到归档的容器中,就可以缓解当前 token 所在容器的存储焦虑。每个容器中有4G的内存,索引占的位置非常有限,通过索引结构指向容器,理论上有无限存储的可能性。
自动扩容时会自动创建存储容器,只有当交易大于等于特定数目时才会创建第一个扩展的容器,这能最大程度地节约开发者的 cycle 。
当 token 的 cycles 不够的时候,创建容器会遇到障碍,为此可以加入 fallback 的策略,默认把交易记录先存在 4G 的本地容器存储当中,cycles 够了之后会自动转移去归档容器。
token默认实现了一个metrics,可以获取token的cycles余额,因此每个token都是可被监控cycles情况,从而得到cycles不足的预警。
归档失败和自动扩容失败等所有失败的场景都经历了严格的测试,确保在生产环境中能够正常工作。
小助手微信号
入群请添加
联系我们
ICPL Twitter
https://twitter.com/icpleague_com
ICPL Medium
https://medium.com/icp-league
ICPL Telegram
https://t.me/ICPL_en
ICPL 论坛
https://icpleague.com