mirror of
https://github.com/logos-blockchain/lez-fuzzing.git
synced 2026-06-07 03:29:26 +00:00
chore: add linting formatting
- align workflows
This commit is contained in:
parent
3294923b87
commit
2320beb110
14
.github/workflows/fuzz-afl.yml
vendored
14
.github/workflows/fuzz-afl.yml
vendored
@ -2,18 +2,18 @@ name: AFL++ Fuzzing
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 2 * * *" # nightly at 02:00 UTC
|
||||
workflow_dispatch: # manual trigger
|
||||
- cron: "0 2 * * *"
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- feat-add-afl-fuzzing
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
RISC0_DEV_MODE: "1"
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
# afl-smoke — 120-second campaign for all 15 targets
|
||||
# afl-smoke — 60-second per targets
|
||||
# ────────────────────────────────────────────────────────────────────────────
|
||||
afl-smoke:
|
||||
name: "AFL++ smoke — ${{ matrix.target }}"
|
||||
@ -108,7 +108,7 @@ jobs:
|
||||
fi
|
||||
echo "Seed inputs: $(ls "$SEEDS" | wc -l)"
|
||||
|
||||
- name: Run AFL++ for 120 seconds
|
||||
- name: Run AFL++ for 60 seconds
|
||||
env:
|
||||
AFL_SKIP_CPUFREQ: "1"
|
||||
AFL_I_DONT_CARE_ABOUT_MISSING_CRASHES: "1"
|
||||
@ -118,7 +118,7 @@ jobs:
|
||||
# Disable errexit so that timeout's exit code 124 (expected signal) does not
|
||||
# cause bash -e to abort the script before the guard below can run.
|
||||
set +e
|
||||
timeout 120 \
|
||||
timeout 60 \
|
||||
afl-fuzz \
|
||||
-i afl-seeds/${TARGET} \
|
||||
-o afl-output/${TARGET} \
|
||||
|
||||
7
.github/workflows/fuzz.yml
vendored
7
.github/workflows/fuzz.yml
vendored
@ -1,12 +1,11 @@
|
||||
name: Fuzzing
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main, develop, feat-add-afl-fuzzing]
|
||||
pull_request:
|
||||
schedule:
|
||||
# Nightly full run
|
||||
- cron: "0 2 * * *"
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
env:
|
||||
RISC0_DEV_MODE: "1"
|
||||
|
||||
78
.github/workflows/lint.yml
vendored
Normal file
78
.github/workflows/lint.yml
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
name: Lint
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "!.github/workflows/*.yml"
|
||||
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- "**.md"
|
||||
- "!.github/workflows/*.yml"
|
||||
|
||||
env:
|
||||
RISC0_DEV_MODE: "1"
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
|
||||
jobs:
|
||||
# ── rustfmt ──────────────────────────────────────────────────────────────────
|
||||
fmt-rs:
|
||||
name: Rust formatting
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
|
||||
|
||||
- name: Install nightly toolchain for rustfmt
|
||||
run: rustup install nightly --profile minimal --component rustfmt
|
||||
|
||||
- name: Check Rust files are formatted
|
||||
run: cargo +nightly fmt --check
|
||||
|
||||
# ── clippy ───────────────────────────────────────────────────────────────────
|
||||
lint:
|
||||
name: Clippy
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha || github.head_ref }}
|
||||
|
||||
- name: Checkout logos-execution-zone alongside lez-fuzzing
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: logos-blockchain/logos-execution-zone
|
||||
path: logos-execution-zone
|
||||
|
||||
- name: Symlink logos-execution-zone to sibling directory
|
||||
run: ln -s "$GITHUB_WORKSPACE/logos-execution-zone" "$GITHUB_WORKSPACE/../logos-execution-zone"
|
||||
|
||||
- name: Install logos-blockchain-circuits
|
||||
uses: ./logos-execution-zone/.github/actions/install-logos-blockchain-circuits
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install stable toolchain with clippy
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: clippy
|
||||
|
||||
- name: Restore Rust cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
shared-key: lint-rust-cache
|
||||
save-if: ${{ github.ref == 'refs/heads/main' }}
|
||||
|
||||
- name: Lint workspace
|
||||
env:
|
||||
RISC0_DEV_MODE: "1"
|
||||
run: cargo clippy --workspace --all-targets --all-features -- -D warnings
|
||||
@ -118,13 +118,10 @@ impl<'a> Arbitrary<'a> for ArbPublicKey {
|
||||
// rejection path in `is_valid_for` independently.
|
||||
let bytes = <[u8; 32]>::arbitrary(u)?;
|
||||
let pk = PublicKey::try_new(bytes).unwrap_or_else(|_| {
|
||||
PublicKey::new_from_private_key(
|
||||
&ArbPrivateKey::arbitrary(u)
|
||||
.map(|w| w.0)
|
||||
.unwrap_or_else(|_| {
|
||||
PrivateKey::try_new([1_u8; 32]).expect("known-good seed")
|
||||
}),
|
||||
)
|
||||
PublicKey::new_from_private_key(&ArbPrivateKey::arbitrary(u).map_or_else(
|
||||
|_| PrivateKey::try_new([1_u8; 32]).expect("known-good seed"),
|
||||
|w| w.0,
|
||||
))
|
||||
});
|
||||
Ok(Self(pk))
|
||||
}
|
||||
@ -145,11 +142,11 @@ impl<'a> Arbitrary<'a> for ArbPubTxMessage {
|
||||
let program_id: [u32; 8] = <[u32; 8]>::arbitrary(u)?;
|
||||
// Generate 0–7 accounts; nonces vector is given the same length.
|
||||
let len = (u8::arbitrary(u)? as usize) % 8;
|
||||
let account_ids = (0..len)
|
||||
.map(|_| ArbAccountId::arbitrary(u).map(|a| a.0))
|
||||
let account_ids = std::iter::repeat_with(|| ArbAccountId::arbitrary(u).map(|a| a.0))
|
||||
.take(len)
|
||||
.collect::<ArbResult<Vec<_>>>()?;
|
||||
let nonces = (0..len)
|
||||
.map(|_| ArbNonce::arbitrary(u).map(|n| n.0))
|
||||
let nonces = std::iter::repeat_with(|| ArbNonce::arbitrary(u).map(|n| n.0))
|
||||
.take(len)
|
||||
.collect::<ArbResult<Vec<_>>>()?;
|
||||
let instruction_data: Vec<u32> = Vec::<u32>::arbitrary(u)?;
|
||||
Ok(Self(Message::new_preserialized(
|
||||
@ -174,9 +171,11 @@ impl<'a> Arbitrary<'a> for ArbWitnessSet {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> ArbResult<Self> {
|
||||
// 0–3 (signature, public_key) pairs
|
||||
let n = (u8::arbitrary(u)? as usize) % 4;
|
||||
let pairs = (0..n)
|
||||
.map(|_| Ok((ArbSignature::arbitrary(u)?.0, ArbPublicKey::arbitrary(u)?.0)))
|
||||
.collect::<ArbResult<Vec<_>>>()?;
|
||||
let pairs = std::iter::repeat_with(|| {
|
||||
Ok((ArbSignature::arbitrary(u)?.0, ArbPublicKey::arbitrary(u)?.0))
|
||||
})
|
||||
.take(n)
|
||||
.collect::<ArbResult<Vec<_>>>()?;
|
||||
Ok(Self(WitnessSet::from_raw_parts(pairs)))
|
||||
}
|
||||
}
|
||||
@ -247,8 +246,8 @@ impl<'a> Arbitrary<'a> for ArbHashableBlockData {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> ArbResult<Self> {
|
||||
// 0–7 transactions per block
|
||||
let n = (u8::arbitrary(u)? as usize) % 8;
|
||||
let transactions = (0..n)
|
||||
.map(|_| ArbNSSATransaction::arbitrary(u).map(|t| t.0))
|
||||
let transactions = std::iter::repeat_with(|| ArbNSSATransaction::arbitrary(u).map(|t| t.0))
|
||||
.take(n)
|
||||
.collect::<ArbResult<Vec<_>>>()?;
|
||||
Ok(Self(HashableBlockData {
|
||||
block_id: u64::arbitrary(u)?,
|
||||
|
||||
@ -11,6 +11,7 @@ use testnet_initial_state::initial_pub_accounts_private_keys;
|
||||
/// Extract the [`AccountId`]s of all signers from a transaction's
|
||||
/// witness set. Used by fuzz targets that need to verify nonce
|
||||
/// increments after `execute_check_on_state`.
|
||||
#[must_use]
|
||||
pub fn signer_account_ids(tx: &common::transaction::NSSATransaction) -> Vec<nssa::AccountId> {
|
||||
use common::transaction::NSSATransaction;
|
||||
match tx {
|
||||
@ -52,15 +53,15 @@ pub struct FuzzAccount {
|
||||
/// has a shape controlled by the fuzzer rather than fixed at compile time.
|
||||
pub fn arbitrary_fuzz_state(u: &mut Unstructured<'_>) -> arbitrary::Result<Vec<FuzzAccount>> {
|
||||
let n = ((u8::arbitrary(u)? as usize) % 8) + 1; // 1..=8
|
||||
(0..n)
|
||||
.map(|_| {
|
||||
Ok(FuzzAccount {
|
||||
account_id: ArbAccountId::arbitrary(u)?.0,
|
||||
balance: u128::arbitrary(u)?,
|
||||
private_key: ArbPrivateKey::arbitrary(u)?.0,
|
||||
})
|
||||
std::iter::repeat_with(|| {
|
||||
Ok(FuzzAccount {
|
||||
account_id: ArbAccountId::arbitrary(u)?.0,
|
||||
balance: u128::arbitrary(u)?,
|
||||
private_key: ArbPrivateKey::arbitrary(u)?.0,
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.take(n)
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Generate a native-transfer [`NSSATransaction`] between two accounts chosen
|
||||
@ -115,8 +116,8 @@ prop_compose! {
|
||||
)(
|
||||
from_idx in 0..accounts.len(),
|
||||
to_idx in 0..accounts.len(),
|
||||
nonce in 0u128..1_000u128,
|
||||
amount in 0u128..10_000u128,
|
||||
nonce in 0_u128..1_000_u128,
|
||||
amount in 0_u128..10_000_u128,
|
||||
) -> NSSATransaction {
|
||||
let (from_id, from_key) = &accounts[from_idx];
|
||||
let (to_id, _) = &accounts[to_idx];
|
||||
@ -127,6 +128,7 @@ prop_compose! {
|
||||
}
|
||||
|
||||
/// Return the test accounts from `testnet_initial_state` as `(AccountId, PrivateKey)` pairs.
|
||||
#[must_use]
|
||||
pub fn test_accounts() -> Vec<(AccountId, PrivateKey)> {
|
||||
initial_pub_accounts_private_keys()
|
||||
.into_iter()
|
||||
@ -168,9 +170,9 @@ prop_compose! {
|
||||
/// the state is left unchanged on rejection (StateIsolationOnFailure).
|
||||
pub fn arb_invalid_account_state_tx()(
|
||||
// Use a random 32-byte seed as a "phantom" account id not in genesis
|
||||
phantom_id_bytes in proptest::array::uniform32(0u8..),
|
||||
phantom_id_bytes in proptest::array::uniform32(0_u8..),
|
||||
amount in (u128::MAX / 2)..u128::MAX, // overflow-inducing amount
|
||||
nonce in 0u128..10u128,
|
||||
nonce in 0_u128..10_u128,
|
||||
) -> NSSATransaction {
|
||||
let phantom_id = nssa::AccountId::new(phantom_id_bytes);
|
||||
// Attempt to sign with a key that has no matching on-chain account
|
||||
@ -216,14 +218,14 @@ pub fn arb_duplicate_tx_sequence() -> impl Strategy<Value = Vec<NSSATransaction>
|
||||
pub fn arb_pathological_sequence() -> impl Strategy<Value = Vec<NSSATransaction>> {
|
||||
let accounts = test_accounts();
|
||||
let n = accounts.len();
|
||||
proptest::collection::vec((0..n, 0..n, 0u128..5u128, any::<bool>()), 1..8_usize).prop_map(
|
||||
proptest::collection::vec((0..n, 0..n, 0_u128..5_u128, any::<bool>()), 1..8_usize).prop_map(
|
||||
move |params| {
|
||||
params
|
||||
.into_iter()
|
||||
.map(|(from_idx, to_idx, nonce, zero_amount)| {
|
||||
let (from_id, from_key) = &accounts[from_idx];
|
||||
let (to_id, _) = &accounts[to_idx];
|
||||
let amount = if zero_amount { 0u128 } else { u128::MAX }; // 0 or overflow
|
||||
let amount = if zero_amount { 0_u128 } else { u128::MAX }; // 0 or overflow
|
||||
common::test_utils::create_transaction_native_token_transfer(
|
||||
*from_id, nonce, *to_id, amount, from_key,
|
||||
)
|
||||
|
||||
@ -9,7 +9,7 @@ pub struct BalanceSnapshot(pub std::collections::HashMap<nssa::AccountId, u128>)
|
||||
impl BalanceSnapshot {
|
||||
/// Capture current total balance over all known accounts.
|
||||
pub fn total(&self) -> u128 {
|
||||
self.0.values().copied().fold(0u128, u128::saturating_add)
|
||||
self.0.values().copied().fold(0_u128, u128::saturating_add)
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,9 +72,8 @@ impl ProtocolInvariant for StateIsolationOnFailure {
|
||||
return Some(InvariantViolation {
|
||||
invariant: self.name(),
|
||||
message: format!(
|
||||
"balance changed despite tx rejection: account {:?} had \
|
||||
"balance changed despite tx rejection: account {acc_id:?} had \
|
||||
{expected_balance} before, {actual_balance} after",
|
||||
acc_id,
|
||||
),
|
||||
});
|
||||
}
|
||||
@ -106,7 +105,7 @@ impl ProtocolInvariant for BalanceConservation {
|
||||
.0
|
||||
.keys()
|
||||
.map(|&id| ctx.state_after.get_account_by_id(id).balance)
|
||||
.fold(0u128, u128::saturating_add);
|
||||
.fold(0_u128, u128::saturating_add);
|
||||
if total_before != total_after {
|
||||
return Some(InvariantViolation {
|
||||
invariant: self.name(),
|
||||
@ -142,10 +141,9 @@ impl ProtocolInvariant for FailedTxNonceStability {
|
||||
return Some(InvariantViolation {
|
||||
invariant: self.name(),
|
||||
message: format!(
|
||||
"nonce changed despite tx rejection: account {:?} nonce was \
|
||||
{:?} before, {:?} after \
|
||||
(griefing attack — victim nonce permanently burned on failed tx)",
|
||||
acc_id, expected_nonce, actual_nonce,
|
||||
"nonce changed despite tx rejection: account {acc_id:?} nonce was \
|
||||
{expected_nonce:?} before, {actual_nonce:?} after \
|
||||
(griefing attack \u{2014} victim nonce permanently burned on failed tx)",
|
||||
),
|
||||
});
|
||||
}
|
||||
@ -241,7 +239,7 @@ pub fn assert_replay_rejection(
|
||||
let replay = applied_tx.execute_check_on_state(state, next_block_id, next_timestamp);
|
||||
assert!(
|
||||
replay.is_err(),
|
||||
"INVARIANT VIOLATION [ReplayRejection]: transaction accepted a second time — \
|
||||
"INVARIANT VIOLATION [ReplayRejection]: transaction accepted a second time \u{2014} \
|
||||
nonce replay not prevented (replay block_id={next_block_id}, \
|
||||
replay timestamp={next_timestamp})",
|
||||
);
|
||||
@ -298,15 +296,14 @@ pub fn assert_nonce_increment_correctness(
|
||||
nonce_before
|
||||
.0
|
||||
.checked_add(1)
|
||||
.expect("nonce overflow — signer nonce at u128::MAX"),
|
||||
.expect("nonce overflow \u{2014} signer nonce at u128::MAX"),
|
||||
);
|
||||
assert_eq!(
|
||||
nonce_after, expected,
|
||||
"INVARIANT VIOLATION [NonceIncrementCorrectness]: signer account {:?} nonce \
|
||||
"INVARIANT VIOLATION [NonceIncrementCorrectness]: signer account {id:?} nonce \
|
||||
not incremented by 1 after successful transaction \
|
||||
— before={:?}, expected={:?}, got={:?} \
|
||||
\u{2014} before={nonce_before:?}, expected={expected:?}, got={nonce_after:?} \
|
||||
(apply_state_diff failed to increment nonce exactly once)",
|
||||
id, nonce_before, expected, nonce_after,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -389,13 +386,13 @@ mod tests {
|
||||
#[test]
|
||||
fn balance_conservation_catches_inflation_on_success() {
|
||||
// Arrange: one account with balance 100.
|
||||
let acc_id = nssa::AccountId::new([1u8; 32]);
|
||||
let acc_id = nssa::AccountId::new([1_u8; 32]);
|
||||
let state_before = V03State::new_with_genesis_accounts(&[(acc_id, 100)], vec![], 0);
|
||||
// Simulate execution that inflated the balance to 200.
|
||||
let state_after = V03State::new_with_genesis_accounts(&[(acc_id, 200)], vec![], 0);
|
||||
|
||||
let mut balances = std::collections::HashMap::new();
|
||||
balances.insert(acc_id, 100u128);
|
||||
balances.insert(acc_id, 100_u128);
|
||||
|
||||
let ctx = InvariantCtx {
|
||||
state_before: &state_before,
|
||||
@ -419,7 +416,7 @@ mod tests {
|
||||
#[test]
|
||||
fn nonce_increment_correctness_passes_when_signer_not_in_snapshot() {
|
||||
// Signer ID is present in the list but absent from the snapshot — skipped.
|
||||
let acc_id = nssa::AccountId::new([9u8; 32]);
|
||||
let acc_id = nssa::AccountId::new([9_u8; 32]);
|
||||
let state = make_empty_state();
|
||||
// Empty snapshot → `continue` branch fires; no assertion is made.
|
||||
assert_nonce_increment_correctness(&[acc_id], &make_empty_nonce_snapshot(), &state);
|
||||
@ -429,7 +426,7 @@ mod tests {
|
||||
fn nonce_increment_correctness_catches_unchanged_nonce() {
|
||||
// Arrange: signer has nonce 5 in the snapshot; the state returns Nonce(0) for the
|
||||
// same account (genesis default). expected = Nonce(6), actual = Nonce(0) → VIOLATION.
|
||||
let acc_id = nssa::AccountId::new([3u8; 32]);
|
||||
let acc_id = nssa::AccountId::new([3_u8; 32]);
|
||||
let state = V03State::new_with_genesis_accounts(&[], vec![], 0);
|
||||
|
||||
let mut nonces = std::collections::HashMap::new();
|
||||
@ -443,7 +440,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn failed_tx_nonce_stability_catches_nonce_mutation() {
|
||||
let acc_id = nssa::AccountId::new([2u8; 32]);
|
||||
let acc_id = nssa::AccountId::new([2_u8; 32]);
|
||||
// before: nonce 5; after: nonce 6 (should not happen on failure)
|
||||
let state_before = V03State::new_with_genesis_accounts(&[(acc_id, 100)], vec![], 0);
|
||||
let state_after = V03State::new_with_genesis_accounts(&[(acc_id, 100)], vec![], 0);
|
||||
@ -455,7 +452,7 @@ mod tests {
|
||||
nonces.insert(acc_id, Nonce(1));
|
||||
|
||||
let mut balances = std::collections::HashMap::new();
|
||||
balances.insert(acc_id, 100u128);
|
||||
balances.insert(acc_id, 100_u128);
|
||||
|
||||
let ctx = InvariantCtx {
|
||||
state_before: &state_before,
|
||||
@ -493,36 +490,33 @@ mod replay_proptest {
|
||||
let accounts = test_accounts();
|
||||
let init_accs: Vec<(nssa::AccountId, u128)> = accounts
|
||||
.iter()
|
||||
.map(|(id, _)| (*id, 1_000_000u128))
|
||||
.map(|(id, _)| (*id, 1_000_000_u128))
|
||||
.collect();
|
||||
V03State::new_with_genesis_accounts(&init_accs, vec![], 0)
|
||||
}
|
||||
|
||||
proptest! {
|
||||
/// **ReplayRejection** — a transaction accepted in block N must be
|
||||
/// **ReplayRejection** \u{2014} a transaction accepted in block N must be
|
||||
/// rejected when replayed in block N+1, because the nonce is consumed
|
||||
/// on first acceptance.
|
||||
#[test]
|
||||
fn replay_rejection_proptest(tx in arb_native_transfer_tx(test_accounts())) {
|
||||
let mut state = make_test_state();
|
||||
|
||||
// Stateless gate — skip structurally invalid transactions (e.g. those
|
||||
// Stateless gate \u{2014} skip structurally invalid transactions (e.g. those
|
||||
// whose public key does not match the declared sender).
|
||||
let validated_tx = match tx.transaction_stateless_check() {
|
||||
Ok(v) => v,
|
||||
Err(_) => return Ok(()),
|
||||
};
|
||||
let Ok(validated_tx) = tx.transaction_stateless_check() else { return Ok(()) };
|
||||
|
||||
// First application — may fail for state-level reasons (e.g. sender
|
||||
// First application \u{2014} may fail for state-level reasons (e.g. sender
|
||||
// has insufficient balance, wrong nonce). In that case there is
|
||||
// nothing to replay.
|
||||
let first_result = validated_tx.execute_check_on_state(&mut state, 1, 0);
|
||||
|
||||
if let Ok(validated_tx) = first_result {
|
||||
if let Ok(applied_tx) = first_result {
|
||||
// Use the shared framework function. assert_replay_rejection uses
|
||||
// assert!() rather than prop_assert!(); for structured proptest
|
||||
// inputs the framework-level panic is equivalent.
|
||||
super::assert_replay_rejection(validated_tx, &mut state, 2, 1);
|
||||
super::assert_replay_rejection(applied_tx, &mut state, 2, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,65 @@
|
||||
//! Fuzzing property library: invariant framework + input generators.
|
||||
|
||||
#![allow(clippy::missing_docs_in_private_items)]
|
||||
#![allow(
|
||||
clippy::missing_docs_in_private_items,
|
||||
reason = "fuzz/test library; internal docs omitted for brevity"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::single_char_lifetime_names,
|
||||
reason = "the `Arbitrary` trait uses `'a` and our impls must match its signature"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::exhaustive_structs,
|
||||
reason = "fuzz-library newtype wrappers and test helpers; non_exhaustive would only add noise"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::missing_inline_in_public_items,
|
||||
reason = "fuzz/test library; inlining hints have negligible effect here"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::question_mark_used,
|
||||
reason = "`?` is the idiomatic Rust error-propagation operator in `Arbitrary` implementations"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::as_conversions,
|
||||
reason = "u8 → usize for index arithmetic is safe and bounded in arbitrary contexts"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::integer_division_remainder_used,
|
||||
reason = "modulo is the natural way to bound arbitrary u8 values to a range"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::arbitrary_source_item_ordering,
|
||||
reason = "items are grouped logically rather than alphabetically for readability"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::iter_over_hash_type,
|
||||
reason = "invariant checks iterate over all accounts; iteration order does not affect correctness"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::arithmetic_side_effects,
|
||||
reason = "arithmetic is bounded by construction in test/fuzz helpers"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::integer_division,
|
||||
reason = "u128::MAX / 2 is intentional for generating overflow-inducing test values"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::module_name_repetitions,
|
||||
reason = "assert_invariants is the canonical, self-documenting name for this function"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::unused_trait_names,
|
||||
reason = "named `Arbitrary` import needed to disambiguate from `proptest::arbitrary::Arbitrary` in generators.rs"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::let_underscore_must_use,
|
||||
reason = "seed-generation IO errors are intentionally ignored in tests"
|
||||
)]
|
||||
#![allow(
|
||||
clippy::let_underscore_untyped,
|
||||
reason = "seed-generation IO errors are intentionally ignored in tests"
|
||||
)]
|
||||
|
||||
pub mod arbitrary_types;
|
||||
pub mod generators;
|
||||
@ -51,9 +110,9 @@ mod seed_gen {
|
||||
for rel in &targets {
|
||||
let p = workspace_root.join(rel);
|
||||
if let Some(parent) = p.parent() {
|
||||
fs::create_dir_all(parent).ok();
|
||||
let _ = fs::create_dir_all(parent);
|
||||
}
|
||||
fs::write(&p, &bytes).ok();
|
||||
let _ = fs::write(&p, &bytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user