一文了解 TON 的技术特点与智能合约开发范式

2024-10-16 09:17:00
简而言之,TON 的核心设计理念是以一种“自下而上”的方式重构传统的区块链协议,并以舍弃互操作性为代价,实现对高并发和高可扩展性的极致追求。

TON 的核心设计思想——高并发与高可扩展性

可以这么说,TON 中所有复杂的技术选型的目的都来自于对高并发与高可扩展性的追求,当然从其诞生的背景我们也不难理解这一点。TON,即 The Open Network,是一个去中心化的计算网络,包含一个 L1 区块链和多个组件。TON 最初由 Telegram 的创始人 Nikolai Durov 及其团队共同开发,而发展到现在则由全球独立贡献者的社区支持并维护。其诞生要追溯到 2017 年,Telegram 团队开始为自己探索区块链解决方案。由于当时没有现有的 L1 区块链能够支持 Telegram 的九位数用户基础,他们决定设计自己的区块链,当时称为 Telegram Open Network。时间来到了 2018 年,为了获得实现 TON 所需的资源,Telegram 在 2018 年第一季度发起了 Gram 代币(后来改名为 Toncoin)的销售。2020 年由于监管问题,Telegram 团队退出了 TON 项目。随后,一小部分开源开发者和 Telegram 比赛获胜者接手了 TON 的代码库,将项目名称更名为 The Open Network,并继续积极地开发区块链至今,且遵循原始 TON 白皮书中概述的原则。

那么既然是以作为 Telegram 的去中心化执行环境作为设计目标,自然要面对两个问题,高并发请求与海量数据,我们知道随着技术发展到现在,号称 TPS 最高的 Solana 实测最高 TPS 也只有 65000 ,这显然不足以支撑百万级 TPS 要求的 Telegram 生态。与此同时随着 Telegram 的大规模应用,其产生的数据量早已突破天际,而区块链作为一个极度冗余的分布式系统,若要求网络中每个节点都保存一份完整的数据,这也是不现实的。

因此为了解决上述两个问题,TON 对主流的区块链协议做出了两个方面的优化:

  • 通过采用“无限分片范式”(Infinite Sharding Paradigm)设计系统,解决数据冗余问题,使其可以承载大数据,同时缓解性能瓶颈问题;
  • 通过引入基于 Actor 模型的完全并行执行环境,极大的提升网络 TPS;

做区块链的链——通过无限分片能力让每个账户都有一条专属的账户链

当下我们知道,分片(sharding)已经成为了大部分区块链协议提升性能降低成本的主流方案,而 TON 则将这点做到了极致,并提出了无限分片范式,所谓无限分片范式,指的是允许区块链根据网络负载动态地增加或减少分片数量。这种范式使得 TON 能够在保持高性能的同时,处理大规模的交易和智能合约操作,理论上 TON 可以为每个账户都建立一条专属的账户链,并通过一定的规则保证这些链之间的一致性,

抽象的来理解,在 TON 中一共存在四层链结构:

  • 账户链(AccountChain):该层链表示与某个账户相关的一系列交易所组成的链,之所以交易可以组成链式结构,是因为对于一个状态机来说,只要执行规则一致,状态机在接收到相同顺序的指令后得到的结果是一致的,因此所有区块链分布式系统中都需要对交易进行链式排序,TON 也不例外。账户链是 TON 网络中最基本的组成单元,通常情况下账户链是一个虚拟的概念,不太可能真正存在一个独立的账户链。
  • 分片链(ShardChain):在大部分的语境下,分片链才是 TON 中实际的组成单元,所谓分片链,即为一组账户链的集合。
  • 工作链(WorkChain):也可以叫做一组有自定义规则的分片链,例如创建一个基于 EVM 的工作链,在其上运行 Solidity 智能合约。理论上,社区中的每个人都可以创建自己的工作链。事实上,构建它是一个相当复杂的任务,在此之前还要支付创建它的(昂贵)费用,并获得验证者的 2/3 的票数来批准创建你的工作链。
  • 主链(MasterChain):最后在 TON 中有一条特殊的链被称为主链,该链负责为所有分片链带来最终性。一旦分片链的区块的哈希值被合并到主链的区块中,该分片链区块及其所有父区块被认为具有最终性,这意味着它们可以被认为是固定且不可变的内容,而被所有分片链的后续区块引用。

