chore: synchronize with latest lee introduction

This commit is contained in:
Roman 2026-06-05 11:12:26 +08:00
parent 2adc491361
commit ccd08aed6f
No known key found for this signature in database
GPG Key ID: 583BDF43C238B83E
16 changed files with 1329 additions and 504 deletions

836
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -52,11 +52,13 @@ unsafe_code = "deny"
[workspace.dependencies] [workspace.dependencies]
# ── LEZ crates — expects logos-execution-zone/ to be cloned at ../logos-execution-zone ── # ── LEZ crates — expects logos-execution-zone/ to be cloned at ../logos-execution-zone ──
nssa = { path = "../logos-execution-zone/nssa" } # LEZ reorganised its directory layout; the package= key keeps the old dependency
nssa_core = { path = "../logos-execution-zone/nssa/core" } # alias so that fuzz_props source code (use nssa::...) compiles unchanged.
common = { path = "../logos-execution-zone/common" } nssa = { path = "../logos-execution-zone/lee/state_machine", package = "lee" }
key_protocol = { path = "../logos-execution-zone/key_protocol" } nssa_core = { path = "../logos-execution-zone/lee/state_machine/core", package = "lee_core" }
testnet_initial_state = { path = "../logos-execution-zone/testnet_initial_state" } 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" } token_core = { path = "../logos-execution-zone/programs/token/core" }
test_program_methods = { path = "../logos-execution-zone/test_program_methods" } test_program_methods = { path = "../logos-execution-zone/test_program_methods" }

View File

@ -788,10 +788,13 @@ mutants-protocol PACKAGES="nssa common":
# ── Housekeeping ────────────────────────────────────────────────────────────── # ── 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: clean:
cargo clean -cargo clean
cargo clean --manifest-path fuzz/Cargo.toml -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) # Remove libFuzzer crash/timeout artifacts for all targets (corpus is kept)
clean-artifacts: clean-artifacts:

View File

@ -116,7 +116,7 @@ just fuzz-props
| Target | Protocol layer | Entry point | | 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_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_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` | | `fuzz_block_verification` | Block hash integrity: HashRoundTrip · HashPreimage completeness (block_id/prev_hash/timestamp) · TxOrderCommitment | `fuzz/fuzz_targets/fuzz_block_verification.rs` |

View File

@ -103,7 +103,7 @@ just fuzz-regression
| Target | What it fuzzes | Entry point | | 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_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_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` | | `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` (03 `(Signature, PublicKey)` pairs; mixes valid and invalid) | | `ArbWitnessSet` | `WitnessSet` (03 `(Signature, PublicKey)` pairs; mixes valid and invalid) |
| `ArbPublicTransaction` | `PublicTransaction` (composed from `ArbPubTxMessage` + `ArbWitnessSet`) | | `ArbPublicTransaction` | `PublicTransaction` (composed from `ArbPubTxMessage` + `ArbWitnessSet`) |
| `ArbProgramDeploymentTransaction` | `ProgramDeploymentTransaction` (arbitrary bytecode) | | `ArbProgramDeploymentTransaction` | `ProgramDeploymentTransaction` (arbitrary bytecode) |
| `ArbHashableBlockData` | `HashableBlockData` (07 `ArbNSSATransaction` entries, random header fields) | | `ArbHashableBlockData` | `HashableBlockData` (07 `ArbLeeTransaction` entries, random header fields) |
| `ArbNSSATransaction` | `NSSATransaction` (`Public` or `ProgramDeployment` variant; `PrivacyPreserving` excluded) | | `ArbLeeTransaction` | `LeeTransaction` (`Public` or `ProgramDeployment` variant; `PrivacyPreserving` excluded) |
### `fuzz_props::generators` (libFuzzer helpers + proptest strategies) ### `fuzz_props::generators` (libFuzzer helpers + proptest strategies)
| Generator | Covers | | Generator | Covers |
|-----------|--------| |-----------|--------|
| `arbitrary_fuzz_state()` | 18 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` | | `arbitrary_fuzz_state()` | 18 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 | | `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 `NSSATransaction` (`Public` or `ProgramDeployment`) from unstructured bytes via `ArbNSSATransaction` | | `arbitrary_transaction()` | Structured `LeeTransaction` (`Public` or `ProgramDeployment`) from unstructured bytes via `ArbLeeTransaction` |
| `arb_borsh_transaction_bytes()` | Raw Borsh bytes including invalid encodings | | `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 | | `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 `NSSATransaction` between known testnet genesis accounts (proptest strategy) | | `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` | | `test_accounts()` | Returns `(AccountId, PrivateKey)` pairs from `testnet_initial_state` |
| `arb_hashable_block_data()` | `HashableBlockData` with 08 valid native transfers (proptest strategy) | | `arb_hashable_block_data()` | `HashableBlockData` with 08 valid native transfers (proptest strategy) |
| `arb_invalid_account_state_tx()` | Phantom accounts + overflow amounts — expected to be rejected (IS-3) | | `arb_invalid_account_state_tx()` | Phantom accounts + overflow amounts — expected to be rejected (IS-3) |

