钱包合约类型
您可能听说过 TON 区块链上不同版本的钱包。但这些版本究竟是什么意思,它们之间有什么区别?
在本文中,我们将探讨 TON 钱包的各种版本和修改。
共同概念
要打破这种紧张关系,我们首先应该明白,钱包并不是 TON 生态系统中的一个特定实体。它们仍然只是由代码和数据组成的智能合约,从这个意义上说,它们与 TON 中的任何其他角色(即智能合约)都是平等的。
与您自己的定制智能合约或其他任何合约一样,钱包可以接收外部和内部信息,发送内部信息和日志,并提供 "获取 "方法。 那么问题来了:它们提供哪些功能,不同版本之间有何不同?
您可以将每个钱包版本视为提供标准外部接口的智能合约实现,允许不同的外部客户端以相同的方式与钱包进行交互。您可以在主 TON monorepo 中找到这些 FunC 和 Fift 语言的实现:
基本钱包
钱包 V1
这是最简单的一种。它只允许您一次发送四笔交易,而且除了您的签名和序列号外不检查任何东西。
钱包源代码:
这个版本甚至没有在常规应用程序中使用,因为它存在一些重大问题:
- 没有从合约中获取序列号和公钥的简单方法。
- 没有
valid_until
检查,因此无法确保交易不会太晚确认。
第一个问题已在 V1R2
和 V1R3
中解决。R
代表 修订
。通常情况下,修订版只是增加获取方法的小更新;你可以在 new-wallet.fif
的更改历史中找到所有这些更新。在下文中,我们将只考虑最新的修订版本。
尽管如此,由于每个后续版本都继承了前一个版本的功能,我们仍应坚持使用它,因为这将有助于我们以后版本的开发。
持久内存布局
- seqno:32 位长序列号。
- public-key: 256 位长公开密钥。
外部信息正文布局
- 数据
- 签名:512 位长 ed25519 签名。
- msg-seqno:32 位长序列号。
- (0-4)模式:最多四个 8 位长整数,定义每条报文的发送模式。
- 最多 4 次引用包含信息的 cell 。
如您所见,钱包的主要功能是提供一种从外部世界与 TON 区块链进行通信的安全方式。seqno
机制可以防止重放攻击,而 Ed25519 签名
则提供了对钱包功能的授权访问。我们将不再详细介绍这些机制,因为它们在外部消息文档页面中有详细描述,并且在接收外部消息的智能合约中非常常见。有效载荷数据由最多 4 个 cell 引用和相应数量的模式组成,它们将直接传输到 send_raw_message(cell msg, int mode) 方法。
请注意,钱包不对通过它发送的内部信息进行任何验证。程序员(即外部客户端)有责任根据 内部信息布局 对数据进行序列化。
退出代码
退出代码 | 说明 |
---|---|
0x21 | 序列号 检查失败,已获得回复保护 |
0x22 | Ed25519签名 检查失败 |
0x0 | 标准成功执行退出代码。 |
Get 方法
- int seqno() 返回当前存储的序列号。
- int get_public_key 返回当前存储的公钥。
钱包 V2
钱包源代码:
该版本引入了 valid_until
参数,用于设置交易的时间限制,以防过迟确认。该版本也没有在 V2R2
中添加的获取公钥的方法。
与前一版本相比,所有不同之处都是由于添加了 valid_until
功能。增加了一个新的退出代码:0x23
,表示 valid_until 检查失败。此外,外部消息体布局中还新增了一个 UNIX-time 字段,用于设置事务的时间限制。所有获取方法保持不变。
外部信息正文布局
- 数据
- signature :512 位长 ed25519 签名。
- msg-seqno:32 位长序列号。
- valid-until:32 位长 Unix 时间整数。
- (0-4)mode:最多四个 8 位长整数,定义每条报文的发送模式。
- 最多 4 次引用包含信息的 cell 。
钱包 V3
该版本引入了 subwallet_id
参数,允许使用同一公钥创建多个钱包(因此可以只有一个种子短语和多个钱包)。和以前一样,V3R2
只增加了获取公钥的方法。
钱包源代码:
本质上,subwallet_id
只是在部署时添加到合约状态的一个数字。由于 TON 中的合约地址是其状态和代码的哈希值,因此钱包地址会随着不同的 subwallet_id
而改变。该版本是目前使用最广泛的版本。它涵盖了大多数使用情况,并且仍然干净、简单,与之前的版本基本相同。所有获取方法保持不变。
持久内存布局
- seqno:32 位序列号。
- subwallet :32 位子钱包 ID。
- public-key: 256 位公开密钥。
外部信息布局
- 数据
- signature :512 位 ed25519 签名。
- subwallet-id:32 位子钱包 ID。
- msg-seqno:32 位序列号。
- valid-until:32 位 UNIX 时间整数。
- (0-4)mode:最多 4 个 8 位整数,定义每个报文的发送模式。
- 最多 4 次引用包含信息的 cell 。
退出代码
退出代码 | 说明 |
---|---|
0x23 | valid_until 检查失败;交易确认尝试太晚 |
0x23 | Ed25519签名 检查失败 |
0x21 | seqno 检查失败;已触发回复保护 |
0x22 | subwallet-id` 与存储的标识不匹配 |
0x0 | 标准成功执行退出代码。 |
钱包 V4
该版本保留了之前版本的所有功能,但也引入了一些非常强大的功能:"插件"。
钱包源代码:
这一功能允许开发人员实现与用户钱包协同工作的复杂逻辑。例如,一个 DApp 可能会要求用户每天支付少量的硬币来使用某些功能。在这种情况下,用户需要通过签署交易在钱包上安装插件。然后,当外部信息要求时,插件将每天向目标地址发送硬币。
插件
插件本质上是 TON 上的其他智能合约,开发者可以根据自己的意愿自由实现。就钱包而言,它们只是存储在钱包持久内存 dictionary 中的智能合约地址。这些插件可以申请资金,并通过向钱包发送内部信息将自己从 "允许列表 "中删除。
持久内存布局
- seqno:32 位长序列号。
- subwallet-id:32 位长 subwallet-id。
- public-key: 256 位长公开密钥。
- plugins:包含插件的字典(可能为空)
接收内部信息
以前所有版本的钱包都是直接接收内部信息。它们只是简单地接受来自任何发件人的资金,而忽略内部信息正文(如果存在),或者换句话说,它们只有一个空的 recv_internal 方法。不过,如前所述,第四版钱包引入了两个额外的可用操作。让我们来看看内部信息体的布局:
- op-code?: 32 位长操作码。这是一个可选字段;任何信息正文中包含少于 32 位的操作码、错误的操作码或未注册为插件的发件人地址,都将被视为简单转账,与之前的钱包版本类似。
- query-id:64 位长整数。该字段对智能合约的行为没有影响;它用于跟踪合约之间的信息链。
- op-code = 0x706c7567,申请资金操作代码。
- TON 币:VARUINT16 申请的 TON 币数量。
- extra_currencies:包含所请求的额外货币数量的字典(可能为空)。
- op-code = 0x64737472,请求从 "允许列表" 中删除插件发送方。
外部信息正文布局
- signature :512 位长 ed25519 签名。
- subwallet-id:32 位长的子钱包 ID。
- valid-until:32 位长 Unix 时间整数。
- msg-seqno:32 位长序列整数 。
- op-code:32 位长操作码。
- op-code = 0x0,简单发送。
- (0-4)mode:最多四个 8 位长整数,定义每条报文的发送模式。
- (0-4)messages:包含信息的 cell 的最多四个引用。
- op-code = 0x1,部署并安装插件。
- workchain:8 位长整数。
- balance :VARUINT16 Toncoin 初始余额。
- state-init:包含插件初始状态的 cell 引用。
- body:包含正文的 cell 引用。
- op-code = 0x2/0x3,安装插件/删除插件。
- wc_n_address:8 位长工作链 ID + 256 位长插件地址。
- balance:VARUINT16 Toncoin 初始余额的金额。
- query-id:64 位长整数。
如您所见,第四个版本仍通过 0x0
操作码提供标准功能,与之前的版本类似。0x2
和 0x3
操作允许对插件字典进行操作。请注意,在使用 0x2
的情况下,您需要自行部署具有该地址的插件。相比之下,0x1
操作码还可通过 state_init 字段处理部署过程。
If state_init
doesn't make much sense from its name, take a look at the following references:
退出代码
退出代码 | 说明 |
---|---|
0x24 | valid_until 检查失败,交易确认尝试太晚 |
0x23 | Ed25519签名 检查失败 |
0x21 | seqno 检查失败,已触发回复保护 |
0x22 | subwallet-id 与存储的标识不匹配 |
0x27 | 插件字典操作失败(0x1-0x3 recv_external 操作码) |
0x50 | 申请资金不足 |
0x0 | 标准成功执行退出代码。 |
Get 方法
- int seqno() 返回当前存储的序列号。
- int get_public_key() 返回当前存储的公钥。
- int get_subwallet_id() 返回当前子钱包 ID。
- int is_plugin_installed(int wc, int addr_hash) 检查是否已安装定义了工作链 ID 和地址散列的插件。
- tuple get_plugin_list() 返回插件列表。