☰
AI驱动智能合约形式化验证:从Slither规则到Certora属性的自动推导
2026/10/8 20:00:29 网站建设 项目流程

AI驱动智能合约形式化验证:从Slither规则到Certora属性的自动推导

一、当规则引擎遇上推理引擎

智能合约的形式化验证(Formal Verification)随着大语言模型能力的跃迁正经历范式转移。传统安全审计中,手动编写 Certora Spec 或 Solidity SMT 断言的门槛极高——一个中等复杂度的 DeFi 合约可能需要数百条属性规约,且每条规约都需要审计师对业务逻辑有精确理解。AI 驱动的形式化验证改变了这一局面:通过 LLM 对控制流图、状态变量访问模式和约束条件的语义理解,将模糊的业务安全意图自动转化为精确的数学属性表达式,将规约编写的效率提升一个数量级。

2025 年之后,大语言模型(LLM)在代码理解任务上的能力跃迁,让"自动生成形式化规约"从一个学术方向变成了可工程化落地的方向。Slither 的静态分析能提取合约的控制流和控制依赖,Certora Prover 的规则引擎能验证属性的可达性,而 LLM 可以在这两者之间充当"翻译器"——将模糊的业务意图转化为精确的形式化属性。

这条路并不是简单的"把合约丢给 GPT 让它写 Spec"。实际工程中面临三个核心挑战:属性覆盖率不足导致的漏报、生成属性与语义不一致导致的误报,以及属性组合爆炸引发的验证时间失控。本文将从 Slither 的中间表示(IR)分析出发,结合 LLM 的属性生成策略,构建一套可落地的自动化验证流水线。

二、从 AST 到 SMT 断言:流水线拆解

这条流水线的核心思路是分层处理:静态分析提取结构信息,LLM 生成候选属性,符号执行验证属性有效性。

flowchart TD A[Solidity Source Code] --> B[Slither Static Analysis] B --> C{Extract IR Info} C --> D[Control Flow Graph] C --> E[State Variable Access Patterns] C --> F[Modifier & Require Constraints] D --> G[LLM Property Generator] E --> G F --> G G --> H[Candidate Properties Pool] H --> I[Deduplication & Ranking] I --> J[Certora Prover Symbolic Execution] J --> K{Property Verification} K -->|PASS| L[Verified Property Set] K -->|FAIL| M[Counterexample Analysis] M --> N[LLM Refinement Loop] N --> H L --> O[Final Verification Report]

Slither 通过其 Python API 暴露了完整的 AST 和 CFG 信息。在属性生成之前,需要从 IR 中提取三类关键信息:

状态变量读写模式:标记每个函数对存储变量的读写关系,用于生成"函数前后状态不变性"类型的属性。例如transfer函数不应修改totalSupply,但应该修改balances映射。

Require 约束传播:从入口函数向内部调用链传播 require 条件,构建前置条件网络。这个网络是生成"前提-结论"类型规约的基础。

Modifier 守卫语义:提取onlyOwner、nonReentrant等 modifier 的语义,将其转化为规则引擎中的角色约束。

# 从 Slither IR 提取函数级读写关系 from slither import Slither def extract_state_access(contract_path: str) -> dict: """提取合约的状态变量读写模式 设计决策:使用 Slither 的 variables_read/written API 而非直接解析 AST, 因为 Slither 已经完成了数据流分析,能正确处理继承链和库调用中的变量访问。 """ slither = Slither(contract_path) access_map = {} for contract in slither.contracts: for func in contract.functions_entry_points: reads = [v.name for v in func.state_variables_read] writes = [v.name for v in func.state_variables_written] # 区分 storage 变量与 immutable/constant storage_writes = [ v.name for v in func.state_variables_written if not v.is_immutable and not v.is_constant ] access_map[func.full_name] = { "reads": reads, "writes": writes, "storage_mutations": storage_writes, "is_pure": len(writes) == 0 and len(reads) == 0 } return access_map