通过采用这样的范式,使 TON 网络具备以下三个特点:

  • 动态分片: TON 可以自动拆分和合并分片链以适应负载的变化。这意味着新块总是快速生成,而交易不会产生很长的等待时间。
  • 高度可扩展: 通过无限分片范式,TON 能够支持几乎无限数量的分片,理论上可以达到 2 的 60 次方个工作链。
  • 自适应性: 当网络中的某个部分负载增加时,该部分可以被细分成更多的分片来处理增加的交易量。相反,当负载减少时,分片可以合并以提高效率。

那么这样一个多链系统,首先需要面临的就是跨链通信问题,尤其是由于具有无限分片的能力,当网络中的分片数量达到一定量级后,链与链之间的信息路由将成为一件困难的事情。试想一下网络中共有 4 个节点,每个节点负责维护 1 条独立的工作链,其中链接关系表示该节点除了负责自身的工作链中交易排序工作之外,还需要监听并处理目标链中状态变化,在 TON 中具体通过监听输出队列的消息实现,

假设工作链 1 中的账户 A 希望向工作链 3 中的账户 C 发送一个消息。则需要设计到消息路由问题,在这个例子中有两条路由路径,工作链 1 -> 工作链 2-> 工作链 3 ,工作链 1 -> 工作链 4 -> 工作链 3 。

当面临更复杂的情况时,就需要一个高效且低成本的路由算法快速完成消息通信,TON 选择了所谓“超立方体路由算法”来实现跨链消息通信路由发现。所谓超立方体结构指的是一种特殊的网络拓扑结构,一个 n 维超立方体是由 2 ^n 个顶点组成的,每个顶点都可以通过一个 n 位的二进制数来唯一标识。在这个结构中,任意两个顶点如果在二进制表示中只有一位不同,那么它们就是相邻的。例如,在一个 3 维超立方体中,顶点 000 和顶点 001 是相邻的,因为它们只在最后一位上不同。而上述例子即是一个 2 维超立方体。

在超立方体路由协议中,消息将从源工作链到目标工作链的路由过程是通过比较源工作链和目标工作链地址的二进制表示来进行的。路由算法会找到这两个地址之间的最小距离(即二进制表示中不同位的数量),并通过相邻工作链逐步转发信息,直到达到目标工作链。这种方法能够确保数据包沿着最短路径传输,从而提高了网络的通信效率。

当然为了简化这个过程,TON 也提出了一个乐观技术方案,当用户可以提供对某个路由路径的有效证明,这通常是某个 merkle trie root,节点即可直接承认该用户提交的消息的可信性,这也被称为即时超立方体路由。

因此我们可以看到 TON 中的地址和其他区块链协议有着明显的区别,其他主流区块链协议大都采用椭圆加密算法生成的公私钥中公钥对应的哈希作为地址,因为地址只是做唯一性区分,而不需要承载路由寻址的功能,而 TON 中的地址有两部分组成,(workchain_id, account_id),其中 workchain_id 即按照超立方体路由算法地址进行编码,在这里就不详细展开了。

还有一个容易产生疑问的点,你可能已经发觉到主链和每个工作链均有链接关系,那么所有跨链信息均通过主链做中继不就可以了么,就像是 cosmos 那样。在 TON 的设计理念中,主链仅用于处理最关键的任务,即维护众多工作链的最终性,将消息通过主链做路由也不是不行,只是由此产生的手续费用将十分昂贵。

最后简单提一下其共识算法,TON 采用了 BFT+PoS 的方式,即任意 staker 均有机会参与区块打包,TON 的选举治理合约会每隔一段时间,从所有 Stakers 中随机选择一个打包的验证者集群,被选中称为验证者的节点将通过 BFT 算法打包出块,若打包错误信息或作恶,其 stake 的 token 将会被罚没,反之将得到出块奖励。这基本上已经是一个比较常见的选择了,因此不在这里展开介绍。

基于 Actor 模型的智能合约和完全并行执行环境

