Move的形式化验证

前言

在学习Aptos官方文档中的The Move Book时,发现后面还有一个The Move Prover Book。意外发现除了通过写Move的单测之外,还有另一种方法来验证Move合约的正确性。此篇就是学习Move形式化验证的学习笔记。

Move prover的环境准备

话不多说,先来跑一下demo,直观感受一下Move Prover是做什么的。

js 复制代码
//环境准备,可以直接在aptos-core的路径下执行
./scripts/dev_setup.sh -yp
source ~/.profile

//跑一下Example
aptos move prove --package-dir aptos-move/move-examples/hello_prover/

得到运行结果:

js 复制代码
[INFO] checking specifications
[INFO] rewriting specifications
[INFO] preparing module 0x42::prove
[INFO] transforming bytecode
[INFO] generating verification conditions
[INFO] 2 verification conditions
[INFO] running solver
[INFO] 117.237s build, 0.025s trafo, 0.042s gen, 0.677s verify, total 117.981s
{
  "Result": "Success"
}

那么这是执行了些什么呢?查看运行的hello_prover目录下的prove.move代码如下:

js 复制代码
module 0x42::prove {
    fun plus1(x: u64): u64 {
        x+1
    }
    spec plus1 {
        ensures result == x+1;
    }

    fun abortsIf0(x: u64) {
        if (x == 0) {
            abort(0)
        };
    }
    spec abortsIf0 {
        aborts_if x == 0;
    }
}

根据之前学习到的Move基础知识,这段代码中有两个函数,分别是plus1和abortsIf0,其函数实现功能也非常简单,一个实现对入参加1,另一个判断传入参数是否为0,如果是0就异常中止。

除此之外,上述代码中还包含了两个以"spec"定义的代码片段,这是什么呢? 这当然就是今天要学习的Move的形式化验证方法了。

形式化验证概念

形式化验证(formal verification)是指使用数学工具分析设计可能行为的空间,是一种使用严格的数学方法来描述行为和推理计算机系统的正确性的技术。

形式化验证经常被应用在操作系统、编译器等对正确性要求高的领域。智能合约在其设计的正确性和安全性方面都有较高的要求,Move Prover就是利用形式化验证方法,为防止 Move 语言编写的智能合约中的错误而设计的工具。

在前面的prove.move代码中的spec代码就是用 Move 规范语言(MSL)写成的,用以说明程序代码应该满足的条件,或者描述函数的行为。

比如spec plus1就是描述了确保plus1函数的结果要等于x+1,spec abortsIf0确保当且仅当x为0时异常中止。

如何编写验证代码

我们先根据aptos.dev/move/prover... 中的示例来学习,首先是一个代码示例:

js 复制代码
module 0x0::m {
    struct Counter has key {
        value: u8,
    }

    public fun increment(a: address) acquires Counter {
        let r = borrow_global_mut<Counter>(a);
        r.value = r.value + 1;
    }

    spec increment {
        aborts_if !exists<Counter>(a);
        ensures global<Counter>(a).value == old(global<Counter>(a)).value + 1;
    }
}

该代码示例原本实现的是一个计数器功能,对一个u8类型的变量进行每次加一的计数功能。

我们按照之前aptos工程的配置设置好之后,在工程目录下运行 aptos move prove,得到结果如下:

可见Move Prover执行过程中出现了错误,这段错误提示的意思就是当这个u8类型的变量是255时,再进行加一操作,就会出现整形溢出的异常。我们已经知道在Move中如果出现整形溢出,Move当前运行程序会异常中止,而在我们编写spec规则时,仅考虑了a这个值不存在的情况,未考虑整形溢出的异常中止情况,因此可以对spec代码进行修正,增加对整形溢出的考虑,代码如下:

js 复制代码
spec increment {
        aborts_if global<Counter>(a).value == 255;
        aborts_if !exists<Counter>(a);
        ensures global<Counter>(a).value == old(global<Counter>(a)).value + 1;
    
    }

在进行验证,得到了验证成功的结果,如下图:

ps:有时候仅运行"aptos move prove"得到的错误信息不能很好的帮助我们定位问题,可以通过增加-t (--trace) 指令,查看详细的错误信息。

除了这个简单的例子外,还可以查看aptos-core/third_party/move/move-examples/experimental/basic-coin/sources/BasicCoin.move中的示例,这是一个典型的coin基本功能的合约,可以看到示例中为coin的余额查询,transfer, withdraw, deposit, burn这些常用功能都写了spec代码,并且有避免代码冗余而抽象出来的Schema写法。

我们平时对自定义的Aptos Coin做形式化验证时,可以参考示例代码中的写法,此处就不一一展开细讲了。看完示例后,按照笔者自己的理解,编写spec代码就是用MSL语言编写函数应该遵循的条件,常用的设计spec代码的思路可以有:

  1. 给出函数触发异常中止的条件,一般用aborts_if
  2. 对函数的返回值的判断,一般配合ensures使用
  3. 函数的入参需要满足的条件,比如transfer函数中转账金额不得大于转出账户余额
  4. 函数一定需要满足的条件,比如transfer函数的转入账户最终余额不会超过MAX值

对Move的形式化验证学习和理解就到此结束了,掌握了基本方法就能够编写出基本的验证代码。但若想发挥出形式化验证的更强功效,还是需要多进行实践和练习。

相关推荐
CESS_Cloud1 天前
CESS 出席华盛顿区块链政策峰会:参与国家安全与数据隐私保护专题讨论
安全·阿里云·web3·去中心化·区块链
TianXuan_Chain3 天前
web3跨链桥协议-Nomad
web3·区块链·智能合约·跨链桥
CertiK3 天前
Web3.0安全开发实践:探索比特币DeFi生态中的PSBT
区块链
选择不变3 天前
慢牛提速经典K线形态-突破下跌起始位和回档三五线,以及徐徐上升三种形态
区块链·通达信指标公式·炒股技巧·短线指标·炒股指标
飞天阁3 天前
Hyperledger Fabric 2.x 环境搭建
运维·区块链·fabric
Sui_Network3 天前
Sui 基金会任命 Christian Thompson 为新任负责人
大数据·人工智能·物联网·区块链·智能合约
电报号dapp1193 天前
NFT交易所开发攻略:打造未来数字艺术品交易新平台
人工智能·去中心化·区块链·智能合约
Q8137574603 天前
中阳动态分散投资策略:构建多元化投资组合的科学路径
人工智能·区块链
BlockOne113 天前
如何用发链框架,快速构建一条区块链?
区块链
web3探路者3 天前
解锁未来:深入探索去中心化应用程序(DApps)的潜力与挑战
去中心化·区块链·dapp开发·blockchain·dapps开发·去中心化 产品开发·dapps