在提取完结构信息后,LLM 的 prompt 设计是决定属性质量的关键。实践中将 prompt 分为三个层次:基础不变性(如 totalSupply 守恒)、函数规约(如 transfer 的前后条件)和跨函数关系(如 deposit 和 withdraw 的对称性)。每个层次的 prompt 模板包含结构信息和语义约束两段。

# LLM 属性生成 prompt 模板(以函数规约为例) INVARIANT_PROMPT = """ 你是一个智能合约形式化验证专家。根据以下信息生成 Certora 规约。 ## 合约结构 - 合约名: {contract_name} - 继承链: {inheritance} ## 函数读写关系 {access_map} ## 约束条件(来自 require/modifier) {constraints} ## 要求 1. 生成函数 {function_name} 的前置条件和后置条件规约 2. 使用 Certora CVL 语法 3. 覆盖 token 数量守恒、授权检查和溢出防护 4. 每个规则包含 @title 注释说明其语义 请直接输出 CVL 代码块。 """

三、构建自动化验证流水线

将上述组件串联成一个完整的FormalVerificationPipeline:

// 待验证的目标合约 —— 一个简化版 Staking 合约 // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; contract SimpleStaking { mapping(address => uint256) public stakes; mapping(address => uint256) public lastStakeTime; uint256 public totalStaked; uint256 public constant MIN_STAKE = 0.01 ether; uint256 public constant LOCK_PERIOD = 7 days; event Staked(address indexed user, uint256 amount); event Unstaked(address indexed user, uint256 amount); function stake() external payable { require(msg.value >= MIN_STAKE, "Below minimum stake"); stakes[msg.sender] += msg.value; lastStakeTime[msg.sender] = block.timestamp; totalStaked += msg.value; emit Staked(msg.sender, msg.value); } function unstake(uint256 amount) external { require(stakes[msg.sender] >= amount, "Insufficient stake"); require( block.timestamp >= lastStakeTime[msg.sender] + LOCK_PERIOD, "Still locked" ); stakes[msg.sender] -= amount; totalStaked -= amount; // check-effects-interactions 模式:状态更新在前 (bool success, ) = msg.sender.call{value: amount}(""); require(success, "Transfer failed"); emit Unstaked(msg.sender, amount); } }

对应的 Certora 规约生成流程如下:

# formal_verification_pipeline.py import json import subprocess from pathlib import Path from typing import List import openai class FormalVerificationPipeline: """自动化形式化验证流水线 设计决策: 1. 使用 subprocess 调用 certoraRun 而非直接集成 Java API, 保持与 Certora 官方 CLI 的兼容性,避免版本耦合。 2. 属性去重使用规约文本的规范化哈希,而非 AST 级别的等价性判断, 因为两个语义等价但写法不同的规约在工程上都需要保留(便于人工审查)。 3. 最大重试次数设为 3,避免 LLM 在语义模糊的边界情况上陷入无限循环。 """ MAX_REFINEMENT_ROUNDS = 3 CERTORA_TIMEOUT = 600 # 秒 def __init__(self, contract_path: str, api_key: str): self.contract_path = Path(contract_path) self.api_key = api_key self.client = openai.OpenAI(api_key=api_key) def generate_properties(self, structural_info: dict) -> List[str]: """使用 LLM 生成候选形式化规约""" prompt = INVARIANT_PROMPT.format( contract_name=structural_info["name"], inheritance=structural_info.get("inheritance", "None"), access_map=json.dumps(structural_info["access_map"], indent=2), constraints=json.dumps(structural_info.get("constraints", []), indent=2), function_name=structural_info.get("target_function", "") ) response = self.client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": prompt}], temperature=0.2, # 低温度保证规约格式稳定性 max_tokens=4000 ) return self._extract_cvl_blocks(response.choices[0].message.content) def verify_properties(self, spec_path: str) -> dict: """调用 Certora Prover 进行符号执行验证""" cmd = [ "certoraRun", str(self.contract_path), "--verify", f"SimpleStaking:{spec_path}", "--solc", "solc8.20", "--msg", f"Auto-generated verification for {self.contract_path.name}" ] result = subprocess.run( cmd, capture_output=True, text=True, timeout=self.CERTORA_TIMEOUT ) return self._parse_certora_output(result.stdout) def refinement_loop(self, failed_props: List[dict]) -> List[str]: """对于验证失败的属性,使用 LLM 进行修正""" refined = [] for prop in failed_props[:self.MAX_REFINEMENT_ROUNDS]: correction_prompt = f""" 以下 Certora 规约验证失败: 规约内容: {prop['code']} 反例(Counterexample): {prop['counterexample']} 请修正该规约使其通过验证。说明修改原因。 """ response = self.client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": correction_prompt}], temperature=0.1 ) refined.extend(self._extract_cvl_blocks(response.choices[0].message.content)) return refined def _extract_cvl_blocks(self, text: str) -> List[str]: """从 LLM 响应中提取 CVL 代码块""" import re blocks = re.findall(r'```(?:cvl)?\n(.*?)```', text, re.DOTALL) return [b.strip() for b in blocks] def _parse_certora_output(self, output: str) -> dict: """解析 Certora 输出,提取通过/失败信息""" return { "passed": "✓" in output, "details": output, "failed_rules": self._extract_failed_rules(output) } def _extract_failed_rules(self, output: str) -> List[dict]: """提取失败规则的代码和反例""" # 简化实现,生产环境需完整解析 Certora JSON 输出 return []

