TON的幕后花絮:部署智能合约的经验教训,第2部分

这是我们在Telegram开放网络上集成支付渠道的系列文章中的第二篇。在第一部分中,我们介绍了网络,详细介绍了比赛经验,并解释了同步和异步智能合约的工作方式。作为该系列的下一个补充,本文详细介绍了我们在9月TON比赛期间如何在网络上建立同步支付渠道。在这里,我们仅讨论Fift(TON的通用编程语言)和FunC(TON用于编写智能合约的编程语言)。

TON白皮书提供了有关支付渠道的更深入的信息,但我们将再次简要说明它们。

相关:TON的幕后花絮:部署智能合约的经验教训,第1部分

同步支付通道允许使用链上资产在两个用户之间链下发送交易。在我们的情况下-GRAM。一方不可能欺骗另一条链外交易,并且交易比执行第一层区块链交易要快得多,因为仅使用用​​户设备即可完成交易而不必写入区块链。有两个基本操作:入金和出金。撤出是实施最具挑战性的一项。

为了正确退出,用户需要提供有关其频道状态的最新信息。状态由每个参与者的步骤和数字签名组成,这意味着无法使用未经双方批准的数据来提供正确的状态。

要部署智能合约,您需要在Fift中编写一个部署脚本并将其编译为.boc(单元包)文件。这样做会使多个单元相互链接。然后需要将GRAM发送到在部署脚本执行过程中收到的地址。一旦在地址上有GRAM,将.boc文件发送到网络,合约将被部署。

要进行函数调用,请编写一个脚本,该脚本将向已部署的智能合约发送外部消息。

基本上,TON上的任何内容都是具有某些引用的单元格。一袋信元是由Telegram团队设计的数据结构。这是一个演员模型。 TON白皮书提供了更多详细信息:“一切都是一袋牢房。”您正在构建一个单元,该单元在部署时将与另一个单元交互。

每个点对点支付渠道都是一个智能合约。让我们看一下智能合约的细分。

相关:电报开放网络的期望:开发人员的观点

部署部分

序列化的Fift脚本用于部署合约。它被保存为.boc文件,并通过网络的轻客户端TON Cli发送到网络。

堆栈上的最新单元是执行上述Fift脚本的结果。

