Sui Move如何重构闪电贷安全机制
闪电贷款作为DeFi基础原语,允许在同一交易内无抵押借款,但需在同一交易内偿还。这一机制历来是把双刃剑:诚实的借款人可进行套利和债务再融资,但攻击者也能利用它放大攻击影响并增加盗取资金量。我们发现Sui的Move语言通过用"烫手山芋"模型取代Solidity对回调和运行时检查的依赖,显著提升了闪电贷安全性。这一转变使闪电贷安全成为语言保证而非开发者责任。
本文分析了DeepBookV3(Sui原生订单簿DEX)中的闪电贷实现。我们将Sui的实现与常见Solidity模式进行比较,展示Move将安全设为默认而非开发者责任的设计理念如何提供更强安全保证,同时简化开发者体验。
Solidity方法:回调和运行时检查
Solidity闪电贷协议传统上依赖回调模式,这提供了最大灵活性但将全部安全负担置于开发者身上。该过程要求借贷协议在能够验证还款前暂时信任借款人。
典型流程包含以下步骤:
- 借款合约调用借贷协议的flashLoan函数
- 协议将代币转移至借款合约
- 协议调用借款合约的onFlashLoan函数
- 借款合约使用借入代币执行其逻辑
- 借款合约偿还贷款
- 原始借贷协议检查其余额以确认还款,如果资金未归还则回滚整个交易
这种基于回调的模型将安全责任置于借贷协议开发者身上,他们必须在函数结束时实施余额检查以确保贷款安全(图2)。由于协议对借款合约进行外部调用,开发者必须仔细管理状态以防止重入风险。Fei Protocol开发者在2022年以8000万美元的代价吸取了这一教训,当时黑客利用系统缺陷(特别是未遵循检查-效果-交互模式)借入资金,然后在借款记录前提取其抵押品。如果接收合约的访问控制未正确实施,甚至借款人自身也可能面临风险。
|
|
此外,最初缺乏标准接口导致了碎片化。尽管EIP-3156后来提出了单资产闪电贷标准,其中贷方从借款人处拉回资金而非期望借款人发送资金,但尚未被所有主要DeFi协议采用,并且带来了自身的一系列安全挑战。
Sui Move方法:可组合安全性
Sui的闪电贷实现根本不同。它利用平台的三个核心特性——独特对象模型、可编程交易块(PTBs)和字节码验证器——在语言层面提供闪电贷安全。
Sui的对象模型和Move能力
要理解Move的安全保证,必须首先理解Sui的对象模型。在以太坊基于账户的模型中,代币余额只是分类账(ERC20合约)中的一个数字,用于跟踪谁拥有什么。用户钱包不直接持有代币,而是持有允许其向中央合约查询余额的密钥。
相比之下,Sui以对象为中心的模型将每个资产(代币、NFT、管理权或流动性池头寸)视为独特、独立的对象。在Sui中,一切都是对象,携带属性、所有权以及被转移或修改的能力。用户账户直接拥有这些对象。没有中央合约分类账;所有权是账户与对象本身之间的直接关系。
这种以对象为中心的方法(特定于Sui,而非Move语言本身)实现了并行交易处理,并允许对象直接作为函数参数传递。这就是Move能力系统发挥作用的地方。能力是编译时属性,定义对象如何使用。
有四个关键能力:
key:允许对象用作存储中的键store:允许对象存储在具有key能力的对象中copy:允许对象被复制drop:允许对象在交易结束时被丢弃或忽略
在我们的闪电贷案例中,关键优势来自省略能力。没有能力的对象不能被存储、复制或丢弃。它变成了"烫手山芋":必须在同一交易内被另一个函数消费的临时证明或收据。在Move中,“消费"对象意味着将其传递给取得所有权并销毁它的函数,使其退出流通。如果不这样做,交易无效且不会执行。
虽然Move的能力系统为闪电贷提供了安全机制,但Sui的PTBs实现了使它们实用的可组合性。
PTBs如何工作
在以太坊中,在EIP-7702(账户抽象)成为规范之前,与DeFi协议的交互需要多个独立交易(例如,一个用于代币授权,另一个用于交换)。这造成了摩擦和潜在故障点。
Sui的PTBs通过允许多个操作链接到单个原子交易中来解决这个问题。虽然这听起来像Solidity的multicall()模式,但PTBs是原生集成的且更强大。关键区别在于PTBs允许一个操作的输出用作下一个操作的输入,所有这些都在同一个块内。
以下是通过Sui CLI进行的闪电贷套利示例,该示例在后续交易命令中使用先前交易命令的结果。(注意实际函数签名和参数会更复杂。)
|
|
这种原子执行模型是Sui闪电贷的基础,但安全机制在于Move语言如何处理资产。
Move字节码验证器
Move字节码验证器是在发布时在模块上运行的静态验证步骤。它强制执行类型、资源、引用和能力约束。流水线分两个阶段工作:编译器对源代码进行类型检查并将其转换为字节码,在模块在链上发布之前,字节码验证器在字节码级别再次执行类型和资源检查,并拒绝任何类型错误或不安全的字节码。这防止手工制作的字节码绕过"烫手山芋"限制,并确保此类值必须在同一交易内消费才有效。
实践中的烫手山芋模式:DeepBookV3
DeepBookV3的闪电贷实现使用这种"烫手山芋"模式创建了一个安全的系统,无需回调或运行时余额检查。
流程很简单:
- 用户调用borrow_flashloan_base
- 函数返回两个对象(Coin
, FlashLoan):借入资金的Coin对象和FlashLoan收据对象 - 用户使用Coin执行操作
- 用户调用return_flashloan_base,传回借入资金和FlashLoan收据
- 最终函数消费收据,交易成功完成
让我们看看返回借入资产和FlashLoan结构的borrow_flashloan_base函数的代码:
|
|
技巧在于FlashLoan结构的定义。注意到缺少什么吗?……没有能力!
|
|
由于此结构是"烫手山芋”,交易有效的唯一方式是通过将其传递给相应的return_flashloan_base函数来消费它,该函数会销毁它。
|
|
烫手山芋模式如何确保还款
这种模式与PTBs的原子性结合,创造了内置的安全保证。Move字节码验证器防止无效字节码被执行,而不是依赖运行时检查。
例如,如果交易调用borrow_flashloan_base但随后未消费返回的FlashLoan对象,则交易无效并失败。因为该结构缺少drop能力,它不能被丢弃。由于它也不能被存储或转移,交易逻辑不完整,整个操作在处理前就会失败。
类似地,如果开发者构建了一个PTB借入资金但省略了最终的return_flashloan_base调用,交易同样无效。MoveVM识别未处理的"烫手山芋"并中止整个交易,回滚所有前置操作。
未能还款不是需要开发者防止的风险,而是系统通过设计防止的逻辑不可能。有效、可执行的交易必须包含还款逻辑。
通过设计实现安全
使用Sui Move,语言本身成为主要安全卫士。在Solidity要求开发者实施运行时检查和仔细状态管理以防止漏洞利用的地方,Move的类型系统首先使得为此用例编写不安全代码变得困难。可以与Rust的安全模型进行类比:正如Rust编译器保证内存安全,由字节码验证器强制执行的Sui Move类型系统保证资产安全。这种模型将安全执行从开发者实现的运行时检查转移到语言自身的字节码验证规则。