TON 中另一个与主流区块链协议不同的点是其智能合约执行环境。为了突破主流区块链协议 TPS 的限制,TON 采用了自下而上的设计思路,采用 Actor 模型重构了智能合约及其执行方式,使其具备了完全并行执行的能力。

我们知道主流的区块链协议大都采用的是单线程串行的执行环境,以 Ethereum 为例,其执行环境 EVM 是一个以交易作为输入的状态机,当出块节点通过打包区块完成对交易的排序后,将以该顺序通过 EVM 执行交易,整个过程是完全串行并单线程的,即某个时刻只能有一笔被执行,这样做的好处是只要确认了交易顺序,执行的结果在广泛的分布式集群中就具有一致性,与此同时由于同时只有一笔交易被串行执行,这就意味着在执行过程中,不可能存在其他交易对某待访问状态数据进行修改,这样就实现了智能合约之间的互操作性。例如我们通过 Uniswap 使用 USDT 购买 ETH,当该交易被执行时,该交易对中 LP 的分布情况即为一个确定值,这样就可以通过某些数学模型得出对应的结果,但假设情况不是这样的,在执行某 bonding curve 的计算时,有其他 LP 添加了新的流动性,那么计算结果将会是一个过时的结果,这显然是不可接受的。

但是这种架构也有明显的局限性,那就是 TPS 的瓶颈,而这个瓶颈在当前多核处理器下显得很老旧,就像你用一个最新的 PC 去玩一些老的电脑游戏,比如红警,当作战单位多到一定数量后,依然会发现卡的不行,这就是软件架构的问题。

你可能会听到一些协议已经在关注这个问题,并提出了自己的并行方案,以当前号称 TPS 最高的 Solana 为例,也具备并行执行的能力。只不过其设计思路与 TON 不同,在 Solana 中,其核心思想是将所有交易按照执行依赖关系分为几组,不同组之间不共享任何状态数据。即不存在相同的依赖,这样不同组内的交易就可以并行执行而不用担心出现冲突的情况,而对于同组内的交易,则还是沿用传统的串行方式执行。

而在 TON 中,其完全舍弃了串行执行的架构,转而采用了一个专为并行而生的开发范式,Actor 模型来重构执行环境。所谓 Actor 模型是由 Carl Hewitt 在 1973 年首次提出,目的是通过消息传递来解决传统并发程序中共享状态的复杂性问题。每个 Actor 都有自己的私有状态和行为,且与其他 Actor 之间不共享任何状态信息。Actor 模型是一种并发计算的计算模型,它通过消息传递来实现并行计算。在这个模型中,"Actor"是基本的工作单元,它能够处理接收的消息、创建新的 Actor、发送更多消息、决定如何响应接下来的消息。Actor 模型需要具备以下几个特性:

  • 封装和独立性:每个 Actor 在处理消息时都是完全独立的,可以并行处理消息而不会互相干扰。
  • 消息传递:Actor 之间仅通过发送和接收消息进行交互,消息传递是异步的。
  • 动态结构:Actor 可以在运行时创建更多的 Actor,这种动态性使得 Actor 模型能够根据需要扩展系统。

TON 采用了这个架构,来设计智能合约模型,这就意味着在 TON 中,每个智能合约都是一个 Actor 模型,其具备完全独立的存储空间。因为不依赖任何外部数据。除此之外,对同一个智能合约的调用还是按照接收队列中消息的排序进行执行,因此 TON 中的交易将可以被高效的并行执行,而不需要担心冲突问题。

然而这样的设计方案也带来了一些全新的影响,对于 DApp 开发者来说,其习惯的开发范式将被打破,具体如下:

  1. 智能合约之间的异步调用

在 TON 的智能合约内部是无法原子性的调用外部合约或访问外部合约数据的,我们知道在 Solidity 中,合约 A 的 function 1 中调用合约 B 的 function 2 ,或者通过合约 C 的只读 functio n3 访问某状态数据,整个过程是原子性的,在一笔交易中被执行,这是一件非常容易的事情,然而在 TON 中,这将不可能实现,任何与外部智能合约的交互都将通过打包新的交易异步执行,这种由智能合约发起的交易也被称为内部消息。且执行过程中无法阻塞以获得执行结果。

