mirror of
https://github.com/logos-blockchain/lez-fuzzing.git
synced 2026-06-07 11:39:30 +00:00
chore: synchronize with latest lee introduction
This commit is contained in:
parent
2adc491361
commit
ccd08aed6f
836
Cargo.lock
generated
836
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
12
Cargo.toml
12
Cargo.toml
@ -52,11 +52,13 @@ unsafe_code = "deny"
|
||||
[workspace.dependencies]
|
||||
|
||||
# ── LEZ crates — expects logos-execution-zone/ to be cloned at ../logos-execution-zone ──
|
||||
nssa = { path = "../logos-execution-zone/nssa" }
|
||||
nssa_core = { path = "../logos-execution-zone/nssa/core" }
|
||||
common = { path = "../logos-execution-zone/common" }
|
||||
key_protocol = { path = "../logos-execution-zone/key_protocol" }
|
||||
testnet_initial_state = { path = "../logos-execution-zone/testnet_initial_state" }
|
||||
# LEZ reorganised its directory layout; the package= key keeps the old dependency
|
||||
# alias so that fuzz_props source code (use nssa::...) compiles unchanged.
|
||||
nssa = { path = "../logos-execution-zone/lee/state_machine", package = "lee" }
|
||||
nssa_core = { path = "../logos-execution-zone/lee/state_machine/core", package = "lee_core" }
|
||||
common = { path = "../logos-execution-zone/lez/common" }
|
||||
key_protocol = { path = "../logos-execution-zone/lee/key_protocol" }
|
||||
testnet_initial_state = { path = "../logos-execution-zone/lez/testnet_initial_state" }
|
||||
token_core = { path = "../logos-execution-zone/programs/token/core" }
|
||||
test_program_methods = { path = "../logos-execution-zone/test_program_methods" }
|
||||
|
||||
|
||||
9
Justfile
9
Justfile
@ -788,10 +788,13 @@ mutants-protocol PACKAGES="nssa common":
|
||||
|
||||
# ── Housekeeping ──────────────────────────────────────────────────────────────
|
||||
|
||||
# Remove all Cargo build artefacts (workspace + fuzz sub-crate)
|
||||
# Remove all Cargo build artefacts (workspace + fuzz sub-crate + logos-execution-zone)
|
||||
# Each command is prefixed with `-` so that a missing sibling workspace (LEZ not cloned)
|
||||
# does not abort the recipe — cargo clean still removes whatever targets are present.
|
||||
clean:
|
||||
cargo clean
|
||||
cargo clean --manifest-path fuzz/Cargo.toml
|
||||
-cargo clean
|
||||
-cargo clean --manifest-path fuzz/Cargo.toml
|
||||
-cargo clean --manifest-path ../logos-execution-zone/Cargo.toml
|
||||
|
||||
# Remove libFuzzer crash/timeout artifacts for all targets (corpus is kept)
|
||||
clean-artifacts:
|
||||
|
||||
@ -116,7 +116,7 @@ just fuzz-props
|
||||
|
||||
| Target | Protocol layer | Entry point |
|
||||
|--------|---------------|-------------|
|
||||
| `fuzz_transaction_decoding` | Borsh decoding of all tx/block types (`NSSATransaction`, `Block`, `HashableBlockData`) with roundtrip re-encoding | `fuzz/fuzz_targets/fuzz_transaction_decoding.rs` |
|
||||
| `fuzz_transaction_decoding` | Borsh decoding of all tx/block types (`LeeTransaction`, `Block`, `HashableBlockData`) with roundtrip re-encoding | `fuzz/fuzz_targets/fuzz_transaction_decoding.rs` |
|
||||
| `fuzz_stateless_verification` | `transaction_stateless_check()` no-panic + idempotency | `fuzz/fuzz_targets/fuzz_stateless_verification.rs` |
|
||||
| `fuzz_state_transition` | `V03State` transition: StateIsolationOnFailure + BalanceConservation + ReplayRejection invariants across up to 8 txs with fuzz-driven state | `fuzz/fuzz_targets/fuzz_state_transition.rs` |
|
||||
| `fuzz_block_verification` | Block hash integrity: HashRoundTrip · HashPreimage completeness (block_id/prev_hash/timestamp) · TxOrderCommitment | `fuzz/fuzz_targets/fuzz_block_verification.rs` |
|
||||
|
||||
@ -103,7 +103,7 @@ just fuzz-regression
|
||||
|
||||
| Target | What it fuzzes | Entry point |
|
||||
|--------|---------------|-------------|
|
||||
| `fuzz_transaction_decoding` | Borsh decoding of `NSSATransaction`, `Block`, and `HashableBlockData`; roundtrip re-encoding of successfully decoded transactions | `fuzz/fuzz_targets/fuzz_transaction_decoding.rs` |
|
||||
| `fuzz_transaction_decoding` | Borsh decoding of `LeeTransaction`, `Block`, and `HashableBlockData`; roundtrip re-encoding of successfully decoded transactions | `fuzz/fuzz_targets/fuzz_transaction_decoding.rs` |
|
||||
| `fuzz_stateless_verification` | `transaction_stateless_check()` no-panic on arbitrary bytes; idempotency — a transaction that passes the check must pass it again | `fuzz/fuzz_targets/fuzz_stateless_verification.rs` |
|
||||
| `fuzz_state_transition` | `execute_check_on_state()` across up to 8 transactions with fuzz-driven initial state and monotonically-advancing block context; asserts **StateIsolationOnFailure** (balances unchanged on rejection), **BalanceConservation** (total balance unchanged on success), and **ReplayRejection** (nonce consumed on first acceptance) | `fuzz/fuzz_targets/fuzz_state_transition.rs` |
|
||||
| `fuzz_block_verification` | Three block-hash invariants: **HashRoundTrip** (`HashableBlockData::from(Block)` is lossless), **HashPreimage** (block_id, prev_block_hash, timestamp each individually affect the hash), **TxOrderCommitment** (reversing the transaction list changes the hash) | `fuzz/fuzz_targets/fuzz_block_verification.rs` |
|
||||
@ -558,19 +558,19 @@ fuzz target parameters for zero-boilerplate structured fuzzing.
|
||||
| `ArbWitnessSet` | `WitnessSet` (0–3 `(Signature, PublicKey)` pairs; mixes valid and invalid) |
|
||||
| `ArbPublicTransaction` | `PublicTransaction` (composed from `ArbPubTxMessage` + `ArbWitnessSet`) |
|
||||
| `ArbProgramDeploymentTransaction` | `ProgramDeploymentTransaction` (arbitrary bytecode) |
|
||||
| `ArbHashableBlockData` | `HashableBlockData` (0–7 `ArbNSSATransaction` entries, random header fields) |
|
||||
| `ArbNSSATransaction` | `NSSATransaction` (`Public` or `ProgramDeployment` variant; `PrivacyPreserving` excluded) |
|
||||
| `ArbHashableBlockData` | `HashableBlockData` (0–7 `ArbLeeTransaction` entries, random header fields) |
|
||||
| `ArbLeeTransaction` | `LeeTransaction` (`Public` or `ProgramDeployment` variant; `PrivacyPreserving` excluded) |
|
||||
|
||||
### `fuzz_props::generators` (libFuzzer helpers + proptest strategies)
|
||||
|
||||
| Generator | Covers |
|
||||
|-----------|--------|
|
||||
| `arbitrary_fuzz_state()` | 1–8 fuzz-driven accounts with arbitrary IDs, balances, and private keys; used by `fuzz_state_transition`, `fuzz_replay_prevention`, `fuzz_validate_execute_consistency`, `fuzz_state_diff_computation` |
|
||||
| `arb_fuzz_native_transfer()` | Correctly-signed native-transfer `NSSATransaction` referencing accounts from an `arbitrary_fuzz_state()` result; gives the fuzzer a path to successful state transitions |
|
||||
| `arbitrary_transaction()` | Structured `NSSATransaction` (`Public` or `ProgramDeployment`) from unstructured bytes via `ArbNSSATransaction` |
|
||||
| `arb_fuzz_native_transfer()` | Correctly-signed native-transfer `LeeTransaction` referencing accounts from an `arbitrary_fuzz_state()` result; gives the fuzzer a path to successful state transitions |
|
||||
| `arbitrary_transaction()` | Structured `LeeTransaction` (`Public` or `ProgramDeployment`) from unstructured bytes via `ArbLeeTransaction` |
|
||||
| `arb_borsh_transaction_bytes()` | Raw Borsh bytes including invalid encodings |
|
||||
| `signer_account_ids()` | Extracts `AccountId`s of all signers from an `NSSATransaction`'s witness set; used to derive signer IDs before `apply_state_diff` consumes the diff |
|
||||
| `arb_native_transfer_tx()` | Valid native-transfer `NSSATransaction` between known testnet genesis accounts (proptest strategy) |
|
||||
| `signer_account_ids()` | Extracts `AccountId`s of all signers from an `LeeTransaction`'s witness set; used to derive signer IDs before `apply_state_diff` consumes the diff |
|
||||
| `arb_native_transfer_tx()` | Valid native-transfer `LeeTransaction` between known testnet genesis accounts (proptest strategy) |
|
||||
| `test_accounts()` | Returns `(AccountId, PrivateKey)` pairs from `testnet_initial_state` |
|
||||
| `arb_hashable_block_data()` | `HashableBlockData` with 0–8 valid native transfers (proptest strategy) |
|
||||
| `arb_invalid_account_state_tx()` | Phantom accounts + overflow amounts — expected to be rejected (IS-3) |
|
||||
|
||||
840
fuzz/Cargo.lock
generated
840
fuzz/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -44,11 +44,11 @@ libfuzzer-sys = { version = "0.4", optional = true }
|
||||
afl = { version = "0.15", optional = true }
|
||||
arbitrary = { version = "1", features = ["derive"] }
|
||||
borsh = "1"
|
||||
nssa = { path = "../../logos-execution-zone/nssa" }
|
||||
nssa_core = { path = "../../logos-execution-zone/nssa/core" }
|
||||
common = { path = "../../logos-execution-zone/common" }
|
||||
nssa = { path = "../../logos-execution-zone/lee/state_machine", package = "lee" }
|
||||
nssa_core = { path = "../../logos-execution-zone/lee/state_machine/core", package = "lee_core" }
|
||||
common = { path = "../../logos-execution-zone/lez/common" }
|
||||
fuzz_props = { path = "../fuzz_props" }
|
||||
testnet_initial_state = { path = "../../logos-execution-zone/testnet_initial_state" }
|
||||
testnet_initial_state = { path = "../../logos-execution-zone/lez/testnet_initial_state" }
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
||||
@ -33,7 +33,7 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
use fuzz_props::arbitrary_types::ArbNSSATransaction;
|
||||
use fuzz_props::arbitrary_types::ArbLeeTransaction;
|
||||
use fuzz_props::generators::{arbitrary_fuzz_state, signer_account_ids};
|
||||
use fuzz_props::invariants::{NonceSnapshot, assert_nonce_increment_correctness};
|
||||
use nssa::V03State;
|
||||
@ -52,7 +52,7 @@ fuzz_props::fuzz_entry!(|data: &[u8]| {
|
||||
.collect();
|
||||
|
||||
// Generate and stateless-check a transaction.
|
||||
let tx_raw = match ArbNSSATransaction::arbitrary(&mut u) {
|
||||
let tx_raw = match ArbLeeTransaction::arbitrary(&mut u) {
|
||||
Ok(w) => w.0,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
use common::transaction::{NSSATransaction, clock_invocation};
|
||||
use common::transaction::{LeeTransaction, clock_invocation};
|
||||
use fuzz_props::generators::{arb_fuzz_native_transfer, arbitrary_fuzz_state, arbitrary_transaction};
|
||||
use nssa::V03State;
|
||||
|
||||
@ -81,7 +81,7 @@ fuzz_props::fuzz_entry!(|data: &[u8]| {
|
||||
|
||||
// Accepted transaction list — populated here, consumed by the replayer phase
|
||||
// so that both pipelines process exactly the same set of transactions.
|
||||
let mut accepted_txs: Vec<NSSATransaction> = Vec::new();
|
||||
let mut accepted_txs: Vec<LeeTransaction> = Vec::new();
|
||||
|
||||
let n_txs: u8 = u8::arbitrary(&mut u).unwrap_or(0) % 8;
|
||||
|
||||
|
||||
@ -19,7 +19,7 @@
|
||||
//! specific account shapes such as zero balance or `u128::MAX` — are reachable.
|
||||
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
use common::transaction::NSSATransaction;
|
||||
use common::transaction::LeeTransaction;
|
||||
use fuzz_props::arbitrary_types::ArbPublicTransaction;
|
||||
use fuzz_props::generators::arbitrary_fuzz_state;
|
||||
use nssa::{V03State, ValidatedStateDiff};
|
||||
@ -47,7 +47,7 @@ fuzz_props::fuzz_entry!(|data: &[u8]| {
|
||||
// Collect the set of accounts the transaction declares it will touch.
|
||||
// `affected_public_account_ids()` returns owned data so `pub_tx` remains
|
||||
// available for both `from_public_transaction` (borrow) and the later move
|
||||
// into `NSSATransaction::Public`.
|
||||
// into `LeeTransaction::Public`.
|
||||
let affected = pub_tx.affected_public_account_ids();
|
||||
|
||||
match ValidatedStateDiff::from_public_transaction(&pub_tx, &state, 1, 0) {
|
||||
@ -77,7 +77,7 @@ fuzz_props::fuzz_entry!(|data: &[u8]| {
|
||||
// we do not panic on a structurally malformed transaction.
|
||||
let mut exec_state = state.clone();
|
||||
// `pub_tx` is moved here; it is no longer borrowed after this point.
|
||||
let tx_for_exec = NSSATransaction::Public(pub_tx);
|
||||
let tx_for_exec = LeeTransaction::Public(pub_tx);
|
||||
if let Ok(checked_tx) = tx_for_exec.transaction_stateless_check() {
|
||||
if checked_tx.execute_check_on_state(&mut exec_state, 1, 0).is_ok() {
|
||||
for acc_id in &affected {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#![cfg_attr(feature = "fuzzer-libfuzzer", no_main)]
|
||||
|
||||
use arbitrary::Unstructured;
|
||||
use common::transaction::NSSATransaction;
|
||||
use common::transaction::LeeTransaction;
|
||||
use fuzz_props::generators::arbitrary_transaction;
|
||||
|
||||
fuzz_props::fuzz_entry!(|data: &[u8]| {
|
||||
@ -22,7 +22,7 @@ fuzz_props::fuzz_entry!(|data: &[u8]| {
|
||||
}
|
||||
|
||||
// Path B: raw decode first, then check — must never panic
|
||||
if let Ok(tx) = borsh::from_slice::<NSSATransaction>(data) {
|
||||
if let Ok(tx) = borsh::from_slice::<LeeTransaction>(data) {
|
||||
let _ = tx.transaction_stateless_check();
|
||||
}
|
||||
});
|
||||
|
||||
@ -2,19 +2,19 @@
|
||||
|
||||
use common::{
|
||||
block::{Block, HashableBlockData},
|
||||
transaction::NSSATransaction,
|
||||
transaction::LeeTransaction,
|
||||
};
|
||||
|
||||
fuzz_props::fuzz_entry!(|data: &[u8]| {
|
||||
// Attempt 1: decode as NSSATransaction and verify roundtrip
|
||||
if let Ok(tx) = borsh::from_slice::<NSSATransaction>(data) {
|
||||
// Attempt 1: decode as LeeTransaction and verify roundtrip
|
||||
if let Ok(tx) = borsh::from_slice::<LeeTransaction>(data) {
|
||||
let re_encoded = borsh::to_vec(&tx).expect("re-encode of valid tx must succeed");
|
||||
let tx2 = borsh::from_slice::<NSSATransaction>(&re_encoded)
|
||||
let tx2 = borsh::from_slice::<LeeTransaction>(&re_encoded)
|
||||
.expect("second decode of re-encoded tx must succeed");
|
||||
assert_eq!(
|
||||
re_encoded,
|
||||
borsh::to_vec(&tx2).unwrap(),
|
||||
"NSSATransaction roundtrip encoding divergence"
|
||||
"LeeTransaction roundtrip encoding divergence"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
//! reachable by the fuzzer.
|
||||
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
use fuzz_props::arbitrary_types::ArbNSSATransaction;
|
||||
use fuzz_props::arbitrary_types::ArbLeeTransaction;
|
||||
use fuzz_props::generators::{arbitrary_fuzz_state, signer_account_ids};
|
||||
use fuzz_props::invariants::{NonceSnapshot, assert_nonce_increment_correctness};
|
||||
use nssa::V03State;
|
||||
@ -47,7 +47,7 @@ fuzz_props::fuzz_entry!(|data: &[u8]| {
|
||||
.collect();
|
||||
|
||||
// Generate the transaction from the remaining fuzz bytes.
|
||||
let tx = match ArbNSSATransaction::arbitrary(&mut u) {
|
||||
let tx = match ArbLeeTransaction::arbitrary(&mut u) {
|
||||
Ok(w) => w.0,
|
||||
Err(_) => return,
|
||||
};
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
//!
|
||||
//! **No changes to `../logos-execution-zone` are required.**
|
||||
//!
|
||||
//! The Rust orphan rule forbids `impl Arbitrary for NSSATransaction` when both
|
||||
//! The Rust orphan rule forbids `impl Arbitrary for LeeTransaction` when both
|
||||
//! the trait and the type come from external crates. Using newtypes (`ArbXxx`)
|
||||
//! sidesteps the restriction entirely.
|
||||
//!
|
||||
@ -10,10 +10,10 @@
|
||||
//!
|
||||
//! ```rust,ignore
|
||||
//! #![no_main]
|
||||
//! use fuzz_props::arbitrary_types::ArbNSSATransaction;
|
||||
//! use fuzz_props::arbitrary_types::ArbLeeTransaction;
|
||||
//! use libfuzzer_sys::fuzz_target;
|
||||
//!
|
||||
//! fuzz_target!(|wrapped: ArbNSSATransaction| {
|
||||
//! fuzz_target!(|wrapped: ArbLeeTransaction| {
|
||||
//! let tx = wrapped.0;
|
||||
//! let Ok(valid_tx) = tx.transaction_stateless_check() else { return; };
|
||||
//! // …
|
||||
@ -21,7 +21,7 @@
|
||||
//! ```
|
||||
|
||||
use arbitrary::{Arbitrary, Result as ArbResult, Unstructured};
|
||||
use common::{HashType, block::HashableBlockData, transaction::NSSATransaction};
|
||||
use common::{HashType, block::HashableBlockData, transaction::LeeTransaction};
|
||||
use nssa::{
|
||||
AccountId, PrivateKey, PublicKey, Signature,
|
||||
program_deployment_transaction::ProgramDeploymentTransaction,
|
||||
@ -210,24 +210,24 @@ impl<'a> Arbitrary<'a> for ArbProgramDeploymentTransaction {
|
||||
}
|
||||
}
|
||||
|
||||
// ── NSSATransaction ───────────────────────────────────────────────────────────
|
||||
// ── LeeTransaction ───────────────────────────────────────────────────────────
|
||||
// `PrivacyPreservingTransaction` is intentionally excluded: it embeds a risc0
|
||||
// ZK receipt that cannot be generated inside a hot fuzzing loop. This matches
|
||||
// the known limitation documented in `docs/fuzzing.md`.
|
||||
|
||||
/// Newtype wrapper providing [`Arbitrary`] for [`NSSATransaction`].
|
||||
/// Newtype wrapper providing [`Arbitrary`] for [`LeeTransaction`].
|
||||
///
|
||||
/// Generates `Public` and `ProgramDeployment` variants only.
|
||||
#[derive(Debug)]
|
||||
pub struct ArbNSSATransaction(pub NSSATransaction);
|
||||
pub struct ArbLeeTransaction(pub LeeTransaction);
|
||||
|
||||
impl<'a> Arbitrary<'a> for ArbNSSATransaction {
|
||||
impl<'a> Arbitrary<'a> for ArbLeeTransaction {
|
||||
fn arbitrary(u: &mut Unstructured<'a>) -> ArbResult<Self> {
|
||||
match u8::arbitrary(u)? % 2 {
|
||||
0 => Ok(Self(NSSATransaction::Public(
|
||||
0 => Ok(Self(LeeTransaction::Public(
|
||||
ArbPublicTransaction::arbitrary(u)?.0,
|
||||
))),
|
||||
_ => Ok(Self(NSSATransaction::ProgramDeployment(
|
||||
_ => Ok(Self(LeeTransaction::ProgramDeployment(
|
||||
ArbProgramDeploymentTransaction::arbitrary(u)?.0,
|
||||
))),
|
||||
}
|
||||
@ -246,7 +246,7 @@ 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 = std::iter::repeat_with(|| ArbNSSATransaction::arbitrary(u).map(|t| t.0))
|
||||
let transactions = std::iter::repeat_with(|| ArbLeeTransaction::arbitrary(u).map(|t| t.0))
|
||||
.take(n)
|
||||
.collect::<ArbResult<Vec<_>>>()?;
|
||||
Ok(Self(HashableBlockData {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use arbitrary::{Arbitrary, Unstructured};
|
||||
use common::{block::HashableBlockData, transaction::NSSATransaction};
|
||||
use common::{block::HashableBlockData, transaction::LeeTransaction};
|
||||
use nssa::{AccountId, PrivateKey};
|
||||
|
||||
use crate::arbitrary_types::{ArbAccountId, ArbNSSATransaction, ArbPrivateKey};
|
||||
use crate::arbitrary_types::{ArbAccountId, ArbLeeTransaction, ArbPrivateKey};
|
||||
use proptest::prelude::*;
|
||||
use testnet_initial_state::initial_pub_accounts_private_keys;
|
||||
|
||||
@ -12,22 +12,22 @@ use testnet_initial_state::initial_pub_accounts_private_keys;
|
||||
/// 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;
|
||||
pub fn signer_account_ids(tx: &common::transaction::LeeTransaction) -> Vec<nssa::AccountId> {
|
||||
use common::transaction::LeeTransaction;
|
||||
match tx {
|
||||
NSSATransaction::Public(pt) => pt
|
||||
LeeTransaction::Public(pt) => pt
|
||||
.witness_set()
|
||||
.signatures_and_public_keys()
|
||||
.iter()
|
||||
.map(|(_, pk)| nssa::AccountId::from(pk))
|
||||
.collect(),
|
||||
NSSATransaction::PrivacyPreserving(pt) => pt
|
||||
LeeTransaction::PrivacyPreserving(pt) => pt
|
||||
.witness_set()
|
||||
.signatures_and_public_keys()
|
||||
.iter()
|
||||
.map(|(_, pk)| nssa::AccountId::from(pk))
|
||||
.collect(),
|
||||
NSSATransaction::ProgramDeployment(_) => vec![],
|
||||
LeeTransaction::ProgramDeployment(_) => vec![],
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ pub fn arbitrary_fuzz_state(u: &mut Unstructured<'_>) -> arbitrary::Result<Vec<F
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Generate a native-transfer [`NSSATransaction`] between two accounts chosen
|
||||
/// Generate a native-transfer [`LeeTransaction`] between two accounts chosen
|
||||
/// from `accounts`.
|
||||
///
|
||||
/// Because every account in the slice has a known private key, the resulting
|
||||
@ -87,7 +87,7 @@ pub fn arbitrary_fuzz_state(u: &mut Unstructured<'_>) -> arbitrary::Result<Vec<F
|
||||
pub fn arb_fuzz_native_transfer(
|
||||
u: &mut Unstructured<'_>,
|
||||
accounts: &[FuzzAccount],
|
||||
) -> arbitrary::Result<NSSATransaction> {
|
||||
) -> arbitrary::Result<LeeTransaction> {
|
||||
if accounts.is_empty() {
|
||||
return Err(arbitrary::Error::IncorrectFormat);
|
||||
}
|
||||
@ -112,9 +112,9 @@ pub fn arb_fuzz_native_transfer(
|
||||
|
||||
// ── Arbitrary (for libFuzzer targets) ────────────────────────────────────────
|
||||
|
||||
/// Generate a structurally plausible `NSSATransaction` from unstructured bytes.
|
||||
pub fn arbitrary_transaction(u: &mut Unstructured<'_>) -> arbitrary::Result<NSSATransaction> {
|
||||
ArbNSSATransaction::arbitrary(u).map(|w| w.0)
|
||||
/// Generate a structurally plausible `LeeTransaction` from unstructured bytes.
|
||||
pub fn arbitrary_transaction(u: &mut Unstructured<'_>) -> arbitrary::Result<LeeTransaction> {
|
||||
ArbLeeTransaction::arbitrary(u).map(|w| w.0)
|
||||
}
|
||||
|
||||
// ── proptest strategies ───────────────────────────────────────────────────────
|
||||
@ -128,7 +128,7 @@ prop_compose! {
|
||||
to_idx in 0..accounts.len(),
|
||||
nonce in 0_u128..1_000_u128,
|
||||
amount in 0_u128..10_000_u128,
|
||||
) -> NSSATransaction {
|
||||
) -> LeeTransaction {
|
||||
let (from_id, from_key) = &accounts[from_idx];
|
||||
let (to_id, _) = &accounts[to_idx];
|
||||
common::test_utils::create_transaction_native_token_transfer(
|
||||
@ -146,11 +146,11 @@ pub fn test_accounts() -> Vec<(AccountId, PrivateKey)> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Strategy: raw bytes that are valid borsh encodings of `NSSATransaction`.
|
||||
/// Strategy: raw bytes that are valid borsh encodings of `LeeTransaction`.
|
||||
pub fn arb_borsh_transaction_bytes() -> impl Strategy<Value = Vec<u8>> {
|
||||
any::<Vec<u8>>().prop_map(|bytes| {
|
||||
// Either pass through raw bytes OR encode a known dummy transaction
|
||||
if borsh::from_slice::<NSSATransaction>(&bytes).is_ok() {
|
||||
if borsh::from_slice::<LeeTransaction>(&bytes).is_ok() {
|
||||
bytes
|
||||
} else {
|
||||
borsh::to_vec(&common::test_utils::produce_dummy_empty_transaction()).unwrap()
|
||||
@ -183,7 +183,7 @@ prop_compose! {
|
||||
phantom_id_bytes in proptest::array::uniform32(0_u8..),
|
||||
amount in (u128::MAX / 2)..u128::MAX, // overflow-inducing amount
|
||||
nonce in 0_u128..10_u128,
|
||||
) -> NSSATransaction {
|
||||
) -> LeeTransaction {
|
||||
let phantom_id = nssa::AccountId::new(phantom_id_bytes);
|
||||
// Attempt to sign with a key that has no matching on-chain account
|
||||
let signing_key = nssa::PrivateKey::try_new(phantom_id_bytes)
|
||||
@ -204,11 +204,11 @@ prop_compose! {
|
||||
/// attack candidates) and some are re-ordered permutations of a valid sequence.
|
||||
/// Used in proptest-level tests and as a seed generator for the state-transition
|
||||
/// fuzz target.
|
||||
pub fn arb_duplicate_tx_sequence() -> impl Strategy<Value = Vec<NSSATransaction>> {
|
||||
pub fn arb_duplicate_tx_sequence() -> impl Strategy<Value = Vec<LeeTransaction>> {
|
||||
let accounts = test_accounts();
|
||||
proptest::collection::vec(arb_native_transfer_tx(accounts), 1..5_usize).prop_flat_map(|txs| {
|
||||
// Build a sequence that: original | duplicates | reversed
|
||||
let duped: Vec<NSSATransaction> = txs
|
||||
let duped: Vec<LeeTransaction> = txs
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(txs.iter().cloned()) // append exact duplicates
|
||||
@ -225,7 +225,7 @@ pub fn arb_duplicate_tx_sequence() -> impl Strategy<Value = Vec<NSSATransaction>
|
||||
/// - self-transfers (sender == recipient),
|
||||
/// - max-nonce wrapping,
|
||||
/// - alternating valid / invalid transactions to test partial-batch isolation.
|
||||
pub fn arb_pathological_sequence() -> impl Strategy<Value = Vec<NSSATransaction>> {
|
||||
pub fn arb_pathological_sequence() -> impl Strategy<Value = Vec<LeeTransaction>> {
|
||||
let accounts = test_accounts();
|
||||
let n = accounts.len();
|
||||
proptest::collection::vec((0..n, 0..n, 0_u128..5_u128, any::<bool>()), 1..8_usize).prop_map(
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use common::transaction::NSSATransaction;
|
||||
use common::transaction::LeeTransaction;
|
||||
use nssa::V03State;
|
||||
use nssa_core::account::Nonce;
|
||||
|
||||
@ -185,7 +185,7 @@ impl ProtocolInvariant for FailedTxNonceStability {
|
||||
/// # Enforcement
|
||||
///
|
||||
/// This invariant **cannot** be enforced through [`InvariantCtx`] because the replay
|
||||
/// check requires re-applying the `NSSATransaction` that `execute_check_on_state`
|
||||
/// check requires re-applying the `LeeTransaction` that `execute_check_on_state`
|
||||
/// consumes and returns on `Ok`. It is therefore **not registered** in
|
||||
/// [`assert_invariants`]; calling `assert_invariants` alone does **not** cover
|
||||
/// `ReplayRejection`.
|
||||
@ -235,7 +235,7 @@ pub struct NonceIncrementCorrectness;
|
||||
///
|
||||
/// # Why a standalone function?
|
||||
///
|
||||
/// `execute_check_on_state` consumes the `NSSATransaction` and returns it on `Ok`,
|
||||
/// `execute_check_on_state` consumes the `LeeTransaction` and returns it on `Ok`,
|
||||
/// so the transaction is not available as a shared reference inside [`InvariantCtx`].
|
||||
/// This function accepts ownership of the returned transaction and performs the
|
||||
/// replay in-place.
|
||||
@ -249,7 +249,7 @@ pub struct NonceIncrementCorrectness;
|
||||
/// }
|
||||
/// ```
|
||||
pub fn assert_replay_rejection(
|
||||
applied_tx: NSSATransaction,
|
||||
applied_tx: LeeTransaction,
|
||||
state: &mut V03State,
|
||||
next_block_id: u64,
|
||||
next_timestamp: u64,
|
||||
@ -270,7 +270,7 @@ pub fn assert_replay_rejection(
|
||||
/// passing the signer IDs derived from the transaction's witness set, the [`NonceSnapshot`]
|
||||
/// captured **before** execution, and the post-execution state.
|
||||
///
|
||||
/// For a `NSSATransaction::Public(tx)`, derive signer IDs as:
|
||||
/// For a `LeeTransaction::Public(tx)`, derive signer IDs as:
|
||||
///
|
||||
/// ```rust,ignore
|
||||
/// let signer_ids: Vec<nssa::AccountId> = tx
|
||||
@ -281,7 +281,7 @@ pub fn assert_replay_rejection(
|
||||
/// .collect();
|
||||
/// ```
|
||||
///
|
||||
/// For `NSSATransaction::ProgramDeployment`, there are no signers; pass an empty slice.
|
||||
/// For `LeeTransaction::ProgramDeployment`, there are no signers; pass an empty slice.
|
||||
///
|
||||
/// # Why a standalone function?
|
||||
///
|
||||
@ -377,7 +377,7 @@ pub fn assert_tx_execution_invariants<E>(
|
||||
state_after: &mut V03State,
|
||||
balances_before: BalanceSnapshot,
|
||||
nonces_before: NonceSnapshot,
|
||||
execution_result: Result<NSSATransaction, E>,
|
||||
execution_result: Result<LeeTransaction, E>,
|
||||
replay_context: (u64, u64),
|
||||
) {
|
||||
let execution_succeeded = execution_result.is_ok();
|
||||
@ -400,19 +400,19 @@ pub fn assert_tx_execution_invariants<E>(
|
||||
if let Ok(applied_tx) = execution_result {
|
||||
// Derive signer IDs from the witness set. ProgramDeployment has no signers.
|
||||
let signer_ids: Vec<nssa::AccountId> = match &applied_tx {
|
||||
NSSATransaction::Public(pt) => pt
|
||||
LeeTransaction::Public(pt) => pt
|
||||
.witness_set()
|
||||
.signatures_and_public_keys()
|
||||
.iter()
|
||||
.map(|(_, pk)| nssa::AccountId::from(pk))
|
||||
.collect(),
|
||||
NSSATransaction::PrivacyPreserving(pt) => pt
|
||||
LeeTransaction::PrivacyPreserving(pt) => pt
|
||||
.witness_set()
|
||||
.signatures_and_public_keys()
|
||||
.iter()
|
||||
.map(|(_, pk)| nssa::AccountId::from(pk))
|
||||
.collect(),
|
||||
NSSATransaction::ProgramDeployment(_) => vec![],
|
||||
LeeTransaction::ProgramDeployment(_) => vec![],
|
||||
};
|
||||
assert_nonce_increment_correctness(&signer_ids, &nonces_for_nonce_check, state_after);
|
||||
let (next_block_id, next_timestamp) = replay_context;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user