caliperforge/cf-invariants-jito-tippayment
GitHub: caliperforge/cf-invariants-jito-tippayment
该项目是一个基于 Crucible 的不变性模糊测试 harness,用于验证 Jito tip-payment 程序在 Solana 上的关键状态更新不变性,通过对比干净实现与植入错误的双胞胎版本来确保测试的有效性。
Stars: 0 | Forks: 0
# cf-invariants-jito-tippayment
**一个用于 [Jito tip-payment program](https://github.com/jito-foundation/jito-programs/tree/master/mev-programs/programs/tip-payment) 的不变性模糊测试 harness,运行在 [Crucible](https://github.com/asymmetric-research/crucible) 上。**
cf-invariants-jito-tippayment 是一个专用的 harness,而不是一个新的 fuzzer。它
将上游的 Jito tip-payment program 从 `anchor-lang` 0.31.1
迁移到 `anchor-lang` 1.0.1,以便由 Crucible v0.2.0(LibAFL
+ LiteSVM)驱动,然后针对干净的参考实现和植入单个错误的双胞胎版本运行不变性类测试。每次推送时,CI 都会重新构建两个
程序变体,并断言 `clean = 0` 违规和 `planted >= 1`
违规。
这是由同一运营者发布的
[cf-invariants-jito](https://github.com/caliperforge/cf-invariants-jito)
(Jito tip-distribution)的兄弟项目。它是
*第二个* 在相同的 anchor-lang 1.0.1
/ Crucible v0.2.0 / platform-tools v1.52 环境下进行 harness 测试的真实 Jito 程序 —— 证明该基础设施
不仅适用于单一目标。
## 范围 — 什么是 Jito tip-payment,此 harness 涵盖的内容
Jito tip-payment program 是 Solana 上
[Jito](https://www.jito.network/) MEV-redistribution 堆栈的链上部分,
用于接收验证者的小费。搜索者将小费打入 8 个硬编码的
`TipPaymentAccount` PDA 中(并行处理);然后验证者调用
`change_tip_receiver` 或 `change_block_builder` 来清空这些 PDA
并将小费记入当前的小费接收者 + 区块构建者。
链上的 `Config` PDA 存储轮换状态 —— 当前的
`tip_receiver`、当前的 `block_builder`,以及
`block_builder_commission_pct`。这两条轮换指令都会
读取此配置,通过 `handle_payments` 清空 8 个 tip PDA,然后
将新的身份写入 Config。
上游代码位于
`jito-foundation/jito-programs/mev-programs/programs/tip-payment`
并采用 Apache-2.0 许可证。
此 harness 不会修改生产程序。它的目标是
`change_tip_receiver` 的**不变性测试面** —— 即无论进行何种调用序列的模糊测试都必须成立的结构
属性 —— 并证明该 harness 能够在干净的参考实现上确认该属性,
并捕获故意植入的回归错误。
## 它测试什么 — 三个不变性类
| 类别 | 测试中的不变性 | 植入错误的位置 |
|---|---|---|
| `change_tip_receiver_state_update` | `invariant_change_tip_receiver_updates_config` — 每次成功的 `change_tip_receiver` 调用后,链上的 `Config.tip_receiver` 等于调用者传入的 `new_tip_receiver` 公钥。 | `programs/tip-payment/src/lib.rs::change_tip_receiver` — `ctx.accounts.config.tip_receiver = ctx.accounts.new_tip_receiver.key();` 提交行被删除。该指令的 lamport 侧效应(清空 + 记入)仍正常运行,因此 Solana 的 runtime 余额检查不会被触发;只有轮换永远不会提交。 |
| `change_block_builder_state_update` | `invariant_change_block_builder_updates_config` — 每次成功的 `change_block_builder` 调用后,链上的 `Config.block_builder` 等于 `new_block_builder` 参数,且 `Config.block_builder_commission_pct` 等于 `block_builder_commission` 参数。 | `programs/tip-payment/src/lib.rs::change_block_builder` — `ctx.accounts.config.block_builder = ctx.accounts.new_block_builder.key();` 提交行被删除。其下方的佣金写入操作仍会运行,因此 lamport 余额检查会通过;只有 block-builder 轮换永远不会提交。 |
| `block_builder_commission_pct_bounds` | `invariant_block_builder_commission_pct_bound` — 在任何时候,链上的 `Config.block_builder_commission_pct` 都在 `[0, 100]` 之间。(该程序通过 `require_gte!(100, …)` 强制使用百分比,而不是基点。) | `programs/tip-payment/src/lib.rs::change_block_builder` — `require_gte!(100, block_builder_commission, …)` 门控被删除。随后该指令会接受调用者提供的任何 `u64` 并将其提交给 `Config.block_builder_commission_pct`。 |
**为什么选择这个不变性集合,而不是 lamport 守恒。** 对于 tip-payment,
一个自然的首选是“每次成功调用后,`{recv_a, recv_b, config, 8
tip_pdas}` 中的总 lamports 是守恒的。”我们首先尝试了这个方案;
但它无法作为 Crucible 的不变性,因为 Solana
SVM runtime 原生地强制每条指令遵守总 lamport 守恒。任何
未能平衡借贷的程序,其交易都会在 fixture 的
`read_account` 察觉到差异之前被 runtime *提前* 拒绝 —— 因此该不变性无法
有效地观察到违规情况。对于此程序,对用户有意义的结构
不变性必须存在于 runtime 不监管的指令后状态
形态中:`Config.tip_receiver` /
`Config.block_builder`(链上轮换是否实际
提交)和 `Config.block_builder_commission_pct`(百分比
门控是否保持)。
已发布提交上的 CI 结果:在三组类别对中的每一对上,均为 `clean = 0` 违规和
`planted >= 1` 违规。CI
徽章是真实情况的来源 —— 如果它是红色的,则说明 harness 损坏了。
## 仓库布局
```
.
├── programs/tip-payment/ # cf-invariants-jito-tippayment port (anchor-lang 1.0.1)
├── references/
│ ├── jito_tippay_ref/ # clean baseline + Crucible fuzz fixtures
│ │ ├── programs/tip-payment/ # ported program (== port above)
│ │ ├── fuzz/jito_change_tip_receiver_state/ # fixture: change_tip_receiver_state_update
│ │ ├── fuzz/jito_change_block_builder_state/ # fixture: change_block_builder_state_update
│ │ └── fuzz/jito_block_builder_commission_bounds/ # fixture: block_builder_commission_pct_bounds
│ ├── jito_tippay_ref_planted_change_tip_receiver_state/ # planted twin (drops the tip-receiver commit)
│ ├── jito_tippay_ref_planted_change_block_builder_state/ # planted twin (drops the block-builder commit)
│ └── jito_tippay_ref_planted_block_builder_commission/ # planted twin (drops the require_gte! gate)
├── .github/workflows/ci.yml # CI: workspace check + build-sbf + harness matrix
├── Cargo.toml # workspace
├── LICENSE # Apache-2.0 (CaliperForge)
├── NOTICE # Jito attribution + modification log
└── README.md
```
每个不变性类别在
`references/jito_tippay_ref/fuzz//src/main.rs` 下都有一个 fixture 真理来源;CI 会在运行前将其
复制到匹配的植入变体中,因此干净运行和植入运行之间的
唯一区别是加载到 LiteSVM 中的 `.so`
二进制文件。
## 锁定的工具链
这些是 CI 在每次推送时构建所依据的版本(参见
[`.github/workflows/ci.yml`](./.github/workflows/ci.yml))。版本锁定
继承自姐妹项目 cf-invariants-jito 的 CI-green 堆栈:
- Rust **stable**。
- `anchor-lang` **1.0.1** — 与 Crucible v0.2.0 的工作区匹配。
- 上游的 [Crucible](https://github.com/asymmetric-research/crucible) **v0.2.0** — 在 CI 中从源码构建(`cargo install --path crates/crucible-fuzz-cli`)。
- 用于 `cargo-build-sbf` 的 Anza / Solana CLI **v2.1.21**。
- Solana platform-tools **v1.52**(作为 `--tools-version v1.52` 传递;
Crucible v0.2.0 的依赖项需要 `edition2024` 支持,而早期的
platform-tools 的 rustc 无法构建)。
- `solana-sdk-ids` **3**(用于模块化替换上游的
`solana-program = "2.2"`,以提供 `loader_v4` / `bpf_loader` /
`sysvar` / `config` / `secp256r1_program` / `native_loader` ID
模块,供 `is_program`/`is_sysvar`/`is_config` 使用)。
fuzz `Cargo.toml` 通过路径依赖引用 Crucible,路径为
`../../../../../crucible/...`,即 `/../crucible`。CI
会在 harness 步骤之前将 Crucible v0.2.0 克隆到该同级路径。
对于本地复现,请执行相同操作。
## 从全新克隆中复现
CI 在每次推送时都会运行以下确切步骤。本地复现是
可选的,并且需要安装上述工具链并将其放入 `PATH`。
```
# 1. 将此 repo 和 Crucible v0.2.0 作为同级目录 Clone。
git clone https://github.com/caliperforge/cf-invariants-jito-tippayment.git
git clone --depth 1 --branch v0.2.0 \
https://github.com/asymmetric-research/crucible.git
cd cf-invariants-jito-tippayment
# 2. Workspace 检查(在 CI 中也作为 workspace-check job 运行)。
cargo check --workspace --locked || cargo check --workspace
# 3. 构建 cf-invariants-jito-tippayment port (SBPF)。
cargo build-sbf --tools-version v1.52 \
--manifest-path programs/tip-payment/Cargo.toml
# 4. 构建 clean reference 和三个植入的 twins。
for variant in jito_tippay_ref \
jito_tippay_ref_planted_change_tip_receiver_state \
jito_tippay_ref_planted_change_block_builder_state \
jito_tippay_ref_planted_block_builder_commission; do
cargo build-sbf --tools-version v1.52 \
--manifest-path "references/${variant}/programs/tip-payment/Cargo.toml"
done
# 5. 从源码构建并安装 Crucible CLI。
(cd ../crucible && cargo install --path crates/crucible-fuzz-cli --locked)
# 6. 运行三个 clean pairs(预期没有 FUZZ_FINDING / [VIOLATION] 行)。
(cd references/jito_tippay_ref/fuzz/jito_change_tip_receiver_state && \
crucible run jito_tip_payment invariant_change_tip_receiver_updates_config \
--release --timeout 30)
(cd references/jito_tippay_ref/fuzz/jito_change_block_builder_state && \
crucible run jito_tip_payment invariant_change_block_builder_updates_config \
--release --timeout 30)
(cd references/jito_tippay_ref/fuzz/jito_block_builder_commission_bounds && \
crucible run jito_tip_payment invariant_block_builder_commission_pct_bound \
--release --timeout 30)
# 7. 对植入的 twins 运行相同的 invariants(预期每个在约 1s 内出现 violations)。
(cd references/jito_tippay_ref_planted_change_tip_receiver_state/fuzz/jito_change_tip_receiver_state && \
crucible run jito_tip_payment invariant_change_tip_receiver_updates_config \
--release --timeout 30)
(cd references/jito_tippay_ref_planted_change_block_builder_state/fuzz/jito_change_block_builder_state && \
crucible run jito_tip_payment invariant_change_block_builder_updates_config \
--release --timeout 30)
(cd references/jito_tippay_ref_planted_block_builder_commission/fuzz/jito_block_builder_commission_bounds && \
crucible run jito_tip_payment invariant_block_builder_commission_pct_bound \
--release --timeout 30)
```
CI 在每次推送时都会运行第 2 到第 7 步。捕获的记分卡(原始
Crucible 输出,已去除 ANSI)作为
`crucible-scorecards` 工作流 artifact 上传,并写入到运行器内部的
`findings/_/scorecard.md`。
`findings/` 已被 gitignore;CI artifact 是权威记录。
有关规范序列,请参见
[`.github/workflows/ci.yml`](./.github/workflows/ci.yml)。
## 这不是什么
- **不是 Crucible 的分支。** Crucible 才是 harness;
cf-invariants-jito-tippayment 只是一个运行在其之上的 target + fuzz fixture。LiteSVM 执行基础设施和
IDL 驱动的 fuzzing 管道归功于 Asymmetric Research。
- **不是 Jito 安全审计。** 植入的双胞胎是一个合成的
单点回归,旨在证明相应的不变性类被触发。单凭此
harness 不对生产环境的 Jito
程序的安全性做任何声明。
- **不是形式化验证工具。** 这是随机不变性 fuzzing,
而不是证明。
## 报告问题、安全联系方式
在此 GitHub 仓库上开启一个 issue,或联系
[michael@caliperforge.com](mailto:michael@caliperforge.com)。
## 许可证
Apache-2.0。参见 [`LICENSE`](./LICENSE) 和 [`NOTICE`](./NOTICE)。
`NOTICE` 文件保留了 Jito 上游的 Apache-2.0 出处,并
描述了相对于上游的修改。
cf-invariants-jito-tippayment 由 Michael Moffett 在
CaliperForge 名下运营。CaliperForge 是一个个人运营的工程工作室。
此脚手架是在 AI 协助下构建的。由 Michael Moffett(CaliperForge 运营者)
编写并审查。完整政策请访问
[caliperforge.com/ai-disclosure](https://caliperforge.com/ai-disclosure)。
[caliperforge.com](https://caliperforge.com)
标签:Jito, Solana, 区块链, 可视化界面, 智能合约审计, 通知系统