fix: targets and props

This commit is contained in:
Roman 2026-04-24 12:04:20 +08:00
parent 9c7409fbe7
commit f411509eb2
No known key found for this signature in database
GPG Key ID: 583BDF43C238B83E
5 changed files with 33 additions and 33 deletions

View File

@ -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;
}
}); });

View File

@ -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() {

View File

@ -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"
); );

View File

@ -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,
)))
} }
} }

View File

@ -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
} }