TON 是一个非常现代的区块链,为智能合约开发带来了一些激进的新想法。它是在以太坊推出后相当长一段时间才设计的,并且有幸了解 EVM 模型中哪些内容运行良好以及哪些内容可以改进。
如果您之前有一些智能合约经验,您可能熟悉以太坊的 Solidity 语言及其 EVM。在学习 TON 开发时,您应该意识到某些设计差异,这些差异使得 TON 上的行为与您的预期完全不同。这篇文章的目的是强调其中一些差异,并为您提供一些关于它们产生原因的一般想法。
从数据转向大数据
了解 TON 的主要目的是,它旨在将区块链带到地球上每个人的手中。这意味着规模巨大——数十亿用户每天发送数十亿笔交易。
将此视为从数据到大数据的转变。当您需要存储餐厅的菜单时,SQL 数据库是一个不错的选择 - 它可以运行强大而灵活的查询,因为所有数据都随时可用。当您需要存储地球上每个人的 Facebook 帖子时,SQL 数据库可能不是最佳选择。这些大量的“大数据”必须被积极地分片——限制了您可以运行的查询的灵活性。不同的目的有不同的权衡。
以下是 TON 区块链的六个独特方面,可能会让大多数 Solidity 开发人员感到惊讶:
1.你的智能合约需要支付租金并向其用户收费
区块链作为一种不可变且永恒的数据存储,在纸面上是一个很棒的概念,但正如我们很快就会看到的,它的扩展是相当不切实际的。以太坊的收费模式受到银行收费模式的启发。您想汇款,需要向银行支付交易费。谁负责支付费用?发起转账的用户。
在区块链上存储数据怎么样——例如为新的智能合约部署字节码?以太坊模型规定发送部署交易的人将支付费用。但这笔费用只支付一次,但如果链上的数据是永恒的,矿工将不得不继续支付基础设施成本才能在未来几年保留这些数据。这些费用经济学不会加起来,如果你试图将它们扩展到数十亿用户,它们最终会崩溃。
从银行转向即时通讯工具
TON 上的费用模型有很大不同。TON 并非模拟银行账户,而是受到*即时通讯*等网络应用程序的启发。谁支付在 Facebook Messenger 上发送消息的费用?绝对不是发起转账的人。应用程序开发商 Facebook Inc(或 Meta Inc,我不关注,我自己使用 Telegram)实际上承担了成本,并且由 Facebook Inc 以某种方式收回这些成本并为自己提供资金。
相应地,在 TON 中,dapp 本身需要支付自己的资源成本。每个智能合约都持有 TON 代币余额,并使用该余额来支付租金。如果智能合约的钱用完了,它最终会被删除(不用担心,一切都是可以恢复的)。请注意,支付链存储费用并不是一次性的,租金支付是连续的。如果您只保留数据很短一段时间,那么您支付的费用会少得多。这些费用经济学更符合矿工的成本,因此更容易扩展。
与 Facebook Inc 非常相似,TON 的合约开发商有很大的自由度来选择如何为其运营提供资金。开发者可以自掏腰包使用 TON 代币为合约提供资金并补贴其用户;或者它可以向用户收取不同操作的 Gas 费用,并将这些 Gas 保留在余额中以用于未来的租金支付。
2. 智能合约之间的调用是异步的,不是原子的
以太坊上强大的 DeFi 生态系统的重要推动因素之一是合约的无缝可组合性。在单笔交易中,您可以获取一些 WBTC,通过Compound的合约将其作为抵押品,并用它借入 USDC,通过Uniswap的合约将此 USDC 交易以获得更多 WBTC - 从而利用您的 WBTC 头寸。整个过程甚至是原子的——如果这些步骤中的任何一个失败,即使是最后一个,整个事务也会回滚,就像从未发生过一样。
当您的智能合约调用不同智能合约的方法时,该调用将立即在同一交易中处理。在这方面,以太坊与在单个服务器上运行整个后端非常相似。后端的每个部分都能够同步访问其他所有部分 - 这是一种很容易推理的方法。它有一个警告,那就是只要它适合一处,它就只能生长。
从单个服务器迁移到微服务集群
如果您将以太坊想象为单个服务器上的整体,那么 TON 更类似于微服务集群。认为每个智能合约可能都在不同的机器上运行。如果两个智能合约想要互相调用,就像两个微服务通信一样,它们可以通过网络发送消息。该消息需要一些时间传输,因此通信突然异步!这意味着当您的智能合约调用不同智能合约的方法时,该调用将在交易终止后在某个不同的未来块上进行处理。
这更难以推理。如果从发送消息到收到消息期间条件发生变化,会发生什么情况?例如,调用合约余额只有一个值,但当第二个合约处理调用时,余额已发生变化。保持一致性更加困难,并且错误可能会蔓延。原子性怎么样?如果您连续进行三个调用并且只有最后一个调用失败,会发生什么情况?如果您需要回滚所有更改,则必须手动执行此操作。
3.你的智能合约不能在其他合约上运行getter方法
这实际上与列表中的前一项不同。在以太坊上,合约之间的调用是同步的,从不同的智能合约读取数据非常简单。假设我的合约有 USDC 余额。由于USDC本身也是一个合约,为了知道自己的余额,我的合约必须调用getBalance
USDC合约的方法。
还记得在单个服务器上运行的单体应用吗?这种方法的一大好处是每个服务都可以直接读取其他服务的状态内存。
从单个服务器迁移到微服务集群
当我们在不同的机器上运行单独的微服务时,跨服务读取状态内存突然变得不可能。TON 上的智能合约只能通过发送异步消息进行通信。如果您需要查询另一个合约的数据并且需要立即得到答案,那么您就不走运了。
实际上变得更奇怪了。如果您在 TON 智能合约上看到 getter 方法,例如getBalance
- 这些方法无法从其他智能合约访问。Getter 方法只能由链外客户端调用,类似于以太坊钱包如何使用 Infura 等完整节点来查询任何智能合约状态。
4. 智能合约代码不是一成不变的,可以很容易地修改
以太坊上 dapp 的最初灵感是由律师起草的法律文件- 因此被称为“智能合约”。开发人员将法律合同的条款编写为代码,而正如您所知,代码就是法律。当现实世界中的两方签订合同时,合同是不可变的。如果任何一方想要更改合同条款,他们将起草一份新合同。
按照这种方法,以太坊上的智能合约代码被设计为不可变且永远不会被修改。多年来,开发者社区已经学会了克服这一限制,并生成了一些繁琐的模式,这些模式依赖于诸如代理合约之类的技巧,该代理合约指向不同的合约以实现升级。
从律师转向软件工程师
与律师不同,软件工程师被教导每一段代码都存在错误。即使错误永远不会发生,需求仍然会随着时间的推移而变化,并且代码经常必须升级和修改。
在 TON 下,合约不可变的假象被完全抛弃了。智能合约可以自由修改自己的代码,就像写入任何其他状态变量一样。如果合约写入代码变量,则它是可变的,如果不写入,则它是不可变的。这在实践中并不是一个大的改变,它只是使繁琐的代理模式变得多余。
5. 你的合约状态不应该有无限的数据结构
这是一个棘手的问题,需要一段时间才能理解,但它将解释为什么 TON 上的一些智能合约是这样构建的。
无界数据结构是智能合约中可以无限增长的状态变量。考虑实现 USDC 代币的 ERC20 合约。该合约需要维护每个用户地址的余额图。不同 USDC 持有者的数量可以无限增长,因为 USDC 可以大量铸造并分解成小块。换句话说,映射中键的数量可以任意多。
如果攻击者试图通过添加越来越多的条目来向合约发送垃圾邮件,会发生什么?他们是否能够引起一些 DoS 攻击并阻止其他诚实用户使用此合约?以太坊为智能合约开发人员非常优雅地解决了这个问题。以太坊费用模型规定写入新状态数据的用户为此数据支付费用。这意味着我们的攻击者将不得不为其垃圾邮件付出高昂的代价。此外,写入以太坊上的地图的天然气成本是恒定的,并不取决于该地图包含多少数据,这意味着其他用户不会遭受垃圾邮件的困扰。最重要的是,在以太坊上发送垃圾地图并不经济,而且保护是由系统提供的。
从无界地图转向无界合约
不幸的是,对于 TON 智能合约开发人员来说,该系统无法防止在合约状态下发送垃圾邮件无界数据结构。TON Gas 费用模型规定写入的成本不是恒定的,成本通常与数据结构中存在的数据量成正比。这种行为源于 TON 对“Bag of Cells”架构的依赖——合约状态被划分为 1023 位块,称为“单元”,开发人员需要维护这些块。映射被实现为单元树,写入树中的叶子需要沿其整个高度写入新的哈希值。如果攻击者在地图中发送垃圾邮件密钥,则某些用户余额将在树中被压得很低,以至于更新它们将超过气体限制。
因此,TON 的最佳实践是避免状态中的无界数据结构。这将保护合约免受狡猾的 DoS 漏洞的影响。这个主题可能值得有自己的独立博客文章,但简而言之,解决方案是依赖合约分片。如果我们的 USDC 合约中可能有无限数量的用户余额,我们应该将单个合约分解为多个子合约 - 每个子合约持有单个用户的余额。
这应该可以解释为什么TON 上的NFT集合合约将每个项目都放在自己单独的合约中(项目数量可以不受限制);以及为什么TON 上的可替代代币合约将每个用户的余额放在自己单独的合约中。
我们通常会提供一些关于 TON 为何如此设计的推理。以太坊汽油费模型(地图写入是固定的且与地图大小无关)过于简化。事实上,随着地图尺寸的增大,矿工需要付出更多的努力来更改其内容。只要地图很小,这种额外的工作就可以忽略不计,但是当地图可以增长到数十亿个条目时,情况就不再是这样了。
6. 钱包是合约,一个公钥可以部署多个钱包
在以太坊上,用户的钱包与其地址同义,并且地址直接从公钥(及其相应的私钥)派生。这是一种 1:1 的关系,每个公钥有一个地址,每个地址有一个公钥。只要用户知道自己的私钥,他们就永远不会丢失钱包。
此外,以太坊上的用户无需执行任何特殊操作即可拥有钱包。以太坊地址就是钱包。该地址可以持有原生货币ETH,该地址可以持有ERC20代币和NFT,该地址可以直接向其他智能合约发送和签署交易。
从地址转移到合同
在 TON 上,钱包不是隐含的,它们是独立的智能合约,必须像任何其他智能合约一样部署。当新用户想要开始使用 TON 区块链时,他们的第一步是在链上部署钱包。该钱包的地址源自钱包合约代码和各种初始化参数(例如用户的公钥)。
这意味着用户可以部署多个钱包,每个钱包都有自己的地址。钱包的代码(基金会不时发布不同的官方代码版本)或其初始化参数(这些参数之一通常是序列号)可能有所不同。这也意味着知道其私钥的用户仍然必须有意识地记住他们的钱包地址(或其构造中使用的初始化参数)。
向 TON 上的某个 dapp 发送交易涉及使用用户的私钥签署消息。与以太坊不同的是,这笔交易不是发送到 dapp 智能合约,而是发送到用户的钱包合约,钱包合约又将消息转发到 dapp 智能合约。
这种设计方法为 TON 的灵活性开辟了新的维度。随着时间的推移,社区可以发明新的钱包合约,例如,考虑一个没有随机数(交易序列号)的钱包,它允许从不同的客户端并行发送多个交易,而无需事先同步。此外,像多重签名钱包这样的特殊钱包(在以太坊上也需要部署特殊的智能合约)的行为与普通钱包一样。
本站所提供的所有资讯均仅供读者参考。这些资讯不代表任何投资建议、提供、邀请或推荐。读者在使用这些资讯时,应当考虑自己的个人需求、投资目标和财务状况。所有投资都伴随着一定的风险,在做出任何投资决策之前请多加留意。