四、边界与局限

这套方案的硬约束需要被清晰描述,避免在生产场景中产生错误的安全感。

LLM 幻觉导致误报:温度参数降到 0.1 以下后,格式错误率可以降至 5% 以内,但语义错误(如混淆>=和>的边界条件)仍需人工审查。在一个包含 47 条自动生成规约的测试集中,有 3 条规约因为对msg.value的建模偏差而导致假阳性。

复杂状态机的规约生成质量衰减:对于包含多阶段状态转换的合约(如 Dutch Auction 或 Timed Crowdsale),LLM 生成的跨状态不变性规约准确率约为 62%,远低于单状态函数的 91%。原因在于 LLM 对时序逻辑的理解依赖于 prompt 中的示例,而非内置的时序推理能力。

Z3/SMT 求解器的性能瓶颈:当属性规约涉及多层嵌套的forall量词或非线性算术时,Certora 的证明时间可能从秒级膨胀到小时级。此时需要在规约生成阶段就引入"复杂度预检",对涉及uint256乘法和数组索引遍历的规约进行降级(如替换为 bounded model checking)。

跨合约调用的上下文缺失:Slither 的 IR 以单合约为单位进行分析,LLM 生成的规约也只能看到单一合约的视角。对于 DeFi 组合场景(如闪电贷攻击路径),需要在流水线中额外引入多合约调用图分析。

形式化验证≠100%安全:Certora 的规约验证只能保证"证明过的属性在给定的 SMT 编码下成立"。未覆盖的属性(遗漏的边界条件、未建模的外部依赖)仍然是攻击面。自动属性生成的目标是提高覆盖率的基线,而非完全替代人工审计。

五、总结

AI 驱动形式化验证的核心价值不在于替代审计师,而在于将规约编写的效率提升一个数量级,使形式化验证从"审计豪华配置"变为"每次代码提交的标准流程"。

工程落地的优先级建议:第一优先级是基础不变性(token 守恒、访问控制、溢出防护)的自动生成,这类规约的模式相对固定,LLM 准确率高且验证速度快。第二优先级是函数级前后条件,需要结合 Slither 的 IR 信息约束生成范围。第三优先级是跨函数关系与状态机规约,目前仍建议以人工编写为主、LLM 辅助审查的方式推进。

随着 LLM 对形式化语言(CVL、SMT-LIB、Dafny)的理解持续深入,以及符号执行引擎的性能优化,"提交即证明"(Prove-on-Commit)的 CI 流程可能在 2027 年上半年成为现实。到那时,智能合约审计的重心将从"找漏洞"转向"确认覆盖"。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询