TON幕后:部署智能合约的经验教训,第二部分
这就是我们系列文章中关于在Telegram Open Network上集成支付通道的第二节。在第一部分中,我们介绍了这个网络,详细描述了我们的参赛体验,并解释了同步和异步智能合约是如何工作的。作为这一系列的又一新作,本文详细介绍了我们如何在9月份的TON竞赛期间构建了一个同步支付通道。这里,我们只会谈论Fift(TON的通用编程语言)和FunC(TON用于编写智能合约的编程语言)。
事实上,TON白皮书提供了关于支付通道的更多深入了解,但我们还是简单解释一下吧。
相关:揭秘TON幕后:部署智能合约的经验教训之第一季
相关:揭秘TON幕后:部署智能合约的经验教训之第一季同步支付通道允许使用链上资产在链下两个用户之间发送交易。在我们这个例子中——就是GRAMs。在一个用户无法通过链下欺骗另一个用户的情况下,交易执行得比执行一层区块链交易要快得多,因为只需要用到用户的设备来完成,无需写入区块链。
这里有两大基本操作:存入和取出。而取出是其中最具挑战性的。
要进行正确的取出,用户需要提供他们通道的最新状态信息。这个状态包括了每个参与者的步骤和数字签名,这意味着如果不经过双方都认可的签名,是不可能提供正确状态的。
要部署一个智能合约,你需要写一个Fift部署脚本,并将其编译成.boc(单元格包)文件。这样做创建了多个将相互链接的单元格。然后,GRAMs则需要发送到部署脚本执行过程中接收到的地址。一旦GRAMs出现在地址上,就把这个.boc文件发送到网络,合约就会被部署。
.boc要调用一个功能,你需要写一个将外部消息发送到已部署智能合约的脚本。
基本上,在TON上的任何东西都是一个带有一些引用的单元格。一袋单元格是由Telegram团队设计的一个数据结构。它是一种演员模型。更详细的内容请参阅TON白皮书:“一切都是一个单元格包。”当你部署一个单元格时,它将与另一个单元格互动。
每个点对点支付通道都是一个单独的智能合约。让我们来看看智能合约的各个部分。
相关:从开发者的角度看TON开放网络:期待什么
相关:从开发者的角度看TON开放网络:期待什么部署部分
使用序列化的Fift脚本来部署合约。它保存在一个.boc文件中,并通过TON Cli(网络的轻客户端)发送到网络。
.boc栈上的最新单元格是执行上述Fift脚本的成果。
常见的Fift部署脚本的组成部分包括但不限于:
在编译完 .boc 文件后,需要将一定数量的GRAMs发送到智能合约地址。必须将 .boc 文件发送到网络以初始化智能合约。发送的GRAMs数量取决于已部署智能合约的外部消息单元格的大小和计算量(不仅仅是代码)。从智能合约的余额中扣除气×气价。这是一个最小化支付部署所需气费的数额。
.boc存储的一个示例:
一个单元格最多可以包含1023位和四个其他单元格的引用。我们设法将整个存储放入一个单元格中,而不需要任何引用。我们的存储最多可以占用765位。
所有智能合约状态
0x0 —— 部署状态
0x00x1 —— 通道已打开并准备存入
0x10x2 —— 第一用户存入
0x20x3 —— 第二用户存入
0x30x4 —— 存入被阻止。可以向智能合约提供状态
0x40x5 —— 第一用户已经提供状态
0x50x6 —— 第二用户已经提供状态
0x60x7 —— 通道已关闭
0x7存入
存入功能接收到简单钱包(转账)的消息以及额外的主题有效载荷。
向通道存入GRAMs:
如果P2P通道智能合约的状态是 0x1 或 0x2 或 0x3,则可以调用存入功能。
0x10x20x3检查状态的FunC代码:

只有持有公钥的业主(写入初始存储)才能进行存入。智能合约检查将通过 recv_internal函数接收到的每个内部消息的签名。如果消息由公钥之一签署,则合约状态变为 0x2 或 0x3(如果是公钥1则为 0x2,如果是公钥2则为 0x3)。如果所有用户都存入了,则在同一函数调用时将合约状态变为 0x4。
recv_internal0x20x30x20x30x4负责更改合约状态的FunC代码:

退款
如果对手方未按时存入,则可以返还资金。
为此,用户需要通过外部消息提供他们的地址和签名。如果提供的签名属于公钥1或公钥2(存入资金的个人),并且合约状态是 0x2 或 0x3,则资金将被退还。
0x20x3负责验证退款申请的FunC代码:

取出
每个人应提供退出状态、该状态的签名以及主题消息的签名。
状态详情:
主题消息签名存储在主片段中,状态存储在单独的引用中,状态签名作为到“签名”引用的引用存储,以避免单元格溢出。
取出的步骤:
检查主题消息签名并确定参与者。
检查主题消息签名并确定参与者。

检查是否是参与者的轮次或者自最后一个状态进入后已过去24小时。将当前参与者的轮次(0x5 或 0x6)写入 contract status。
检查是否是参与者的轮次或者自最后一个状态进入后已过去24小时。将当前参与者的轮次(《0x5
坚强_if>)写入 contract status。0x50x6contract status举例来说,这是给《first_user_pubkey》所有人的主题消息的正确签名例子:
first_user_pubkey
然后,我们需要验证写入到状态的智能合约地址确实是实际的合约地址:

接下来,我们需要验证状态下的签名:

之后,有两个断言:

在 new_state_num > state_num 的情况下,我们需要将 new_state_num 存储在新的 time_to_send 相等于 now() + 86401(现在时间+24小时),并且也写入实际合约状态(如果第一名参与者调用,则为 0x5,否则为 0x6)。
new_state_num > state_numnew_state_numtime_to_sendnow() + 864010x6