Cyber-Castellum/DWARF

GitHub: Cyber-Castellum/DWARF

DWARF 是针对 Cardano 节点实现的模糊测试与对抗性测试框架,覆盖 CBOR 解码、ledger 规则、mini-protocol 和 consensus 层面,并桥接 Antithesis 确定性模拟平台实现大规模并行变异探索。

Stars: 0 | Forks: 0

# DWARF DWARF 是一个用于 Cardano 节点实现的模糊测试与对抗性测试框架 (Haskell `cardano-node` 和 Rust `amaru`)。它利用结构性畸形和对抗性输入, 测试节点的序列化/反序列化、**ledger-rule**、mini-protocol、runtime、resource 和 consensus 层面,捕获结构化证据,并**将其模糊测试[桥接到 Antithesis](https://antithesis.com) 确定性模拟平台**,使得真实节点能够在数千条探索时间线中处理变异的 payload。 它通过同一套定义在两处运行: 1. **本地框架** —— 一个场景驱动的模糊测试/测试运行器 (Python `profile_manager` + `cardano-profile` CLI + Web dashboard),能够启动容器化的 Cardano devnet (Haskell `cardano-node`、Rust `amaru` 或**混合**部署),运行涵盖 CBOR、ledger-rule、mini-protocol、runtime、resource 和 consensus 系列的各种场景, 并捕获结构化、可重放的证据。它还驱动**原生覆盖率引导的模糊测试** —— 由基于真实 edge coverage 的 AFL++ 驱动,针对经过 SanitizerCoverage 插桩的 `cardano-node`(decode + 完整的 Conway ledger 规则,包含 `applyBlock`)。 2. **Antithesis 桥接器** —— 一个生成器,将模糊测试场景转化为自包含的 Antithesis 测试包,外加一个 Haskell **`dwarf-adversary`**,它作为 node-to-node (N2N) peer 加入活跃的 testnet,并向被测节点提供结构性变异的 CBOR,以及一个进程内的 **`dwarf-decoder-fuzz`** 负载(相同的 `applyBlock` 层,在 Antithesis 下运行)。Profile 对实现、版本、网络、拓扑和 peer-sharing 进行参数化。 ## 它的功能 ### 本地模糊测试 本地目录(`dwarf/scenarios/`,223 个 YAML 场景)涵盖以下系列: | 系列 | 测试内容 | |---|---| | **CBOR 结构性模糊** | 针对结构性变异 CBOR 的解码器健壮性 —— block-header、block、tx-body、certificate、auxiliary-data —— 针对 `cardano-node` 和 `amaru` (`*-cbor-*`, `edge-cases-cbor-*`)。 | | **原生覆盖率引导模糊** | 基于边缘引导的 AFL++,运行在**经过 SanitizerCoverage 插桩的 `cardano-node`** 依赖树上(GHC `-fllvm` + LLVM SanCov),封装在跨平台 Docker 镜像中。单一 harness,通过 `DWARF_DECODER` 选择层面:`tx / block / header / txbody / ledger / applytx / applyblock` 解码 + ledger 表面,以及 `handshake / txsub / keepalive` mini-protocol 编解码器。参见 [`COVERAGE-HARNESS.md`](antithesis/components/dwarf-adversary/COVERAGE-HARNESS.md)。 | | **Ledger-rule 模糊** | 解码 + **运行真实的 Conway ledger 规则**:`applytx`(mempool `applyTx` STS)和 `applyblock`(在 genesis 初始化的 `NewEpochState` 上执行完整的 `BBODY → LEDGERS → per-tx LEDGER`)。这是最深层的表面 —— 它们触及 `ConwayUtxow/Utxo/Certs` 验证,而不仅仅是解码。 | | **Mini-protocol 模糊** | N2N 协议语法 / 时序 / 状态机 —— 专用的 `cardano-node-mini-protocol-*-fuzz` 场景,涵盖 handshake、chain-sync、block-fetch、tx-submission、keep-alive、peer-sharing,以及错误版本 / 畸形 handshake 门控。 | | **对抗性拓扑 / consensus** | Eclipse、Sybil、拜占庭 block-fetch、fork-switch、时代 / 硬分叉边界。 | | **Runtime / 网络故障** | 分区-重组、重启 / tip 恢复、冻结 / 恢复、keep-alive 失败级联、slow-loris、时间偏移。 | | **资源压力** | 宿主机 cpu / 磁盘 / 内存 / 带宽耗尽以及同步期间的磁盘空间填满 (`resource-*`)。 | | **Mempool / tx 压力** | 批处理 / 窗口压力、mempool-relay 压力、local-tx-monitor 故障。 | | **快照 / 恢复** | 快照损坏 / 恢复、多日暂停-恢复、确定性检查点。 | | **差异性测试** | 针对相同输入的 `amaru` ↔ `cardano-node` 验证路径一致性 (`replay-and-diff`)。 | | **取证 / 证据** | pcap / syscall / gc 捕获、bundle attestation、chain-verify、SARIF 导出、凭证检查。 | | **Runtime 底层 / 分阶段** | 大量 `runtime-substrate-*` 和 `phase*` —— runtime profile、能力演示,以及上述系列所依赖的生成的多节点基线。 | CBOR 模糊测试使用两种引擎,可根据场景通过 `load` 原语进行选择: `cbor_fuzz` / `cbor_fuzz_target`(语义、结构感知变异)和 `cbor_fuzz_structured`(字节级结构变异),以及用于精选边界情况的 `cbor_edge_cases`。每次运行都会在 `dwarf/runs/`(可通过 dashboard 检查)和 `dwarf/evidence/` 下生成一个清单、断言摘要、NDJSON 日志和探针输出。 ### 原生覆盖率引导模糊测试 除了生成式 CBOR 模糊测试外,DWARF 还针对经过原生插桩的 `cardano-node` 运行 **edge-coverage-guided AFL++**。整个依赖树使用 GHC `-fllvm` + LLVM **SanitizerCoverage** pass 进行编译,因此 AFL 可以根据真实的 edge coverage 来引导变异 —— 封装为跨平台 Docker 镜像(`dwarf-haskell-cov`)。单个 harness (`dwarf-decode-any`) 通过 `DWARF_DECODER` 选择测试表面,从纯解码(`tx`、`block`、`header`)到 Conway ledger 规则(`applytx`、`applyblock`)。 这些相同的测试表面被接入为 DWARF 场景(`dwarf scenario run cardano-node-cov--aflpp-smoke`,由 `aflpp_smoke_exit_clean` 断言)并作为一个双后端定义:相同的 `applyblock` 表面也可以通过 `dwarf-decoder-fuzz --target applyblock` **在 Antithesis 下进行进程内运行**。 `applyblock` 表面在每个进程中从 genesis 构建一次初始的 Conway `NewEpochState`,并通过完整的区块应用 STS 应用解码后的 tx —— 从而触及真实的单笔 tx ledger 验证(`ValueNotConservedUTxO`、`BadInputsUTxO`、 `StakeKeyNotRegisteredDELEG` 等),这是框架中最深层的模糊测试表面。活动证据(SARIF + 各表面指标 + 报告)位于 `reports/` 下;原始模糊器日志位于 `raw/logs/` 下。 ### Profile、devnet 和 target DWARF 不假设有固定的网络 —— 它从 *profile* **启动所需的 devnet**。Profile 参数化内容包括: - **实现** —— Haskell `cardano-node`、Rust `amaru` 或并行运行两者的**混合** devnet(`node_type: haskell | amaru | mixed`,带有独立的 `haskell_count` / `amaru_count`)。 - **网络 / 版本** —— 完全本地的 devnet (network-magic 42) 或通过上游 peer 地址连接到公共网络 —— **preview、preview2、preprod** —— 从而使得相同的场景可以针对真实的网络区块形状和时代边界运行。 - **拓扑与 consensus 旋钮** —— `topology_pattern`(例如 `local-mesh`),`shared_genesis`,`peer_sharing` 开/关。 该框架附带 **12 个现成的 profile** 以及一个用于生成更多 profile 的 **模板系统**: | Profile | 结构 | |---|---| | a / b | Haskell,禁用 / 启用 peer-sharing | | c | 混合:1 个 Haskell + 1 个 Amaru(最小化) | | h | 生成的混合配置:2 个 Haskell + 1 个 Amaru (local-mesh,共享 genesis) | | i | 生成的 Haskell(3 个节点) | | d / f / e / g | Amaru / Haskell preview & preview2 proofs | | j / k | Haskell / Amaru preprod proofs | | l | Amaru 封闭 devnet | **本地 devnet 后端** 将 profile 转换为 `docker-compose.yml`,并在任何 Docker 宿主机上启动 devnet;单独的部署路径在远程 runtime root 上运行它。因为两种实现和混合 devnet 都是一等公民,DWARF 还支持**差异性测试** —— 向 `amaru` 和 `cardano-node` 提供相同的对抗性输入,并断言它们的验证路径一致 (`replay-and-diff`)。 ### CLI 与 dashboard 一切操作都通过 `cardano-profile` CLI 运行 —— 这是一个广泛的功能表面,包括 `profile` / `list-profiles`、`scenario` / `run`(带 `--backend local-devnet` 或 `antithesis`)、`fuzz` / `campaign`、`replay` / `replay-and-diff` / `reproduce` / `minimize`、`coverage` / `compare` / `stats`、`snapshot` / `evidence` / `export` (SARIF)、`deploy` / `status` / `doctor`,以及 `antithesis` / `moog` 桥接命令。相同的代码路径支撑着一个 Web **dashboard**(即 "Operate" 视图:运行、配置、场景、覆盖率趋势、崩溃分诊、运行对比/字段差异、时间线)。参见 `INSTALL.md` 和 `OPERATIONS.md`。 ### Antithesis 集成 **Antithesis 目前仅支持 `cardano-node`。** CBOR 场景生成器(`profile_manager/antithesis_generator.py`)是受支持的桥接器,并且它对 cardano-node 进行了硬性限制(`SUPPORTED_IMPLEMENTATIONS = {"cardano-node"}`;它严格要求一个 `cbor_fuzz` 加载原语,否则将引发异常)。它将 CBOR 解码场景转换为可部署的包,将每个解码目标映射到 adversary 协议 + CBOR 形状: | 解码目标 | N2N 协议 | CBOR 形状 | 已构建 | |---|---|---|---| | block-header | chain-sync (#2) | `block-header` | ✅ | | block | block-fetch (#3) | `block` | ✅ | | tx-body | tx-submission2 (#4) | `tx-body` | ✅ | | certificate | tx-submission2 (#4) | `certificate` | ✅ | | auxiliary-data | tx-submission2 (#4) | `auxiliary-data` | ✅ | `render_bundle()` 输出一个自包含的包:完整的 Antithesis 测试 harness(setup `sidecar` + composer `adversary` 驱动程序)、testnet(producers、relays、tracer、tx-generator),以及为目标协议/形状配置好接线的 `dwarf-adversary`。 对于 block-fetch,它还会应用 **topology eclipse** (`_apply_eclipse`),使得被测节点仅从 adversary 处获取区块。 ### `dwarf-adversary` 一个 Haskell N2N peer(`antithesis/components/dwarf-adversary/`,镜像 `ghcr.io/j-gainsec/dwarf-adversary:0.19.0`),它能处理真实的 Ouroboros N2N mini-protocol —— chain-sync (#2)、block-fetch (#3)、tx-submission2 (#4)、keep-alive (#8) —— 并向被测节点提供**结构性变异的 CBOR**。它通过代理上游 producer 或提供内置语料库来引导有效链,达到 GSM `CaughtUp` 状态,然后通过变异编解码器对目标解码器进行模糊测试。 **按时间线播种(穷举模糊测试)。** 变异生成器是 `mkStdGen(seed XOR fnv1a64(payloadBytes))`,因此不同的 payload 会发生不同的变异。基础 seed 来自于 `--seed`,其默认值为 **`random`**:adversary 在启动时从 `/dev/urandom` 提取一个新的 `Word64` —— 并且由于 Antithesis 将熵作为按时间线的选择点进行拦截,**每条探索到的时间线都会从不同的 seed 开始模糊测试**,因此探索的是变异 seed 空间,而不是固定的。提取的值会被记录(`reproduce with --seed 0x…`),显式传入 `--seed 0x` 可 RNG 以进行确定性重现。 关键标志:`--protocol {chainsync|blockfetch|txsubmission}`、`--cbor-shape {block-header|block|tx-body|certificate|auxiliary-data}`、 `--mutation-rate`、`--upstream HOST:PORT`、`--seed {random|0x|}`、`--network-magic`、 `--listen-port`、`--baked-chain FILE`(提供嵌入式链,无上游)、 `--capture-to FILE`(序列化捕获的链)、`--selftest`。 ### 容器镜像 DWARF 的几个部分需要预构建的容器镜像: | 镜像 | 位置 | 使用者 | |---|---|---| | `ghcr.io/j-gainsec/dwarf-adversary:` | **GitHub Container Registry (公开)** —— 当前版本 `:0.19.0` | N2N `dwarf-adversary` **以及**进程内 `dwarf-decoder-fuzz` harness(两个二进制文件均内置)。由 Antithesis `cardano_node_dwarf` compose 包拉取。通过 `antithesis/components/dwarf-adversary/build-image.sh` 构建/推送。 | | `ghcr.io/j-gainsec/dwarf-haskell-cov:` | **GitHub Container Registry (公开)** —— 与 adversary 相同的 `j-gainsec` registry(例如 `:0.1-clean`)。通过 `antithesis/components/dwarf-adversary/coverage-docker/build.sh` 构建/推送;根据 `coverage-docker/` 中的 Dockerfile 实现跨平台、可重现构建。 | 原生 SanCov 覆盖率引导的 AFL++ harness (`dwarf-cov-run `)。 | | `ghcr.io/cardano-foundation/cardano-node-antithesis/*` (tracer-sidecar, tx-generator, sidecar, …), `ghcr.io/pragma-org/amaru/loader` | **公共上游仓库**,在 compose 文件中通过摘要固定 | Antithesis testnet 底层结构(producers、relays、tracer、tx-generator)。 | 该框架自身的 devnet node/amaru 镜像通过 `infrastructure/docker/` 在本地构建 (参见 `delivery/scripts/build-image.sh`)。拉取/推送到 `ghcr.io/j-gainsec/*` 需要具有适当作用域的 GHCR 凭证。 ### 启动实时运行 实时 Antithesis 活动通过 **Moog** 启动,这是一个链上请求者流程 (`profile_manager/moog.py`,显示为 `cardano-profile moog …`)。它处理请求者注册、链上 token / MPFS 交互、Cardano Foundation oracle 交接,以及针对 GitHub repo + commit + bundle 目录的 `create-test-plan`(免费试运行)/ `create-test --approve`(计费启动)。结果从 Antithesis 租户读回(分诊报告 / SDK 断言)。机密信息(PAT、钱包、租户凭证)存储在 repo 之外,永远不会被提交。 ## 已确认状态 **已在 Antithesis 上确认上线**(租户 `amaru-cardano`,`--no-faults`,运行 1 小时): | CBOR 形状 | 路径 | 实时断言 | 状态 | |---|---|---|---| | block-header | chain-sync | `dwarf_served_mutated_header` (接收到即解码) | ✅ 上线 (SP2) | | **tx-body** | tx-submission | `dwarf_served_mutated_tx` | ✅ **通过** 2026-06-13 (运行 `0e1c9877…`,完成于 1 小时 12 分钟) | | **block** | block-fetch | `dwarf_served_mutated_block` | ✅ **通过** 2026-06-13 (运行 `ea5ad7d0…`,完成于 1 小时 13 分钟) | | certificate | tx-submission | `dwarf_served_mutated_tx` (在 tx 内部提供) | 已构建;尚未进行实时运行 | | auxiliary-data | tx-submission | `dwarf_served_mutated_tx` (在 tx 内部提供) | 已构建;尚未进行实时运行 | 两种硬服务路径形状(tx-body、block)现已在 Antithesis 上得到验证:真实的 `cardano-node` 连接到 `dwarf-adversary`,拉取结构性变异的 CBOR,并 在其上运行解码器 —— adversary 保持稳定(无崩溃)并且运行完成。 **本地门控**(在构建宿主机 testnet 上运行,全部通过): - `tools/sp3a_topology_eclipse_repro.sh` —— 单网络拓扑下的 block-fetch 遮蔽:`dwarf_served_mutated_block=69`, VRFKeyBadProof 0, RestartCount 0。 - `tools/sp3a_eclipse_repro.sh` / `tools/sp3a_baked_repro.sh` —— 自定义网络 / 内置语料库遮蔽下的 block-fetch(仅限本地功能)。 - `tools/sp3_caughtup_repro.sh` —— 前进的 CaughtUp peer 底层。 针对 block-fetch 的实时遮蔽在完整的 harness 包内部**仅使用单一默认网络上的拓扑**(没有自定义 docker 网络)—— dwarf-adversary 不提供 peer-sharing gossip,因此被测节点只能到达 adversary。 自定义网络和无 producer 的内置包作为本地功能保留(它们缺乏 Antithesis 测试 harness,不得进行实时运行)。 **原生覆盖率引导的模糊测试**(`dwarf-haskell-cov`,原生 GHC SanCov): - 所有测试表面在容器内运行正常,edge coverage > 0,100% 稳定性,0 次崩溃。 - `applyblock` 表面已被证明能触及真实的单笔 tx Conway ledger 规则 (`ConwayUtxow/Utxo/Certs`),并且在整个 DWARF 框架中运行通过(`dwarf scenario run cardano-node-cov-applyblock` → 通过,约 20.7k 条 edges,0 次崩溃),并在 Antithesis 下进行进程内运行 (`dwarf-decoder-fuzz --target applyblock`)。 - 一场涵盖所有 9 个表面的 **8 小时穷举活动**运行了 **约 2050 万次执行,0 次崩溃**(`applyblock` 以约 28k 条 edges 领先覆盖率)。结果、SARIF 以及各表面指标位于 [`reports/`](reports/) 下。 ## 布局 ``` DWARF/ ├── README.md INSTALL.md OPERATIONS.md RELEASE-NOTES.md TEST-OUTPUTS.md ├── antithesis/ │ ├── components/dwarf-adversary/ # Haskell N2N adversary (cabal) │ ├── cardano_node_dwarf/ # full-harness CBOR bundle (live-proven) │ ├── cardano_node_dwarf_eclipse/ # custom-network eclipse (local-only) │ ├── cardano_node_dwarf_baked/ # baked-corpus eclipse (local-only) │ ├── amaru-single/ # early scaffolding (amaru on Antithesis NOT supported) │ └── mixed-haskell-amaru/ # early scaffolding (unvalidated, never run live) ├── dwarf/ │ ├── cardano-profile # CLI entrypoint │ ├── profile_manager/ # framework + antithesis.py + antithesis_generator.py + moog.py │ ├── scenarios/ # 223 scenario YAMLs (~8 families) │ ├── primitives/ # primitive registry + schemas │ ├── profiles/ # 12 profiles + templates/ │ ├── runs/ bundles/ evidence/ # run artifacts + evidence │ ├── spec/ # SARIF + spec schemas │ └── docs/ ├── delivery/ # Docker delivery wrapper (framework image) ├── infrastructure/docker/ ├── tools/ # local repro/validation gates ├── tests/ # framework + integration tests └── docs/ # design specs + implementation plans ``` ## 构建与运行 **本地框架 / dashboard**(任何带有 Compose v2 的 Docker 宿主机): ``` delivery/scripts/install.sh delivery/scripts/build-image.sh delivery/scripts/deploy.sh delivery/scripts/status.sh ``` **`dwarf-adversary`**(构建于 GHC 9.6.x 宿主机): ``` cd antithesis/components/dwarf-adversary cabal build -w ghc-9.6.7 exe:dwarf-adversary ./build-image.sh ghcr.io//dwarf-adversary: ``` **原生覆盖率引导的 harness**(`dwarf-haskell-cov`,GHC 9.6.x + LLVM-15): ``` cd antithesis/components/dwarf-adversary/coverage-docker ./build.sh ghcr.io//dwarf-haskell-cov: # 运行一个 surface (edge-guided AFL++) 持续 N 秒: docker run --rm -v "$PWD/out:/out" ghcr.io//dwarf-haskell-cov: applyblock 60 ``` **生成 Antithesis 包**,基于 CBOR 解码场景,通过 `cardano-profile` CLI: ``` dwarf/cardano-profile antithesis build \ --scenario dwarf/scenarios/cardano-node-cbor-tx-body-fuzz.yaml \ --registry ghcr.io/ --tag 0.10.0 --out antithesis/cardano_node_dwarf ``` (生成器位于 `dwarf/profile_manager/antithesis_generator.py`;实时活动通过 Moog 请求者流程从包中启动。) 使用 `delivery/tests/test_delivery_contract.sh` 验证包布局。 ## 路线图 CBOR 系列已完全桥接到 Antithesis(header + tx-body + block 已验证实时运行;certificate + auxiliary-data 已构建并等待实时运行),并且 **原生覆盖率引导的模糊测试**现在涵盖了解码和完整的 Conway ledger-rule 表面 (`applytx`, `applyblock`),其中 `applyblock` 也在 Antithesis 下进行进程内运行。 最高杠杆的后续步骤是**共识级别的 header 验证** (Praos/VRF/KES,不同于 ledger 的 BBODY 规则)、针对实时节点的 **SP4 mini-protocol 语法/状态机模糊测试**(adversary 已经能处理每个 N2N 协议),以及**差异性** `amaru` ↔ `cardano-node` 一致性验证。Runtime/网络故障和快照场景在很大程度上与 Antithesis 的原生故障注入器重复,最好将其表示为故障配置;resource-pressure 和取证场景保留在本地。有关规范、功能映射图和活动证据,请参见 `docs/` 和 `reports/`。
标签:Cardano, 区块链, 可视化界面, 安全测试, 攻击性安全, 测试框架, 请求拦截, 逆向工具