mirror of
https://github.com/logos-blockchain/lez-fuzzing.git
synced 2026-06-14 06:59:29 +00:00
test: add genesis invariants target to catch 8 mutants
This commit is contained in:
parent
2974cd5e30
commit
cf24be46d9
1
.gitignore
vendored
1
.gitignore
vendored
@ -33,6 +33,7 @@ fuzz/coverage/
|
||||
mutants.out/
|
||||
mutants-harness.out/
|
||||
mutants-protocol.out/
|
||||
mutants-protocol.out.backup/
|
||||
|
||||
# ── Misc ──────────────────────────────────────────────────────────────────────
|
||||
# Performance baseline output from `just perf-baseline` or CI
|
||||
|
||||
BIN
corpus/libfuzz/fuzz_genesis_invariants/seed
generated
Normal file
BIN
corpus/libfuzz/fuzz_genesis_invariants/seed
generated
Normal file
Binary file not shown.
@ -126,3 +126,9 @@ name = "fuzz_merkle_tree"
|
||||
path = "fuzz_targets/fuzz_merkle_tree.rs"
|
||||
test = false
|
||||
bench = false
|
||||
|
||||
[[bin]]
|
||||
name = "fuzz_genesis_invariants"
|
||||
path = "fuzz_targets/fuzz_genesis_invariants.rs"
|
||||
test = false
|
||||
bench = false
|
||||
|
||||
137
fuzz/fuzz_targets/fuzz_genesis_invariants.rs
Normal file
137
fuzz/fuzz_targets/fuzz_genesis_invariants.rs
Normal file
@ -0,0 +1,137 @@
|
||||
#![cfg_attr(feature = "fuzzer-libfuzzer", no_main)]
|
||||
//! Fuzz target: genesis-state and system-account invariants.
|
||||
//!
|
||||
//! This target is **input-independent**: the fuzz input is always ignored.
|
||||
//! It asserts deterministic invariants about the genesis state produced by
|
||||
//! `V03State::new_with_genesis_accounts`, `system_faucet_account_id`,
|
||||
//! `system_bridge_account_id`, and `V03State::add_pinata_token_program`.
|
||||
//!
|
||||
//! # Covered mutations (from `lee/state_machine/src/state.rs`)
|
||||
//!
|
||||
//! | Line | Mutation | Assertion that catches it |
|
||||
//! |------|--------------------------------------------------------|-----------------------------------------------------|
|
||||
//! | 312 | `commitment_set_digest → Default::default()` | `[CommitmentSetDigestNonDefault]` |
|
||||
//! | 368 | delete `program_owner` from `add_pinata_token_program` | `[PinataTokenProgramOwner]` |
|
||||
//! | 370 | delete `data` from `add_pinata_token_program` | `[PinataTokenData]` |
|
||||
//! | 385 | `system_faucet_account → Default::default()` | `[FaucetBalance]` + `[FaucetProgramOwner]` |
|
||||
//! | 386 | delete `program_owner` from `system_faucet_account` | `[FaucetProgramOwner]` |
|
||||
//! | 387 | delete `balance` from `system_faucet_account` | `[FaucetBalance]` |
|
||||
//! | 393 | `system_bridge_account → Default::default()` | `[BridgeProgramOwner]` |
|
||||
//! | 394 | delete `program_owner` from `system_bridge_account` | `[BridgeProgramOwner]` |
|
||||
//! | 406 | `system_bridge_account_id → Default::default()` | `[BridgeIdNonDefault]` + `[SystemAccountIdDistinct]` |
|
||||
//!
|
||||
//! # Corpus note
|
||||
//!
|
||||
//! A single `\x00` seed file is sufficient — the input bytes are never read.
|
||||
//! The seed is required by `cargo fuzz run -runs=0` so that the replay phase
|
||||
//! has at least one execution to check against.
|
||||
|
||||
use nssa::{Account, AccountId, V03State, system_bridge_account_id, system_faucet_account_id};
|
||||
|
||||
fuzz_props::fuzz_entry!(|_data: &[u8]| {
|
||||
let default_account = Account::default();
|
||||
|
||||
// ── INVARIANT [BridgeIdNonDefault] ────────────────────────────────────────
|
||||
// `system_bridge_account_id()` must return a non-default `AccountId`.
|
||||
// Catches the mutation at state.rs:406 that replaces the function body with
|
||||
// `Default::default()`.
|
||||
let bridge_id = system_bridge_account_id();
|
||||
assert_ne!(
|
||||
bridge_id,
|
||||
AccountId::default(),
|
||||
"INVARIANT VIOLATION [BridgeIdNonDefault]: \
|
||||
system_bridge_account_id() must not return AccountId::default()",
|
||||
);
|
||||
|
||||
// The two system account IDs must also be distinct so that they occupy
|
||||
// separate entries in the public-state map.
|
||||
let faucet_id = system_faucet_account_id();
|
||||
assert_ne!(
|
||||
faucet_id,
|
||||
bridge_id,
|
||||
"INVARIANT VIOLATION [SystemAccountIdDistinct]: \
|
||||
system_faucet_account_id() and system_bridge_account_id() must differ",
|
||||
);
|
||||
|
||||
// Build the genesis state with no extra accounts.
|
||||
let state = V03State::new_with_genesis_accounts(&[], vec![], 0);
|
||||
|
||||
// ── INVARIANT [FaucetBalance] ─────────────────────────────────────────────
|
||||
// The system faucet account must hold `u128::MAX` tokens.
|
||||
// Catches state.rs:385 (whole account → Default) and
|
||||
// state.rs:387 (delete `balance` field from struct literal).
|
||||
let faucet = state.get_account_by_id(faucet_id);
|
||||
assert_eq!(
|
||||
faucet.balance,
|
||||
u128::MAX,
|
||||
"INVARIANT VIOLATION [FaucetBalance]: \
|
||||
system_faucet_account must have balance == u128::MAX, got {}",
|
||||
faucet.balance,
|
||||
);
|
||||
|
||||
// ── INVARIANT [FaucetProgramOwner] ────────────────────────────────────────
|
||||
// The system faucet account must have a non-default `program_owner`.
|
||||
// Catches state.rs:385 (whole account → Default) and
|
||||
// state.rs:386 (delete `program_owner` field from struct literal).
|
||||
assert_ne!(
|
||||
faucet.program_owner,
|
||||
default_account.program_owner,
|
||||
"INVARIANT VIOLATION [FaucetProgramOwner]: \
|
||||
system_faucet_account must have a non-default program_owner",
|
||||
);
|
||||
|
||||
// ── INVARIANT [BridgeProgramOwner] ───────────────────────────────────────
|
||||
// The system bridge account must have a non-default `program_owner`.
|
||||
// Catches state.rs:393 (whole account → Default) and
|
||||
// state.rs:394 (delete `program_owner` field from struct literal).
|
||||
let bridge = state.get_account_by_id(bridge_id);
|
||||
assert_ne!(
|
||||
bridge.program_owner,
|
||||
default_account.program_owner,
|
||||
"INVARIANT VIOLATION [BridgeProgramOwner]: \
|
||||
system_bridge_account must have a non-default program_owner",
|
||||
);
|
||||
|
||||
// ── INVARIANT [CommitmentSetDigestNonDefault] ─────────────────────────────
|
||||
// A freshly created empty state has an all-zero Merkle root, which equals
|
||||
// `CommitmentSetDigest::default()`. The genesis state inserts
|
||||
// `DUMMY_COMMITMENT` via SHA-256, producing a strictly different root.
|
||||
// Catches state.rs:312 that replaces `commitment_set_digest()` with
|
||||
// `Default::default()`.
|
||||
let empty_digest = V03State::new().commitment_set_digest();
|
||||
let genesis_digest = state.commitment_set_digest();
|
||||
assert_ne!(
|
||||
genesis_digest,
|
||||
empty_digest,
|
||||
"INVARIANT VIOLATION [CommitmentSetDigestNonDefault]: \
|
||||
commitment_set_digest of genesis state must differ from the empty state's \
|
||||
all-zero root",
|
||||
);
|
||||
|
||||
// ── INVARIANT [PinataTokenProgramOwner] ──────────────────────────────────
|
||||
// An account created by `add_pinata_token_program` must have a non-default
|
||||
// `program_owner` field.
|
||||
// Catches state.rs:368 (delete `program_owner` from the struct literal).
|
||||
//
|
||||
// ── INVARIANT [PinataTokenData] ──────────────────────────────────────────
|
||||
// An account created by `add_pinata_token_program` must have non-default
|
||||
// `data` (specifically `vec![3; 33]` encoded as `Data`).
|
||||
// Catches state.rs:370 (delete `data` from the struct literal).
|
||||
let pt_id = AccountId::new([0xABu8; 32]);
|
||||
let mut pinata_state = V03State::new_with_genesis_accounts(&[], vec![], 0);
|
||||
pinata_state.add_pinata_token_program(pt_id);
|
||||
let pt = pinata_state.get_account_by_id(pt_id);
|
||||
|
||||
assert_ne!(
|
||||
pt.program_owner,
|
||||
default_account.program_owner,
|
||||
"INVARIANT VIOLATION [PinataTokenProgramOwner]: \
|
||||
add_pinata_token_program must set a non-default program_owner on the account",
|
||||
);
|
||||
assert_ne!(
|
||||
pt.data,
|
||||
default_account.data,
|
||||
"INVARIANT VIOLATION [PinataTokenData]: \
|
||||
add_pinata_token_program must set non-default data on the account",
|
||||
);
|
||||
});
|
||||
@ -37,6 +37,7 @@ targets=(
|
||||
fuzz_multi_block_state_sequence
|
||||
fuzz_sequencer_vs_replayer
|
||||
fuzz_merkle_tree
|
||||
fuzz_genesis_invariants
|
||||
)
|
||||
|
||||
# cargo-fuzz requires the nightly toolchain (-Zsanitizer=address etc.).
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user