Fift部署脚本的通常部分包括(但不限于):

  • 智能合约的代码作为单个单元格(通常用FunC编写,然后编译为Fift ASM代码,并使用path-to-compiled-asm.fif包含在主.fif文件中)。
  • 智能合约的初始存储(请参见下文)。
  • 新的智能合约地址(来自智能合约初始状态的哈希,还包括智能合约代码单元格和初始存储单元格)。
  • recv_external函数的第一次调用的参数(参数的数量和类型取决于协定)。
  • 一个用于初始化的外部消息单元,它将被序列化为字节并打包到.boc文件中,该文件包含第1至4点中的所有数据以及一些尚缺乏文档的其他数据。
  • 编译.boc时,需要将特定数量的GRAM发送到智能合约地址。必须将.boc文件发送到网络以初始化智能合约。 GRAM的数量取决于部署的智能合约的外部消息单元(不仅是其代码)的计算大小和数量。天然气×天然气价格是从已部署的智能合约余额中获取的。这是部署期间支付天然气所需的最低费用。

    存储的表示形式:

  • seqno 32位
  • contract_status 4位
  • first_user_pubkey。第一方的公开密钥256位
  • second_user_pubkey。第二方的公开密钥256位
  • time_to_send。提交第一个实际状态后的发送时间32位(有效期至2038年)
  • depositSum。两个参与者的存款总和,最多121位
  • state_num 64位。当前发生的状态数量
  • 一个信元最多包含1023位,并包含对其他信元的四个引用。我们能够将整个存储空间放在一个单元中,而无需一个参考。我们的存储最多可占用765位。

    所有智能合约状态

    0x0-部署状态

    0x1 —通道已打开并准备存入

    0x2-用户存款1

    0x3-用户存款2

    0x4 —存款被冻结。可以为智能合约提供状态

    0x5-用户1提供了状态

    0x6 —用户2已提供状态

    0x7 —通道已关闭

    存入

    存款功能从简单的钱包(转账)接收消息,并带有额外的有效载荷。

    将GRAM存入通道:

  • 用户生成一个附加的正文有效载荷,该正文有效载荷包括消息(例如1位)及其在单独的.fif文件中的签名。
  • 主体有效负载被编译为.boc文件。
  • 主体有效负载从此.boc文件加载到.fif文件中,作为主体单元“传输”参考(.fif负责从钱包中传输GRAM)。
  • 当已编译的.fif文件发送到网络时,将使用参数(通道的存款金额和目标地址)调用recv_external函数。
  • send_raw_message函数被执行。存放的GRAM和其他主体有效负载将发送到P2P通道智能合约目标地址。
  • P2P通道智能合约的recv_internal函数被调用。 GRAM由通道合约接收。
  • 如果P2P通道智能合约的状态为0x1或0x2或0x3,则可以调用存款功能。

    检查状态的FunC代码:

    1“ src =” https://s3.cointelegraph.com/storage/uploads/view/fec2786551d613f2491a5d1d0ed9e80e.png“ title =” 1

    只有公开密钥(写在初始存储中)的所有者才可以进行存款。智能合约检查将通过recv_internal函数接收的每个内部消息的签名。如果消息是由公共密钥所有者之一签名的,则合约状态将更改为0x2或0x3(如果是公共密钥1,则为0x2;如果是公共密钥2,则为0x3)。如果所有用户都进行了存款,则在同一函数调用中合约状态将变为0x4。

    负责更改合约状态的FunC代码:

    2“ src =” https://s3.cointelegraph.com/storage/uploads/view/8e0a38712f545a84e75550886b981c54.png“ title =” 2

    退款

    如果交易对手未按时存入资金,则可以退还资金。

    为此,用户需要通过外部消息提供其地址和签名。如果提供的签名属于公钥1或公钥2(进行存款的人)且合约状态为0x2或0x3,则资金将退还。

    负责验证退款申请的FunC代码:

    3“ src =” https://s3.cointelegraph.com/storage/uploads/view/b7aa62614b98dffc5bfb0a7765037cff.png“ title =” 3

    退出

    每个人都应提供退出状态,该状态的签名以及正文消息的签名。

    州详情:

  • 智能合约地址(排除从前具有相同参与者的P2P通道进入正确状态的可能性)。
  • 第一位参与者的最终余额。
  • 第二个参与者的最终余额。
  • 州号。
  • 正文消息签名存储在主切片中,状态存储在单独的引用中,状态签名作为对“签名”引用的引用存储,以避免单元溢出。

    提款步骤:

  • 检查正文消息签名并确定参与者。

  • 4“ src =” https://s3.cointelegraph.com/storage/uploads/view/2bc23eac21afee3c36f71d8adaa342c7.png“ title =” 4

  • 检查是否轮到参与者了,或者自从上次进入状态以来已经过了24小时。将当前参与者的回合(0x5或0x6)写入合约状态。

  • 为first_user_pubkey的所有者正确的正文消息签名的示例:

    5“ src =” https://s3.cointelegraph.com/storage/uploads/view/be5d23cf5c69a7b5a13fb84d54731e2c.png“ title =” 5

    然后,我们需要验证写入状态的智能合约地址是否为实际的合约地址:

    6“ src =” https://s3.cointelegraph.com/storage/uploads/view/34c973fd686dfd3e92390527df929504.png“ title =” 6

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

    7“ src =” https://s3.cointelegraph.com/storage/uploads/view/9bf517ce3c99327ce8297a8438b8c32d.png“ title =” 7

    之后,有两个断言:

  • 从存储中存入的金额应等于参与者总余额的总和。
  • 新输入的状态号必须大于或等于前一个。
  • 8

    在new_state_num> state_num的情况下,我们需要存储new_state_num,其新的time_to_send等于now()+ 86401(距当前时间24小时),并写入实际合约状态(如果第一个参与者拨打电话,则为0x5,否则为0x6)。 。

    在另一种情况下,如果new_state_num == state_num,则需要对“签名”引用添加另外两个引用,其中每个参与者的地址均在其地址下方。

    如果签名正确,则会从一个地址撤回GRAM,并将其放入所有者的地址。

    9“ src =” https://s3.cointelegraph.com/storage/uploads/view/80e8e8d78e61f77b9a20ab15955be261.png“ title =” 9

    每次成功调用一次,我们都需要存储所有存储数据,即使它们没有变化。

    未解决的问题

    假定第一位用户部署了合约,而参与者则同意了佣金。在我们的案例中,关于佣金的协议正在链下达成。

    考虑到玩家可以写下无关的状态并在此之后记录实际状态,我们还没有弄清楚如何计算总佣金。请记住,每次成功调用recv_internal或recv_external函数时,我们都需要从P2P通道智能合约中支付费用。

    如前所述,我们需要在不可反弹的未来智能合约地址中添加一些GRAM,以便对其进行初始化。

    在比赛的最后一天,TON的开发人员使用新功能对stdlib.fc库进行了提交,该功能允许获取实际的智能合约余额。

    10

    欢迎提出解决此问题的建议

    结论

    FunC和Fift允许任何开发人员访问软件工程的低端世界,为已经习惯以太坊或任何其他智能合约平台的区块链开发人员提供新的机会和功能。 TON是分片的区块链非常重要,因此在其上实施智能合约更具挑战性。例如,以太坊的合约是同步运行的,不需要处理诸如等待其他合约的答复之类的情况。

    智能合约通信的异步方式是使其具有可伸缩性的唯一选择,TON具有这些选择。我们的解决方案最终要比Solidity难以实施,但是总会有一个权衡。绝对有可能在TON上建立高级智能合约,并且TON团队处理该合约的方式令人印象深刻。我们期待看到更多有助于部署和建立FunC合约的库和工具。

    我们非常喜欢所有任务,并希望我们有更多的时间来执行所有任务。尽管如此,我们还是在TON竞赛中获得了两个奖项:最佳同步支付渠道第一名和最佳异步支付渠道第三名。

    我们将在第三部分中分享我们自己的个人反馈。

    本文表达的观点,想法和观点仅是作者的个人观点,不一定反映或代表Cointelegraph的观点和观点。

    本文由Nick Kozlov和Kirill Kuznetsov共同撰写。

    尼克·科兹洛夫(Nick Kozlov)是Button钱包的CTO和联合创始人,Button钱包是软件开发人员和研究员,也是TON竞赛的获胜者之一。

    Kirill Kuznetsov是Button钱包的共同创始人,也是TON竞赛的获胜者之一。

    资讯来源:由0x资讯编译自COINTELEGRAPH,原文:https://cointelegraph.com/news/behind-the-scenes-of-ton-lessons-learned-on-deploying-smart-contracts-part-2。版权归作者所有,未经许可,不得转载
    提示:投资有风险,入市需谨慎,本资讯不作为投资理财建议。请理性投资,切实提高风险防范意识;如有发现的违法犯罪线索,可积极向有关部门举报反映。
    你可能还喜欢