例如我们开发一个 DEX,如果采用 EVM 中常见的范式,通常会有一个统一的 router 合约用于管理交易路由,而每个 Pool 都单独管理某个交易对相关的 LP 数据,那么假设当前有两个池子 USDT-DAI 和 DAI-ETH。当用户希望通过 USDT 直接购买 ETH,就可以通过 router 合约在一笔交易中顺序请求这两个池子,完成原子性交易。然而在 TON 中就没有这么容易实现了,需要思考新的开发范式,若仍然复用该该范式的话,那信息流可能是这样的,这个请求将伴随一个由用户发起的 external message 和三个 internal messages 完成(注意这是用于说明差异性的,真实的开发中甚至连 ERC 20 的范式也要重新设计)。

  1. 需要仔细考虑跨合约调用时出现执行错误情况的处理流程,为每个合约间调用设计相应的弹回(bounce)函数。

我们知道在主流的 EVM 中,当交易执行时遇到问题时,整个交易将会被回滚,即被重置到执行最初时的状态。这在串行单线程模型中是容易理解的。然而在 TON 中,由于合约间调用采用了异步的方式执行,即使后续某环节出错,由于前面已经被成功执行的交易已经被执行并确认,这就有可能造成问题。因此 TON 中设置了一种特殊的消息类型,叫做弹回消息,即当某内部消息触发的后续执行过程出现错误时,被触发合约可以通过触发合约预留的弹回函数将触发合约中的某些状态重置。

  1. 在某些复杂情况下,先被接收的交易不一定先被执行完毕,因此不可以预设这种时序关系。

在这样一个异步和并行智能合约调用的系统中,定义处理操作顺序可能很难。这就是为什么 TON 中的每个消息都有它的逻辑时间 Lamport time(后面简称 lt)。它用于理解哪个事件引发了另一个以及验证者首先需要处理什么。对于一个简单的模型,先被接收的交易一定先被执行完成。


在这个模型中,A 和 B 分别表示两个智能合约,则有如果 msg 1 _lt < msg 2 _lt,则 tx 1 _lt < tx 2 _lt 的时序关系。

然而在较为复杂的情况下,这个规则就会被打破。在官方文档中有这样的例子,假设我们有三个合约 A、B 和 C。在一笔交易中,A 发送两个内部消息 msg 1 和 msg 2 :一个给 B,另一个给 C。尽管它们是按确切顺序创建的(先 msg 1 ,然后是 msg 2),但我们无法确定 msg 1 将在 msg 2 之前被处理。这是因为从 A 到 B 和从 A 到 C 的路由可能在长度和验证者集中有所不同。如果这些合约位于不同的分片链中,其中一条消息可能需要几个区块才能到达目标合约。即我们有两种可能的交易路径,如图所示。

  1. 在 TON 中,其智能合约的持久化存储采用了一个以 Cell 为单元的有向无环图作为数据结构,

数据将按照编码规则紧凑的压缩为一个 Cell,同时按照有向无环图的方式向下延伸,这与 EVM 中状态数据基于 hashmap 的结构组织不同,由于数据请求算法的不同,TON 中为不同深度的数据处理设置了不同的 Gas 价格,越深的 Cell 数据处理所需要的 Gas 越高,因此在 TON 中存在一种 DOS 攻击的范式,即某些恶意用户通过发送大量垃圾消息占用某个智能合约中所有的浅层 Cell,这就意味着诚实用户的存储成本将越来越高。而在 EVM 中,由于 hashmap 的查询复杂度为 o( 1),因此有着相同的 Gas,不会有类似问题。所以 TON Dapp 开发者应该尽量避免智能合约中出现无界数据类型。当出现无界数据类型时,应通过分片的方式将其打散。

  1. 还有一些特征则不那么特殊了,例如智能合约需要为存储支付租金,在 TON 中智能合约天然是可升级的,以及原生的抽象账户功能,即在 TON 中所有钱包地址均为智能合约,只是未被初始化等,这些需要开发者小心留意。

转载:https://www.chaincatcher.com/article/2128150

其它:https://github.com/UnsignedInt8/TON/tree/master/TON

当前页面是本站的「Baidu MIP」版。发表评论请点击:完整版 »