Jungle 4 (Testnet)

自助式账户创建

VP-0002

摘要

本提案创建由网络治理所有的 new.vaulta 账户,并向其部署一个账户创建合约。它为钱包、交易所和各类指引提供一个标准的账户创建入口,其安全由网络共识保障,而非依赖某个运营方的密钥。该合约是自助式、由付款驱动的:用户以网络代币支付,合约便在单一步骤中创建其账户并购买该账户的 RAM,全程无需运营方参与。实施分为两次 BP MSIG(15/21)步骤:先创建该账户,再部署已公布哈希的合约代码。

依据

网络应当拥有一个标准的账户创建入口。账户是使用这条链的先决条件,而当钱包、交易所或指引把新用户引向 new.vaulta 时,所指向的合约其行为由 15/21 BP 共识保障,而不是依赖任何运营方的善意。

该功能本身已经过验证:同一份合约自 2025 年 10 月起就在 create.gm 上投入生产运行,在 Vaulta 主网上创建账户。所缺少的是网络的所有权。create.gm 由个人密钥控制:持有密钥的人可以在任何时刻不加通知地替换已部署的代码,因此即便审计过该合约的集成方,也无法确定收取其用户付款的究竟是什么代码;单个运营方可能停止服务或丢失密钥,链的正门也随之消失;而且由于接口只是一笔带备注的代币转账,把真正的创建合约与专为收款却不交付而制作的仿冒品区分开的,只有一个可信的名称。在 new.vaulta 之下,代码只能通过在执行前公开可见的 msig 变更,账户与网络共存续,网络也就此指明了唯一真实的入口。

机制

账户与权限

  • new.vaulta 通过 BP MSIG(15/21)创建,遵循与其他 *.vaulta 账户相同的模式。两项权限都在创建时的 eosio::newaccount 操作中设定,因此其后不再需要 updateauth
  • owneractive 均委托给 eosio.prods@active,其归结为 15/21 BP 共识。active 另包含 new.vaulta@eosio.code,因此合约可以以自身权限发送内联操作,即 eosio::newaccountcore.vaulta::buyram、退还多余付款的 core.vaulta::transfer,以及自身的 logcreation
  • 因此,未来所有 setcode/setabi 都需要同样的 15/21 批准。不存在阈值更低的管理权限,合约也没有任何需要这类权限的管理操作。
  • 所有合约参数(付款代币、字节配给量、代理合约)均为编译期常量。更改其中任何一项都是代码变更:需要新发布的提交和哈希,并由 15/21 setcode 批准。不存在配置操作。

合约接口

该合约由代币转账通知驱动,而非通过直接调用的操作驱动。

操作 权限 用途
transfer 通知(*::transfer 代币发送方 收到由 core.vaulta 发行的付款代币后,解析备注、创建账户、购买其 RAM 并退还多余部分
parsememo(memo)(只读) accountname-PUBLICKEY 解析为账户名和单一密钥权限
estimatecost()(只读) 返回创建一个账户的当前代币成本
logcreation(account, from, excess, ram, timestamp) new.vaulta 每次创建时发出的内联日志操作,用于索引。from 是发出付款转账的账户,未必就是被创建的账户

备注格式为 accountname-PUBLICKEYPUB_ 和旧版密钥格式均可)。每次创建支出 3,260 字节的 RAM 成本加上系统手续费,用户至少需发送该金额。多余部分会转入新账户。账户实际收到的 RAM 是这笔付款按市场价所能买到的数量,因此接近 3,260 字节而非恰好等于该数,网络的每账户基础配给量则在此之上另行叠加。

付款仅接受由 core.vaulta 发行的指定代币(A,4 位小数)。合约会放行而非拒绝以下转账:并非发给它的转账、由它自己发出的转账,以及来自 eosio.ramcore.vaulta 的转账,后者涵盖合约自身的 RAM 购买,以及 buyram 过程中 core.vaulta 解包付款时退回合约的那一段。除备注为 bypass 的情形外,转入任何其他代币都会被拒绝,发送方的交易随之失败。

备注为 bypass 的转账会被接受并留存,不创建任何账户;这正是直接向合约注资的方式。备注先于代币被检查,因此来自任何代币合约的任何代币,只要备注为 bypass 就会被留存。合约没有任何可将这些资金转出的操作,因此以此方式送入的余额会一直留在那里,直到 15/21 setcode 为其开辟出路。

