代码审计师眼中的 Move 语言

✦
作者: OtterSec
原文链接:
https://osec.io/blog/tutorials/2022-09-06-move-introduction/
译者: GG@ComingChat
✦
✦
本文讨论 Move 语言的类型系统和形式化验证
在这篇文章中,我将试图确切地讨论 move 是如何使自己更安全的编程实践、潜在的缺点以及为希望构建结构上更安全的程序的协议开发人员提供的实用设计技巧。
类型
Move 的主要卖点之一是使用类型化的资源。但 Aptos 和 Sui 在具体化这种模式的方式上略有不同,以 coin.move 为例。
/// Main structure representing a coin/token in an account's custody.struct Coin<phantom CoinType> has store {/// Amount of coin this address has.value: u64,}
Aptos
/// A coin of type `T` worth `value`. Transferable and storablestruct Coin<phantom T> has key, store {id: UID,balance: Balance<T>}
Sui
/// Liquidity pool with reserves.struct LiquidityPool<phantom X, phantom Y, phantom LP> has key {coin_x_reserve: Coin<X>,coin_y_reserve: Coin<Y>,// ...}
这具有在编译时对齐类型信息的优点。不小心将错误类型的 Coin 传递给函数是很困难的。
public fun mint<X, Y, LP>( pool_addr: address, coin_x: Coin<X>, coin_y: Coin<Y> ): Coin<LP> acquires LiquidityPool, EventsStore { // ... let (x_reserve_size, y_reserve_size) = get_reserves_size<X, Y, LP>(pool_addr);这是必需的。必须指定类型参数,这样就不可能忘记这样的约束 很简洁。约束是通过类型参数对齐而不是冗长的等价检查来完成的
fun initialize_internal<CoinType>(// ...): (BurnCapability<CoinType>, FreezeCapability<CoinType>, MintCapability<CoinType>) {// ...assert!(!exists<CoinInfo<CoinType>>(account_addr),error::already_exists(ECOIN_INFO_ALREADY_PUBLISHED),);
/// The struct to hold all the underlying `Coin`s. /// Stored as a resources.struct ReserveCoinContainer<phantom Coin0> has key {/// Stores the available `Coin`.underlying_coin: Coin<Coin0>,/// Stores the LP `Coin` that act as collateral.collateralised_lp_coin: Coin<LP<Coin0>>,/// Mint capability for LP Coin.mint_capability: MintCapability<LP<Coin0>>,/// Burn capability for LP Coin.burn_capability: BurnCapability<LP<Coin0>>,// ...}
public(friend) fun create<Coin0>( lp_store: &signer, // ...) acquires Reserves { lp::assert_is_lp_store(signer::address_of(lp_store)); // ... move_to(lp_store, ReserveCoinContainer<Coin0> { // ... });struct Market<phantom T> { reserves: Coin<T>,// ...}struct Position<phantom T> {amount: u64,// ...}
/// Get the price of the token per lamport.public fun get_price(type_info: TypeInfo): Decimal acquires Oracle {let oracle = borrow_global_mut<Oracle>();let price = table::borrow_mut_with_default<TypeInfo, Decimal>(&mut oracle.prices,type_info,decimal::one());*price}
为您的资源提供独特的初始化程序 直接将资源与实例关联
形式验证
形式验证是另一个令人兴奋的功能。
作为我们协议工作的一部分,我们积极使用形式验证来证明安全性的各个方面。
然而,这不是灵丹妙药。关键是弄清楚要证明什么。
一个明显的想法可能是跨特定函数的属性。例如,我们可能希望确保交换不会降低池子的价值——类似于我们报告的 Solana AMM 舍入问题。
然而,这也可以通过一个简单的运行时断言来检查。例如,我们建议 Pontem 断言流动性池代币价值正在严格增加。
let cmp = u256::compare(&lp_value_after_swap_and_fee, &lp_value_before_swap_u256); assert!(cmp == 2, ERR_INCORRECT_SWAP);
当我们证明函数之间的关系时,Move 验证器真的很出色。
无法通过断言轻松证明的更复杂关系的一个示例是 Move 存储库中的 no_free_money_theorem 函数。
// #[test] // TODO: cannot specify the test-only functions
fun no_free_money_theorem(coin1_in: u64, coin2_in: u64): (u64, u64) acquires Pool {let share = add_liquidity(coin1_in, coin2_in);remove_liquidity(share)}spec no_free_money_theorem {pragma verify=false;ensures result_1 <= coin1_in;ensures result_2 <= coin2_in;}
没有明确的方法可以用断言来表达这一点,因为这会在时间上分离的两个函数之间进行观察。
不变量也非常有用。例如,对费用参数(费用永远不会超过 100%)或池子的供应量强制执行不变量可以更容易地对协议进行推理。
例如,Ian 使用不变量来清楚地定义他的 AMM 状态的核心属性。
spec PoolState { invariant supply >= MINIMUM_LIQUIDITY;}Move 验证器的另一个有用模式是 aborts_if。更具体地说,用 aborts_if 为 false 断言函数永不中止会很有帮助。
尽管循环不变量有点笨拙,但 Ian 也能够证明一个相对不平凡的函数不会中止。
fun multiply_vec_by_n_coins(input: vector<u64>): vector<u128> {let amounts_times_coins = vector::empty<u128>();let i = 0;let n_coins = vector::length(&input);while ({spec {invariant len(amounts_times_coins) == i;invariant i <= n_coins;invariant forall j in 0..i: amounts_times_coins[j] == input[j] * n_coins;};< n_coins){vector::push_back(amounts_times_coins,::borrow(&input, (i as u64)) as u128) * (n_coins as u128));i = i + 1;};spec {assert i == n_coins;assert len(input) == n_coins;};amounts_times_coins}spec multiply_vec_by_n_coins {pragma opaque;aborts_if false;ensures len(result) == len(input);ensures forall j in 0..len(input): result[j] == input[j] * len(input);}
总结

【免责声明】市场有风险,投资需谨慎。本文不构成投资建议,用户应考虑本文中的任何意见、观点或结论是否符合其特定状况。据此投资,责任自负。
来源链接

