caliperforge/cf-invariants-jito-priorityfee
GitHub: caliperforge/cf-invariants-jito-priorityfee
针对 Jito 优先费用分配程序的不变性模糊测试 harness,通过 Crucible 驱动干净与植入缺陷双版本对比来验证链上状态更新的正确性。
Stars: 0 | Forks: 0
# cf-invariants-jito-priorityfee
[](https://github.com/caliperforge/cf-invariants-jito-priorityfee/actions/workflows/ci.yml)
**一个针对 [Jito priority-fee-distribution 程序](https://github.com/jito-foundation/jito-programs/tree/master/mev-programs/programs/priority-fee-distribution) 的不变性模糊测试 harness,运行在 [Crucible](https://github.com/asymmetric-research/crucible) 上。**
cf-invariants-jito-priorityfee 是一个专注的 harness,而不是一个新的 fuzzer。
它将上游的 Jito priority-fee-distribution 程序从
`anchor-lang` 0.31.1 移植到 `anchor-lang` 1.0.1,以便能由
Crucible v0.2.0 (LibAFL + LiteSVM) 驱动,然后针对干净的参考版本和植入单点 bug 的孪生版本运行一个不变性类别。每次
push,CI 都会重新构建两个程序变体,并断言 `clean = 0`
违规和 `planted >= 1` 违规。
这是
[cf-invariants-jito](https://github.com/caliperforge/cf-invariants-jito)
(Jito tip-distribution) 和
[cf-invariants-jito-tippayment](https://github.com/caliperforge/cf-invariants-jito-tippayment)
(Jito tip-payment) 的同级产物,由同一运营者发布。这是在相同的 anchor-lang 1.0.1 /
Crucible v0.2.0 / platform-tools v1.52 轨道上进行 harness 测试的*第三个*
真实 Jito 程序——证明该轨道可以推广到单个目标之外。
## 范围 — 什么是 Jito priority-fee-distribution,本 harness 涵盖了什么
Jito priority-fee-distribution 程序是
Solana 上的 [Jito](https://www.jito.network/) MEV-redistribution 栈的链上部分,
负责验证者-质押者分配的 *priority-fee* 部分,
这与负责 *tip* 部分的 tip-distribution 程序不同。验证者通过
`transfer_priority_fee_tips` 将 priority-fee lamports 记入每个 epoch 的
`PriorityFeeDistributionAccount` (PFDA);一旦上传了 merkle root,质押者就可以据此申领。
PFDA 会跟踪一个 `total_lamports_transferred` 字段——这是对每次成功的 `transfer_priority_fee_tips` 调用的
`lamports` 参数的结构化运行总和。这个字段存在于
`priority-fee-distribution` 的 `PriorityFeeDistributionAccount` 上,
但不存在于 tip-distribution 的 `TipDistributionAccount` 上,使其成为该程序核心价值的独有特性。
上游代码位于
`jito-foundation/jito-programs/mev-programs/programs/priority-fee-distribution`
并采用 Apache-2.0 许可证。
本 harness 没有修改生产环境的程序。它针对的是 `transfer_priority_fee_tips` 的**不变性面**——即无论进行何种调用序列的模糊测试都必须成立的结构化属性——并证明该 harness 能够在干净的参考版本上确认该属性,并捕获一个故意植入的回归缺陷。
## 测试内容 — 一个不变性类别
| 类别 | 测试中的不变性 | 植入 bug 的位置 |
|---|---|---|
| `transfer_increments_total_state_update` | `invariant_transfer_priority_fee_tips_increments_total` — 在每次成功的 `transfer_priority_fee_tips(lamports)` 调用之后,链上的 `PriorityFeeDistributionAccount.total_lamports_transferred` 都会增加确切的 `lamports` 数量(所有成功调用的总和等于 fixture 端的预言机)。 | `programs/priority-fee-distribution/src/lib.rs::transfer_priority_fee_tips` — `ctx.accounts.priority_fee_distribution_account.increment_total_lamports_transferred(lamports)?;` 调用被移除。该指令的 system-program lamport transfer 仍然正常运行,因此 Solana 的 runtime 余额检查不会被触发;只是新的总和到 PFDA 状态的结构化提交永远不会发生。 |
**为什么是这个不变性,而不是 lamport 守恒。** 对于任何支付程序来说,一个自然的首选是“`{from, pfda, expired_funds_account}` 中的总 lamports 在每次成功调用时都是守恒的。”这不能作为 Crucible 的不变性,因为 Solana SVM runtime 原生地强制执行每条指令的 total-lamport 守恒。任何未能平衡借贷的程序,其交易都会在 fixture 的 `read_account` 发现差异*之前*,被 runtime 拒绝——因此该不变性无法有效地观察到违规情况。对于该程序而言,一个对用户有意义的结构化不变性必须存在于 runtime 不会监管的指令后状态形态中。`PriorityFeeDistributionAccount.total_lamports_transferred`
——即链上的运行总和是否真正提交——正是这种形态,而且它是 *priority-fee 特有的*,不与 tip-distribution 同类共享。
在发布的提交上的 CI 结果:`clean = 0` 违规和
`planted >= 1` 违规。CI 徽章是真相的来源——如果
它是红色的,则说明 harness 已损坏。
## 仓库布局
```
.
├── programs/priority-fee-distribution/ # cf-invariants-jito-priorityfee port (anchor-lang 1.0.1)
├── programs/vote-state/ # vendored jito-programs-vote-state (anchor-lang 1.0.1)
├── references/
│ ├── jito_pfd_ref/ # clean baseline + Crucible fuzz fixture
│ │ ├── programs/priority-fee-distribution/ # ported program (== port above)
│ │ ├── programs/vote-state/ # vendored helper
│ │ └── fuzz/jito_transfer_priority_fee_total/ # fuzz fixture
│ └── jito_pfd_ref_planted_transfer_increments_total/
│ ├── programs/priority-fee-distribution/ # planted variant (1-line drop)
│ ├── programs/vote-state/
│ └── fuzz/jito_transfer_priority_fee_total/ # synced fixture (same code as clean)
├── .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
```
该不变性的 fuzz-fixture 源码仅存放在
`references/jito_pfd_ref/fuzz/jito_transfer_priority_fee_total/src/main.rs` 下;
CI 在运行前会将相同的源码复制到植入 bug 的变体中,因此
干净运行和植入运行之间唯一的区别是加载到 LiteSVM 中的
`.so` 二进制文件。
## 固定的工具链
这些是 CI 在每次 push 时构建所用的版本(参见
[`.github/workflows/ci.yml`](./.github/workflows/ci.yml))。版本锁定
继承自同类的 cf-invariants-jito / cf-invariants-jito-tippayment
项目的 CI-green 栈:
- Rust **stable**。
- `anchor-lang` **1.0.1** — 与 Crucible v0.2.0 的 workspace 匹配。
- 上游的 [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-sha256-hasher` **3** (用于替代上游
`solana_program::hash::hashv` 的模块化替代品——这是 anchor-lang 1.0.1 shim 不再重新导出的唯一 solana_program 子模块)。
- `solana-sdk-ids` **3** (传递性地用于 vote-state crate 的
`vote::id()` 检查)。
fuzz `Cargo.toml` 通过路径依赖引用 Crucible,路径为
`../../../../../crucible/...`,即 `port/` 的同级目录。
CI 会在 harness 步骤之前将 Crucible v0.2.0 克隆到该同级路径。要进行本地复现,请执行相同操作(或者在该路径下符号链接一个现有的
Crucible v0.2.0 检出)。
## 从全新克隆中复现
CI 在每次 push 时都会完全执行以下步骤。本地复现是
可选的,并且需要安装上述工具链并将其放在 `PATH` 中。
```
# 1. 克隆此 repo + Crucible v0.2.0 作为同级目录。
git clone https://github.com/caliperforge/cf-invariants-jito-priorityfee.git
git clone --depth 1 --branch v0.2.0 \
https://github.com/asymmetric-research/crucible.git
cd cf-invariants-jito-priorityfee
# 2. Workspace 检查(也在 CI 中作为 workspace-check job 运行)。
cargo check --workspace --locked || cargo check --workspace
# 3. 构建 cf-invariants-jito-priorityfee port (SBPF)。
cargo build-sbf --tools-version v1.52 \
--manifest-path programs/priority-fee-distribution/Cargo.toml
# 4. 构建 clean reference + planted twin。
for variant in jito_pfd_ref \
jito_pfd_ref_planted_transfer_increments_total; do
cargo build-sbf --tools-version v1.52 \
--manifest-path "references/${variant}/programs/priority-fee-distribution/Cargo.toml"
done
# 5. 从源码构建 + 安装 Crucible CLI。
(cd ../crucible && cargo install --path crates/crucible-fuzz-cli --locked)
# 6. 在 clean pair 上运行 harness(预期没有 FUZZ_FINDING 行)。
(cd references/jito_pfd_ref/fuzz/jito_transfer_priority_fee_total && \
crucible run jito_priority_fee_distribution \
invariant_transfer_priority_fee_tips_increments_total \
--release --timeout 30)
# 7. 针对 planted twin 运行相同的 invariant(预期在 ~1s 内出现一个 FUZZ_FINDING)。
(cd references/jito_pfd_ref_planted_transfer_increments_total/fuzz/jito_transfer_priority_fee_total && \
crucible run jito_priority_fee_distribution \
invariant_transfer_priority_fee_tips_increments_total \
--release --timeout 30)
```
CI 会在每次 push 时运行第 2 步到第 7 步。记录的 scorecard(原始
Crucible 输出,已去除 ANSI)作为
`crucible-scorecards` 工作流产物上传,并写入到运行器内部的
`findings/_/scorecard.md` 路径下。
`findings/` 被添加到了 gitignore;CI 产物是标准的记录。
有关标准的执行顺序,请参见 [`.github/workflows/ci.yml`](./.github/workflows/ci.yml)。
## 这不是什么
- **不是 Crucible 的一个分支。** Crucible 是 harness;
cf-invariants-jito-priorityfee 是一个在其之上运行的目标 + 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-priorityfee 由 Michael Moffett 在
CaliperForge 名下运营。CaliperForge 是一家独立运营的工程工作室。
此脚手架是在 AI 协助下构建的。由
CaliperForge 的运营者 Michael Moffett 编写和审查。完整政策见
[caliperforge.com/ai-disclosure](https://caliperforge.com/ai-disclosure)。
[caliperforge.com](https://caliperforge.com)
标签:Anchor, Solana, 不变量测试, 区块链安全, 可视化界面, 智能合约, 通知系统