840
fuzz/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -44,11 +44,11 @@ libfuzzer-sys = { version = "0.4", optional = true }
afl = { version = "0.15", optional = true } afl = { version = "0.15", optional = true }
arbitrary = { version = "1", features = ["derive"] } arbitrary = { version = "1", features = ["derive"] }
borsh = "1" borsh = "1"
nssa = { path = "../../logos-execution-zone/nssa" } nssa = { path = "../../logos-execution-zone/lee/state_machine", package = "lee" }
nssa_core = { path = "../../logos-execution-zone/nssa/core" } nssa_core = { path = "../../logos-execution-zone/lee/state_machine/core", package = "lee_core" }
common = { path = "../../logos-execution-zone/common" } common = { path = "../../logos-execution-zone/lez/common" }
fuzz_props = { path = "../fuzz_props" } 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] [profile.release]
debug = true debug = true

View File

@ -33,7 +33,7 @@
use std::collections::HashSet; use std::collections::HashSet;
use arbitrary::{Arbitrary, Unstructured}; 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::generators::{arbitrary_fuzz_state, signer_account_ids};
use fuzz_props::invariants::{NonceSnapshot, assert_nonce_increment_correctness}; use fuzz_props::invariants::{NonceSnapshot, assert_nonce_increment_correctness};
use nssa::V03State; use nssa::V03State;
@ -52,7 +52,7 @@ fuzz_props::fuzz_entry!(|data: &[u8]| {
.collect(); .collect();
// Generate and stateless-check a transaction. // 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, Ok(w) => w.0,
Err(_) => return, Err(_) => return,
}; };

View File

