mirror of
https://github.com/logos-blockchain/lez-fuzzing.git
synced 2026-06-07 03:29:26 +00:00
fix: targets and props
This commit is contained in:
parent
9c7409fbe7
commit
f411509eb2
@ -18,12 +18,10 @@ fuzz_target!(|data: &[u8]| {
|
|||||||
let recomputed2 = hashable.block_hash();
|
let recomputed2 = hashable.block_hash();
|
||||||
assert_eq!(recomputed, recomputed2, "block_hash() is not deterministic");
|
assert_eq!(recomputed, recomputed2, "block_hash() is not deterministic");
|
||||||
|
|
||||||
// Log divergence between stored and recomputed hash for coverage guidance.
|
// We intentionally do NOT assert that the stored header hash equals the
|
||||||
// We do NOT assert equality because adversarially-crafted fuzz inputs can
|
// recomputed one: adversarially-crafted fuzz inputs can store an arbitrary
|
||||||
// store an arbitrary hash field without matching the body content.
|
// hash field that does not match the body content, and that is a valid input
|
||||||
let stored_hash = block.header.hash;
|
// for the purpose of this target (which only tests hash stability, not
|
||||||
if stored_hash == recomputed {
|
// block validity).
|
||||||
// Hashes match — this is the expected case for a valid sequencer-produced block
|
let _ = (block.header.hash, recomputed);
|
||||||
let _ = stored_hash;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -21,7 +21,7 @@ fuzz_target!(|data: &[u8]| {
|
|||||||
|
|
||||||
// Generate up to 8 transactions and apply them
|
// Generate up to 8 transactions and apply them
|
||||||
let n_txs: u8 = u8::arbitrary(&mut u).unwrap_or(0) % 8;
|
let n_txs: u8 = u8::arbitrary(&mut u).unwrap_or(0) % 8;
|
||||||
for _ in 0..n_txs {
|
for i in 0..n_txs {
|
||||||
let Ok(tx) = arbitrary_transaction(&mut u) else {
|
let Ok(tx) = arbitrary_transaction(&mut u) else {
|
||||||
break;
|
break;
|
||||||
};
|
};
|
||||||
@ -34,8 +34,12 @@ fuzz_target!(|data: &[u8]| {
|
|||||||
// Clone state before to detect state leakage on failure
|
// Clone state before to detect state leakage on failure
|
||||||
let state_snapshot = state.clone();
|
let state_snapshot = state.clone();
|
||||||
|
|
||||||
let block_id: u64 = 1;
|
// Advance block_id and timestamp each iteration so the state machine
|
||||||
let timestamp: u64 = 0;
|
// sees a realistic monotonically-increasing context. Using the same
|
||||||
|
// block_id=1 / timestamp=0 for every tx hides bugs that only manifest
|
||||||
|
// when the block context changes across a multi-transaction sequence.
|
||||||
|
let block_id: u64 = 1 + u64::from(i);
|
||||||
|
let timestamp: u64 = u64::from(i);
|
||||||
let result = tx.execute_check_on_state(&mut state, block_id, timestamp);
|
let result = tx.execute_check_on_state(&mut state, block_id, timestamp);
|
||||||
|
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
|
|||||||
@ -13,7 +13,7 @@ fuzz_target!(|data: &[u8]| {
|
|||||||
let tx2 = borsh::from_slice::<NSSATransaction>(&re_encoded)
|
let tx2 = borsh::from_slice::<NSSATransaction>(&re_encoded)
|
||||||
.expect("second decode of re-encoded tx must succeed");
|
.expect("second decode of re-encoded tx must succeed");
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
borsh::to_vec(&tx).unwrap(),
|
re_encoded,
|
||||||
borsh::to_vec(&tx2).unwrap(),
|
borsh::to_vec(&tx2).unwrap(),
|
||||||
"NSSATransaction roundtrip encoding divergence"
|
"NSSATransaction roundtrip encoding divergence"
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
use arbitrary::{Arbitrary, Unstructured};
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
use common::{block::HashableBlockData, transaction::NSSATransaction};
|
use common::{block::HashableBlockData, transaction::NSSATransaction};
|
||||||
use nssa::{AccountId, PrivateKey};
|
use nssa::{AccountId, PrivateKey};
|
||||||
|
|
||||||
|
use crate::arbitrary_types::ArbNSSATransaction;
|
||||||
use proptest::prelude::*;
|
use proptest::prelude::*;
|
||||||
use testnet_initial_state::initial_pub_accounts_private_keys;
|
use testnet_initial_state::initial_pub_accounts_private_keys;
|
||||||
|
|
||||||
@ -9,26 +11,15 @@ use testnet_initial_state::initial_pub_accounts_private_keys;
|
|||||||
/// A best-effort attempt to create a structurally plausible `NSSATransaction`
|
/// A best-effort attempt to create a structurally plausible `NSSATransaction`
|
||||||
/// from unstructured bytes. Falls back to raw borsh decoding.
|
/// from unstructured bytes. Falls back to raw borsh decoding.
|
||||||
pub fn arbitrary_transaction(u: &mut Unstructured<'_>) -> arbitrary::Result<NSSATransaction> {
|
pub fn arbitrary_transaction(u: &mut Unstructured<'_>) -> arbitrary::Result<NSSATransaction> {
|
||||||
// Prefer structured generation; raw decode as fallback
|
// Prefer structured generation (via Arbitrary impls); raw borsh decode as fallback.
|
||||||
if bool::arbitrary(u)? {
|
if bool::arbitrary(u)? {
|
||||||
let raw = Vec::<u8>::arbitrary(u)?;
|
let raw = Vec::<u8>::arbitrary(u)?;
|
||||||
borsh::from_slice::<NSSATransaction>(&raw).map_err(|_| arbitrary::Error::IncorrectFormat)
|
borsh::from_slice::<NSSATransaction>(&raw).map_err(|_| arbitrary::Error::IncorrectFormat)
|
||||||
} else {
|
} else {
|
||||||
// Generate a minimal empty public tx using known test keys
|
// Use the full ArbNSSATransaction generator, which produces both Public and
|
||||||
let signing_key = PrivateKey::try_new([u8::arbitrary(u)?; 32])
|
// ProgramDeployment variants with realistic account IDs, nonces, and witness sets —
|
||||||
.map_err(|_| arbitrary::Error::IncorrectFormat)?;
|
// far richer than the previous degenerate single-byte key / empty-message path.
|
||||||
let program_id = nssa::program::Program::authenticated_transfer_program().id();
|
ArbNSSATransaction::arbitrary(u).map(|w| w.0)
|
||||||
let message = nssa::public_transaction::Message::try_new(
|
|
||||||
program_id,
|
|
||||||
vec![],
|
|
||||||
vec![],
|
|
||||||
u128::arbitrary(u)?,
|
|
||||||
)
|
|
||||||
.map_err(|_| arbitrary::Error::IncorrectFormat)?;
|
|
||||||
let witness = nssa::public_transaction::WitnessSet::for_message(&message, &[&signing_key]);
|
|
||||||
Ok(NSSATransaction::Public(nssa::PublicTransaction::new(
|
|
||||||
message, witness,
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -44,11 +44,18 @@ impl ProtocolInvariant for StateIsolationOnFailure {
|
|||||||
|
|
||||||
fn check(&self, ctx: &InvariantCtx<'_>) -> Option<InvariantViolation> {
|
fn check(&self, ctx: &InvariantCtx<'_>) -> Option<InvariantViolation> {
|
||||||
if ctx.result.is_err() {
|
if ctx.result.is_err() {
|
||||||
// Capture snapshot totals for comparison
|
for (acc_id, &expected_balance) in &ctx.balances_before.0 {
|
||||||
let _before_total = ctx.balances_before.total();
|
let actual_balance = ctx.state_after.get_account_by_id(*acc_id).balance;
|
||||||
let _state_after = ctx.state_after;
|
if actual_balance != expected_balance {
|
||||||
// TODO: implement actual balance extraction from V03State once API is confirmed
|
return Some(InvariantViolation {
|
||||||
// (use state_after.get_account_by_id per known account and compare with before)
|
invariant: self.name(),
|
||||||
|
message: format!(
|
||||||
|
"balance changed despite tx rejection: account {:?} had {expected_balance} before, {actual_balance} after",
|
||||||
|
acc_id,
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user