若请求的账户名已存在,该笔转账失败,付款方保留其代币。

资金与运行成本

  • 一次性new.vaulta 账户本身及其部署代码所需的 RAM 由网络出资,作为创建 MSIG 的一部分,遵循新合约账户的通行做法。创建步骤以 eosio 为付款方购买 409,600 字节,该数值在部署所计费的约 390,000 字节之上留出了余量。合约不存储任何表,因此占用仅为代码和 ABI。
  • 持续成本:运行完全由用户出资。每次创建的 CPU 和 NET 由作为交易首个签名者的付款用户承担,所创建账户的 RAM 在交易内用用户自己的付款购买。一次创建过后,合约自身的代币余额保持不变,其 RAM 占用也没有增加,因为退还多余部分所产生的余额行由 core.vaulta 承担。new.vaulta 无需质押,其持有的余额只会是通过 bypass 备注送入的部分。

限制

合约不设任何速率或规模限制,这是刻意为之:

  • 没有任何补贴。用户支付全额 RAM 成本加系统手续费;不存在垃圾发送者可以耗尽的捐赠资金或额度池。
  • 对任何愿意付费的人而言,账户创建在协议层面本就是无需许可的。通过 new.vaulta 大规模创建付费账户,与直接用原始的 newaccount + buyram 相比,可能性完全相同,成本也完全相同。合约增加的只是便利。
  • 规模天然固定:每次创建支出相当于 3,260 字节的成本,每笔转账一个账户,多付部分退还给新账户。
  • 名称规则不变:作为创建方,new.vaulta 只能创建普通名称。所请求的名称必须恰好为 12 个字符,取自 a-z1-5,且不含点号,因为长度不足 12 的无点名称需要已中标的名称拍卖,带点名称需要后缀所有者的权限,而 new.vaulta 两者皆无。

使用方式

用户通过单笔代币转账创建账户:

  1. 调用 estimatecost(只读)获取当前价格。RAM 价格可能在估算与转账之间变动,因此惯例是按估算值再加一小笔缓冲发送:恰好等于估算值的付款可能在临界处失败,而这笔缓冲会退还给新账户,成为其初始余额。
  2. 附带备注 myaccount123-PUB_K1_...,将不少于该金额的网络代币转账至 new.vaulta,所用名称需符合限制一节中的规则。
  3. 合约以给定密钥作为 myaccount123 唯一的 owner/active 权限创建该账户,通过 core.vaultamyaccount123 购买 RAM,并将多付部分退还给 myaccount123

新建的账户持有 RAM,但没有 CPU 和 NET,因此无法自行转出退还的余额或执行 updateauth。钱包应当为该账户的首批交易联合签名或提供资源。

由一个密钥同时承担两项权限是有意的设计。单独的 owner 密钥、多重签名或基于账户的恢复方式,可在创建之后通过 updateauth 设置。

范围边界

  • 本提案仅涉及 new.vaulta 账户及其账户创建合约。
  • 无收入机制:合约仅收取 RAM 成本加系统手续费,并将任何多余部分退还给所创建的账户。

待解决问题

无。

后续步骤

合约源代码发布于 contracts/create

部署步骤所提议的产物如下:

  • 提交:d103fdcea03bf2a6e3e0ff5ed893ec10389c29a2
  • 代码哈希:7166a16ea0d6db98fe18cde9a2d56b30f091dcbb19efcd0a0b5fa0e510f272c8,对应 38,534 字节的 WASM
  • ABI 哈希:4f47d205f7cc990efb548d002f324e9c23c8a5ab65012dbab4430973e3c7ffdb,对应 610 字节的序列化 ABI
  • CDT:v4.1.1,以构建参数的形式固定在工具链容器中

重新构建的方法是检出该提交并运行 make build/create/production,编译在上述容器内进行。shasum -a 256 contracts/create/build/create.wasm 得到代码哈希;将 contracts/create/build/create.abi 序列化为二进制形式得到 610 字节,其哈希即 ABI 哈希。这两个值也正是 create.gm 在 Vaulta 主网上运行的内容,由 get_raw_abicode_hashabi_hash 返回。

  • 作者在提出部署步骤之前,于本节记录代码哈希、其构建所依据的提交,以及可复现该哈希的 CDT 版本,以便 BP 自行构建并比对。
  • 作者为每个步骤的交易加上回溯引用,并将其固定到承载本提案最终文本的提交。