@ -38,7 +38,7 @@
use std::collections::HashSet; use std::collections::HashSet;
use arbitrary::{Arbitrary, Unstructured}; 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 fuzz_props::generators::{arb_fuzz_native_transfer, arbitrary_fuzz_state, arbitrary_transaction};
use nssa::V03State; use nssa::V03State;
@ -81,7 +81,7 @@ fuzz_props::fuzz_entry!(|data: &[u8]| {
// Accepted transaction list — populated here, consumed by the replayer phase // Accepted transaction list — populated here, consumed by the replayer phase
// so that both pipelines process exactly the same set of transactions. // 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; let n_txs: u8 = u8::arbitrary(&mut u).unwrap_or(0) % 8;

View File

@ -19,7 +19,7 @@
//! specific account shapes such as zero balance or `u128::MAX` — are reachable. //! specific account shapes such as zero balance or `u128::MAX` — are reachable.
use arbitrary::{Arbitrary, Unstructured}; use arbitrary::{Arbitrary, Unstructured};
use common::transaction::NSSATransaction; use common::transaction::LeeTransaction;
use fuzz_props::arbitrary_types::ArbPublicTransaction; use fuzz_props::arbitrary_types::ArbPublicTransaction;
use fuzz_props::generators::arbitrary_fuzz_state; use fuzz_props::generators::arbitrary_fuzz_state;
use nssa::{V03State, ValidatedStateDiff}; 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. // Collect the set of accounts the transaction declares it will touch.
// `affected_public_account_ids()` returns owned data so `pub_tx` remains // `affected_public_account_ids()` returns owned data so `pub_tx` remains
// available for both `from_public_transaction` (borrow) and the later move // 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(); let affected = pub_tx.affected_public_account_ids();
match ValidatedStateDiff::from_public_transaction(&pub_tx, &state, 1, 0) { 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. // we do not panic on a structurally malformed transaction.
let mut exec_state = state.clone(); let mut exec_state = state.clone();
// `pub_tx` is moved here; it is no longer borrowed after this point. // `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 let Ok(checked_tx) = tx_for_exec.transaction_stateless_check() {
if checked_tx.execute_check_on_state(&mut exec_state, 1, 0).is_ok() { if checked_tx.execute_check_on_state(&mut exec_state, 1, 0).is_ok() {
for acc_id in &affected { for acc_id in &affected {

View File

@ -1,7 +1,7 @@
#![cfg_attr(feature = "fuzzer-libfuzzer", no_main)] #![cfg_attr(feature = "fuzzer-libfuzzer", no_main)]
use arbitrary::Unstructured; use arbitrary::Unstructured;
use common::transaction::NSSATransaction; use common::transaction::LeeTransaction;
use fuzz_props::generators::arbitrary_transaction; use fuzz_props::generators::arbitrary_transaction;
fuzz_props::fuzz_entry!(|data: &[u8]| { 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 // 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(); let _ = tx.transaction_stateless_check();
} }
}); });

View File

@ -2,19 +2,19 @@
use common::{ use common::{
block::{Block, HashableBlockData}, block::{Block, HashableBlockData},
transaction::NSSATransaction, transaction::LeeTransaction,
}; };
fuzz_props::fuzz_entry!(|data: &[u8]| { fuzz_props::fuzz_entry!(|data: &[u8]| {
// Attempt 1: decode as NSSATransaction and verify roundtrip // Attempt 1: decode as LeeTransaction and verify roundtrip
if let Ok(tx) = borsh::from_slice::<NSSATransaction>(data) { 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 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"); .expect("second decode of re-encoded tx must succeed");
assert_eq!( assert_eq!(
re_encoded, re_encoded,
borsh::to_vec(&tx2).unwrap(), borsh::to_vec(&tx2).unwrap(),
"NSSATransaction roundtrip encoding divergence" "LeeTransaction roundtrip encoding divergence"
); );
} }

View File

@ -25,7 +25,7 @@
//! reachable by the fuzzer. //! reachable by the fuzzer.
use arbitrary::{Arbitrary, Unstructured}; 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::generators::{arbitrary_fuzz_state, signer_account_ids};
use fuzz_props::invariants::{NonceSnapshot, assert_nonce_increment_correctness}; use fuzz_props::invariants::{NonceSnapshot, assert_nonce_increment_correctness};
use nssa::V03State; use nssa::V03State;
@ -47,7 +47,7 @@ fuzz_props::fuzz_entry!(|data: &[u8]| {
.collect(); .collect();
// Generate the transaction from the remaining fuzz bytes. // 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, Ok(w) => w.0,
Err(_) => return, Err(_) => return,
}; };

View File

@ -2,7 +2,7 @@
//! //!
//! **No changes to `../logos-execution-zone` are required.** //! **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`) //! the trait and the type come from external crates. Using newtypes (`ArbXxx`)
//! sidesteps the restriction entirely. //! sidesteps the restriction entirely.
//! //!
@ -10,10 +10,10 @@
//! //!
//! ```rust,ignore //! ```rust,ignore
//! #![no_main] //! #![no_main]
//! use fuzz_props::arbitrary_types::ArbNSSATransaction; //! use fuzz_props::arbitrary_types::ArbLeeTransaction;
//! use libfuzzer_sys::fuzz_target; //! use libfuzzer_sys::fuzz_target;
//! //!
//! fuzz_target!(|wrapped: ArbNSSATransaction| { //! fuzz_target!(|wrapped: ArbLeeTransaction| {
//! let tx = wrapped.0; //! let tx = wrapped.0;
//! let Ok(valid_tx) = tx.transaction_stateless_check() else { return; }; //! let Ok(valid_tx) = tx.transaction_stateless_check() else { return; };
//! // … //! // …
@ -21,7 +21,7 @@
//! ``` //! ```
use arbitrary::{Arbitrary, Result as ArbResult, Unstructured}; use arbitrary::{Arbitrary, Result as ArbResult, Unstructured};
use common::{HashType, block::HashableBlockData, transaction::NSSATransaction}; use common::{HashType, block::HashableBlockData, transaction::LeeTransaction};
use nssa::{ use nssa::{
AccountId, PrivateKey, PublicKey, Signature, AccountId, PrivateKey, PublicKey, Signature,
program_deployment_transaction::ProgramDeploymentTransaction, program_deployment_transaction::ProgramDeploymentTransaction,
@ -210,24 +210,24 @@ impl<'a> Arbitrary<'a> for ArbProgramDeploymentTransaction {
} }
} }
// ── NSSATransaction ─────────────────────────────────────────────────────────── // ── LeeTransaction ───────────────────────────────────────────────────────────
// `PrivacyPreservingTransaction` is intentionally excluded: it embeds a risc0 // `PrivacyPreservingTransaction` is intentionally excluded: it embeds a risc0
// ZK receipt that cannot be generated inside a hot fuzzing loop. This matches // ZK receipt that cannot be generated inside a hot fuzzing loop. This matches
// the known limitation documented in `docs/fuzzing.md`. // 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. /// Generates `Public` and `ProgramDeployment` variants only.
#[derive(Debug)] #[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> { fn arbitrary(u: &mut Unstructured<'a>) -> ArbResult<Self> {
match u8::arbitrary(u)? % 2 { match u8::arbitrary(u)? % 2 {
0 => Ok(Self(NSSATransaction::Public( 0 => Ok(Self(LeeTransaction::Public(
ArbPublicTransaction::arbitrary(u)?.0, ArbPublicTransaction::arbitrary(u)?.0,
))), ))),
_ => Ok(Self(NSSATransaction::ProgramDeployment( _ => Ok(Self(LeeTransaction::ProgramDeployment(
ArbProgramDeploymentTransaction::arbitrary(u)?.0, ArbProgramDeploymentTransaction::arbitrary(u)?.0,
))), ))),
} }
@ -246,7 +246,7 @@ impl<'a> Arbitrary<'a> for ArbHashableBlockData {
fn arbitrary(u: &mut Unstructured<'a>) -> ArbResult<Self> { fn arbitrary(u: &mut Unstructured<'a>) -> ArbResult<Self> {
// 07 transactions per block // 07 transactions per block
let n = (u8::arbitrary(u)? as usize) % 8; 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) .take(n)
.collect::<ArbResult<Vec<_>>>()?; .collect::<ArbResult<Vec<_>>>()?;
Ok(Self(HashableBlockData { Ok(Self(HashableBlockData {

View File

@ -1,8 +1,8 @@
use arbitrary::{Arbitrary, Unstructured}; use arbitrary::{Arbitrary, Unstructured};
use common::{block::HashableBlockData, transaction::NSSATransaction}; use common::{block::HashableBlockData, transaction::LeeTransaction};
use nssa::{AccountId, PrivateKey}; use nssa::{AccountId, PrivateKey};
use crate::arbitrary_types::{ArbAccountId, ArbNSSATransaction, ArbPrivateKey}; use crate::arbitrary_types::{ArbAccountId, ArbLeeTransaction, ArbPrivateKey};
use proptest::prelude::*; use proptest::prelude::*;
use testnet_initial_state::initial_pub_accounts_private_keys; 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 /// witness set. Used by fuzz targets that need to verify nonce
/// increments after `execute_check_on_state`. /// increments after `execute_check_on_state`.
#[must_use] #[must_use]
pub fn signer_account_ids(tx: &common::transaction::NSSATransaction) -> Vec<nssa::AccountId> { pub fn signer_account_ids(tx: &common::transaction::LeeTransaction) -> Vec<nssa::AccountId> {
use common::transaction::NSSATransaction; use common::transaction::LeeTransaction;
match tx { match tx {
NSSATransaction::Public(pt) => pt LeeTransaction::Public(pt) => pt
.witness_set() .witness_set()
.signatures_and_public_keys() .signatures_and_public_keys()
.iter() .iter()
.map(|(_, pk)| nssa::AccountId::from(pk)) .map(|(_, pk)| nssa::AccountId::from(pk))
.collect(), .collect(),
NSSATransaction::PrivacyPreserving(pt) => pt LeeTransaction::PrivacyPreserving(pt) => pt
.witness_set() .witness_set()
.signatures_and_public_keys() .signatures_and_public_keys()
.iter() .iter()
.map(|(_, pk)| nssa::AccountId::from(pk)) .map(|(_, pk)| nssa::AccountId::from(pk))
.collect(), .collect(),
NSSATransaction::ProgramDeployment(_) => vec![], LeeTransaction::ProgramDeployment(_) => vec![],
} }
} }
@ -74,7 +74,7 @@ pub fn arbitrary_fuzz_state(u: &mut Unstructured<'_>) -> arbitrary::Result<Vec<F
.collect() .collect()
} }
/// Generate a native-transfer [`NSSATransaction`] between two accounts chosen /// Generate a native-transfer [`LeeTransaction`] between two accounts chosen
/// from `accounts`. /// from `accounts`.
/// ///
/// Because every account in the slice has a known private key, the resulting /// 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( pub fn arb_fuzz_native_transfer(
u: &mut Unstructured<'_>, u: &mut Unstructured<'_>,
accounts: &[FuzzAccount], accounts: &[FuzzAccount],
) -> arbitrary::Result<NSSATransaction> { ) -> arbitrary::Result<LeeTransaction> {
if accounts.is_empty() { if accounts.is_empty() {
return Err(arbitrary::Error::IncorrectFormat); return Err(arbitrary::Error::IncorrectFormat);
} }
@ -112,9 +112,9 @@ pub fn arb_fuzz_native_transfer(
// ── Arbitrary (for libFuzzer targets) ──────────────────────────────────────── // ── Arbitrary (for libFuzzer targets) ────────────────────────────────────────
/// Generate a structurally plausible `NSSATransaction` from unstructured bytes. /// Generate a structurally plausible `LeeTransaction` from unstructured bytes.
pub fn arbitrary_transaction(u: &mut Unstructured<'_>) -> arbitrary::Result<NSSATransaction> { pub fn arbitrary_transaction(u: &mut Unstructured<'_>) -> arbitrary::Result<LeeTransaction> {
ArbNSSATransaction::arbitrary(u).map(|w| w.0) ArbLeeTransaction::arbitrary(u).map(|w| w.0)
} }
// ── proptest strategies ─────────────────────────────────────────────────────── // ── proptest strategies ───────────────────────────────────────────────────────
@ -128,7 +128,7 @@ prop_compose! {
to_idx in 0..accounts.len(), to_idx in 0..accounts.len(),
nonce in 0_u128..1_000_u128, nonce in 0_u128..1_000_u128,
amount in 0_u128..10_000_u128, amount in 0_u128..10_000_u128,
) -> NSSATransaction { ) -> LeeTransaction {
let (from_id, from_key) = &accounts[from_idx]; let (from_id, from_key) = &accounts[from_idx];
let (to_id, _) = &accounts[to_idx]; let (to_id, _) = &accounts[to_idx];
common::test_utils::create_transaction_native_token_transfer( common::test_utils::create_transaction_native_token_transfer(
@ -146,11 +146,11 @@ pub fn test_accounts() -> Vec<(AccountId, PrivateKey)> {
.collect() .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>> { pub fn arb_borsh_transaction_bytes() -> impl Strategy<Value = Vec<u8>> {
any::<Vec<u8>>().prop_map(|bytes| { any::<Vec<u8>>().prop_map(|bytes| {
// Either pass through raw bytes OR encode a known dummy transaction // 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 bytes
} else { } else {
borsh::to_vec(&common::test_utils::produce_dummy_empty_transaction()).unwrap() 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..), phantom_id_bytes in proptest::array::uniform32(0_u8..),
amount in (u128::MAX / 2)..u128::MAX, // overflow-inducing amount amount in (u128::MAX / 2)..u128::MAX, // overflow-inducing amount
nonce in 0_u128..10_u128, nonce in 0_u128..10_u128,
) -> NSSATransaction { ) -> LeeTransaction {
let phantom_id = nssa::AccountId::new(phantom_id_bytes); let phantom_id = nssa::AccountId::new(phantom_id_bytes);
// Attempt to sign with a key that has no matching on-chain account // Attempt to sign with a key that has no matching on-chain account
let signing_key = nssa::PrivateKey::try_new(phantom_id_bytes) 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. /// 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 /// Used in proptest-level tests and as a seed generator for the state-transition
/// fuzz target. /// 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(); let accounts = test_accounts();
proptest::collection::vec(arb_native_transfer_tx(accounts), 1..5_usize).prop_flat_map(|txs| { proptest::collection::vec(arb_native_transfer_tx(accounts), 1..5_usize).prop_flat_map(|txs| {
// Build a sequence that: original | duplicates | reversed // Build a sequence that: original | duplicates | reversed
let duped: Vec<NSSATransaction> = txs let duped: Vec<LeeTransaction> = txs
.iter() .iter()
.cloned() .cloned()
.chain(txs.iter().cloned()) // append exact duplicates .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), /// - self-transfers (sender == recipient),
/// - max-nonce wrapping, /// - max-nonce wrapping,
/// - alternating valid / invalid transactions to test partial-batch isolation. /// - 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 accounts = test_accounts();
let n = accounts.len(); let n = accounts.len();
proptest::collection::vec((0..n, 0..n, 0_u128..5_u128, 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(

View File

@ -1,4 +1,4 @@
use common::transaction::NSSATransaction; use common::transaction::LeeTransaction;
use nssa::V03State; use nssa::V03State;
use nssa_core::account::Nonce; use nssa_core::account::Nonce;
@ -185,7 +185,7 @@ impl ProtocolInvariant for FailedTxNonceStability {
/// # Enforcement /// # Enforcement
/// ///
/// This invariant **cannot** be enforced through [`InvariantCtx`] because the replay /// 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 /// consumes and returns on `Ok`. It is therefore **not registered** in
/// [`assert_invariants`]; calling `assert_invariants` alone does **not** cover /// [`assert_invariants`]; calling `assert_invariants` alone does **not** cover
/// `ReplayRejection`. /// `ReplayRejection`.
@ -235,7 +235,7 @@ pub struct NonceIncrementCorrectness;
/// ///
/// # Why a standalone function? /// # 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`]. /// so the transaction is not available as a shared reference inside [`InvariantCtx`].
/// This function accepts ownership of the returned transaction and performs the /// This function accepts ownership of the returned transaction and performs the
/// replay in-place. /// replay in-place.
@ -249,7 +249,7 @@ pub struct NonceIncrementCorrectness;
/// } /// }
/// ``` /// ```
pub fn assert_replay_rejection( pub fn assert_replay_rejection(
applied_tx: NSSATransaction, applied_tx: LeeTransaction,
state: &mut V03State, state: &mut V03State,
next_block_id: u64, next_block_id: u64,
next_timestamp: 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`] /// passing the signer IDs derived from the transaction's witness set, the [`NonceSnapshot`]
/// captured **before** execution, and the post-execution state. /// 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 /// ```rust,ignore
/// let signer_ids: Vec<nssa::AccountId> = tx /// let signer_ids: Vec<nssa::AccountId> = tx
@ -281,7 +281,7 @@ pub fn assert_replay_rejection(
/// .collect(); /// .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? /// # Why a standalone function?
/// ///
@ -377,7 +377,7 @@ pub fn assert_tx_execution_invariants<E>(
state_after: &mut V03State, state_after: &mut V03State,
balances_before: BalanceSnapshot, balances_before: BalanceSnapshot,
nonces_before: NonceSnapshot, nonces_before: NonceSnapshot,
execution_result: Result<NSSATransaction, E>, execution_result: Result<LeeTransaction, E>,
replay_context: (u64, u64), replay_context: (u64, u64),
) { ) {
let execution_succeeded = execution_result.is_ok(); 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 { if let Ok(applied_tx) = execution_result {
// Derive signer IDs from the witness set. ProgramDeployment has no signers. // Derive signer IDs from the witness set. ProgramDeployment has no signers.
let signer_ids: Vec<nssa::AccountId> = match &applied_tx { let signer_ids: Vec<nssa::AccountId> = match &applied_tx {
NSSATransaction::Public(pt) => pt LeeTransaction::Public(pt) => pt
.witness_set() .witness_set()
.signatures_and_public_keys() .signatures_and_public_keys()
.iter() .iter()
.map(|(_, pk)| nssa::AccountId::from(pk)) .map(|(_, pk)| nssa::AccountId::from(pk))
.collect(), .collect(),
NSSATransaction::PrivacyPreserving(pt) => pt LeeTransaction::PrivacyPreserving(pt) => pt
.witness_set() .witness_set()
.signatures_and_public_keys() .signatures_and_public_keys()
.iter() .iter()
.map(|(_, pk)| nssa::AccountId::from(pk)) .map(|(_, pk)| nssa::AccountId::from(pk))
.collect(), .collect(),
NSSATransaction::ProgramDeployment(_) => vec![], LeeTransaction::ProgramDeployment(_) => vec![],
}; };
assert_nonce_increment_correctness(&signer_ids, &nonces_for_nonce_check, state_after); assert_nonce_increment_correctness(&signer_ids, &nonces_for_nonce_check, state_after);
let (next_block_id, next_timestamp) = replay_context; let (next_block_id, next_timestamp) = replay_context;