BitsLabSec/movy

GitHub: BitsLabSec/movy

Movy 是一个面向 Move 语言的先进测试框架,为 Sui 等链上智能合约提供模糊测试、不变量验证和执行追踪能力。

Stars: 16 | Forks: 8

# Movy ![movy](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/98cec966b1230653.jpg) **Movy** 是一个 Move 测试框架,提供以下功能: - 用于 Move 语言的模块化底层基础构件。具体而言,借鉴自 [revm](https://github.com/bluealloy/revm) 的 executor 和 tracer 抽象以及分层数据库设计,允许您模拟和检查执行过程。 - 继承自最先进的静态分析器 [MoveScan](https://dl.acm.org/doi/10.1145/3650212.3680391) 的静态分析能力。 - 借鉴 [Belobog](https://github.com/abortfuzz/belobog) 从头重新实现的尖端模糊测试,支持属性测试和链上模糊测试,其风格类似于 [foundry](https://getfoundry.sh/forge/advanced-testing/overview),允许通过使用 Move 语言编写不变量来实现。 - 一个类似于 `forge test` 的运行器(`movy sui test`),可执行您的 `test_*` 函数并自动填充其 object 和类型参数。 - 以及更多功能…… 在[这里](https://docs.movy.rs)查看我们的文档。 __Movy 仍处于非常早期的 alpha 阶段,我们正在全力以赴开发新功能。__ ## 演示案例 ### 追踪 Transaction ``` let mut tracer = TreeTracer::new(); let _ = executor.run_tx_trace( tx, epoch, timestamp_ms, Some(tracer), )?; println!("The trace is:\n{}", trace.take_inner().pprint()); ``` 此代码片段可追踪任意 transaction `tx`,无论是链上的还是您自己构建的。 ### 不变量测试 在单个函数中部署您的 Move module,即使它需要多个 transaction。 ``` public fun movy_init( deployer: address, attacker: address ) { let mut scenario = ts::begin(deployer); { ts::next_tx(&mut scenario, deployer); counter::create(ts::ctx(&mut scenario)); }; ts::next_tx(&mut scenario, attacker); { let mut counter_val = ts::take_shared(&scenario); counter::increment(&mut counter_val, 0); assert!(counter::value(&counter_val) == 1, 0); ts::return_shared(counter_val); }; ts::end(scenario); } ``` 在您的 Move 测试 module 中为函数编写一个不变量测试: ``` #[test] public fun movy_pre_increment( movy: &mut context::MovyContext, ctr: &mut Counter, _n: u64 ) { let (ctr_id, val) = extract_counter(ctr); let state = context::borrow_mut_state(movy); bag::add(state, ctr_id, val); } #[test] public fun movy_post_increment( movy: &mut context::MovyContext, ctr: &mut Counter, n: u64 ) { let (ctr_id, new_val) = extract_counter(ctr); let state = context::borrow_state(movy); let previous_val = bag::borrow(state, ctr_id); if (*previous_val + n != new_val) { crash_because(b"Increment does not correctly inreases internal value.".to_string()); } } ``` ### 使用 `sui test` 运行测试 `movy sui test` 会构建并部署您的 package,运行 `movy_init`,然后执行每个名称以 `test_` 开头的 `#[test]` 函数——这与 `forge test` 非常相似。与原生的 Move 测试 运行器不同,这些测试函数可以带有 **参数**,包括 Sui object 和类型参数, 并且 `movy` 会为您自动填充它们。 ``` // test-data/counter/tests/movy.move #[test] fun test_counter_smoke() { assert!(1 + 1 == 2, 0); } // Object and type-parameter arguments are filled by movy: #[test] public fun test_increment_typed(ctr: &mut Counter) { let _ty = std::type_name::get(); let before = counter::value(ctr); counter::increment(ctr, 3); assert!(counter::value(ctr) == before + 3, 300); } ``` 运行 package 中的所有测试: ``` movy sui test --locals ./test-data/counter ``` #### 发现 object 和待定参数:`--only-init` `--only-init` 会运行 `movy_init`,然后打印它生成的 object 以及每个测试 函数及其仍然需要的参数——这是填充参数的起点: ``` movy sui test --locals ./test-data/counter --only-init ``` ``` === objects after movy_init (3) === deployer: 0xb641... attacker: 0xa773... 0x95e1… 0x2::coin::Coin<0x2::sui::SUI> [owned by 0xa773… (attacker)] v3 0xd726… ::counter::Counter [shared (v3)] v3 0xdbcf… 0x2::package::UpgradeCap [owned by 0xb641… (deployer)] v2 === test functions === ::counter_tests::test_counter_smoke() [no args] ::counter_tests::test_increment_typed(&mut ::counter::Counter) [needs args] ``` #### 填充参数:`--object-mapping` 和 `--test-ty` 将 object 参数绑定到特定的 object,并将类型参数固定为具体类型(使用 `--only-init` 打印的 object id): ``` movy sui test --locals ./test-data/counter \ --object-mapping 'counter::counter::Counter/0xd726…e5d3' \ --test-ty 'counter::counter_tests::test_increment_typed:0/0x2::sui::SUI' ``` - `--object-mapping /0x` — 使用给定的 object 填充 object 参数。 可重复使用(或以逗号分隔);相同类型的条目按参数顺序被消耗。 - `--test-ty :/` — 设置测试函数的类型参数 ``。 类型和函数选择器接受**本地 package 名称**(例如 `counter::counter::Counter`), 并解析为当前部署的地址。因此,即使部署的 package id 发生变化,映射在重新构建后依然有效, 并且您可以直接从 `--only-init` 或 `--trace` 中粘贴类型。未映射的 object/类型参数将退回到自动的、模糊测试器式的填充方式。 #### 固定部署地址:`--deploy-at` 由 `movy_init` 生成的 object id 在源码编辑后保持稳定,但新部署的 package 在其 bytecode 发生变化时会被分配一个新的 id——这也会改变每个类型字符串(`::counter::Counter`)。将 package 固定到一个特定地址,以保持 id 和类型字符串的稳定: ``` movy sui test --locals ./test-data/counter --deploy-at counter:0xcafe… ``` `--deploy-at :0x
` 可重复使用,并通过名称匹配 package。 #### 失败与可复现性 当 transaction 中止时(例如 `assert!` 失败),测试即会失败(退出码非零)。此外, 您的 `movy_pre_*` / `movy_post_*` 不变量会在每个测试运行期间被应用,因此通过 `crash_because` 报告的不变量违规也会导致测试失败,并显示原因: ``` oracle crash detected for ::counter_tests::test_increment_typed: Counter should be always increasing ``` `--seed` 会固定 RNG(进而固定 gas object 和新派生的 package/object id),而 `--checkpoint` / `--epoch` / `--epoch-ms` 会固定链上上下文,让运行过程完全离线工作(否则它们将从 `--rpc` 获取)。传递 `--trace` 以打印每个测试的执行轨迹。 ### Call Graph 与 Type Graph 为 Move package 生成 Type Graph。 ![type graph](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/d0b1dcee29230659.svg) 为 Move package 生成 Call Graph。 ![call graph](https://static.pigsec.cn/wp-content/uploads/repos/2026/06/fcd1230c93230707.svg) ### 静态分析 TODO。 ## 用法 ### 将 Movy 作为工具使用 安装依赖: ``` apt install -y libssl-dev libclang-dev ``` 构建 `movy` 二进制文件。 ``` git clone https://github.com/BitsLabSec/movy cd movy cargo build --release ``` 请注意,必须安装稳定的 Rust 工具链。 查看用法菜单。 ``` ./target/release/movy --help ``` ### 将 Movy 作为库使用 将此内容添加到您的 `Cargo.toml` 中 ``` movy = {git = "https://github.com/BitsLabSec/movy", branch = "master"} ``` 遗憾的是,`sui` 和 `aptos` 都不在 `crates.io` 上,因此我们目前无法发布 crate,除非我们完全重新实现这两个链的 MoveVM。 ### 编写不变量 要为合约编写不变量,请参阅 [counter 示例](./test-data/counter/tests/movy.move)。请注意,您需要将这一行添加到您的 `Move.toml` 中。它是测试依赖项,永远不会在链上运行。 ``` [dev-dependencies] movy = {git = "https://github.com/BitsLabSec/movy", subdir = "move/movy", rev = "master"} ``` ## Roadmap 目前,`movy` 处于非常早期的 alpha 阶段,尚缺少以下功能: - 将我们的更改上游至 [sui](https://github.com/MystenLabs/sui) 和 [aptos-core](https://github.com/aptos-labs/aptos-core) - 完整的 Aptos 支持。(我们有一个用于此目的的私有分支,但仍在摸索一个良好的 API 设计。) - 链上事件回测。 ## 致谢 Belobog 的灵感来源于几个开创性的项目: - [Belobog](https://github.com/abortfuzz/belobog) - [ityfuzz](https://github.com/fuzzland/ityfuzz) - [move-fuzzer](https://github.com/fuzzland/move-fuzzer) - [sui-fuzzer](https://github.com/FuzzingLabs/sui-fuzzer) - [historical-dev-inspect](https://github.com/kklas/historical-dev-inspect)
标签:Move语言, SOC Prime, 云安全监控, 区块链, 可视化界面, 开发工具, 测试框架, 静态分析