fix: apply formatting and rebuild artifacts

This commit is contained in:
Moudy 2026-04-03 01:17:42 +02:00
parent 88e3b368c3
commit 74e16db68f
54 changed files with 319 additions and 91 deletions

1
Cargo.lock generated
View File

@ -7833,6 +7833,7 @@ version = "0.1.0"
dependencies = [
"nssa_core",
"risc0-zkvm",
"serde",
]
[[package]]

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -18,7 +18,9 @@ type Instruction = Vec<u8>;
fn main() {
// Read inputs
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: greeting,
},
@ -50,5 +52,11 @@ fn main() {
// with the NSSA program rules.
// WARNING: constructing a `ProgramOutput` has no effect on its own. `.write()` must be
// called to commit the output.
ProgramOutput::new(self_program_id, instruction_data, vec![pre_state], vec![post_state]).write();
ProgramOutput::new(
self_program_id,
instruction_data,
vec![pre_state],
vec![post_state],
)
.write();
}

View File

@ -18,7 +18,9 @@ type Instruction = Vec<u8>;
fn main() {
// Read inputs
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: greeting,
},
@ -57,5 +59,11 @@ fn main() {
// with the NSSA program rules.
// WARNING: constructing a `ProgramOutput` has no effect on its own. `.write()` must be
// called to commit the output.
ProgramOutput::new(self_program_id, instruction_data, vec![pre_state], vec![post_state]).write();
ProgramOutput::new(
self_program_id,
instruction_data,
vec![pre_state],
vec![post_state],
)
.write();
}

View File

@ -65,7 +65,9 @@ fn move_data(from_pre: AccountWithMetadata, to_pre: AccountWithMetadata) -> Vec<
fn main() {
// Read input accounts.
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: (function_id, data),
},

View File

@ -26,7 +26,9 @@ fn hello_world_program_id() -> ProgramId {
fn main() {
// Read inputs
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: (),
},
@ -55,7 +57,12 @@ fn main() {
// Write the outputs.
// WARNING: constructing a `ProgramOutput` has no effect on its own. `.write()` must be
// called to commit the output.
ProgramOutput::new(self_program_id, instruction_data, vec![pre_state], vec![post_state])
.with_chained_calls(vec![chained_call])
.write();
ProgramOutput::new(
self_program_id,
instruction_data,
vec![pre_state],
vec![post_state],
)
.with_chained_calls(vec![chained_call])
.write();
}

View File

@ -32,7 +32,9 @@ fn hello_world_program_id() -> ProgramId {
fn main() {
// Read inputs
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: (),
},
@ -68,7 +70,12 @@ fn main() {
// Write the outputs.
// WARNING: constructing a `ProgramOutput` has no effect on its own. `.write()` must be
// called to commit the output.
ProgramOutput::new(self_program_id, instruction_data, vec![pre_state], vec![post_state])
.with_chained_calls(vec![chained_call])
.write();
ProgramOutput::new(
self_program_id,
instruction_data,
vec![pre_state],
vec![post_state],
)
.with_chained_calls(vec![chained_call])
.write();
}

View File

@ -160,7 +160,13 @@ fn execute_and_prove_program(
) -> Result<Receipt, NssaError> {
// Write inputs to the program
let mut env_builder = ExecutorEnv::builder();
Program::write_inputs(program.id(), caller_program_id, pre_states, instruction_data, &mut env_builder)?;
Program::write_inputs(
program.id(),
caller_program_id,
pre_states,
instruction_data,
&mut env_builder,
)?;
let env = env_builder.build().unwrap();
// Prove the program

View File

@ -59,7 +59,13 @@ impl Program {
// Write inputs to the program
let mut env_builder = ExecutorEnv::builder();
env_builder.session_limit(Some(MAX_NUM_CYCLES_PUBLIC_EXECUTION));
Self::write_inputs(self.id, caller_program_id, pre_states, instruction_data, &mut env_builder)?;
Self::write_inputs(
self.id,
caller_program_id,
pre_states,
instruction_data,
&mut env_builder,
)?;
let env = env_builder.build().unwrap();
// Execute the program (without proving)

View File

@ -147,8 +147,11 @@ impl PublicTransaction {
"Program {:?} pre_states: {:?}, instruction_data: {:?}",
chained_call.program_id, chained_call.pre_states, chained_call.instruction_data
);
let mut program_output =
program.execute(caller_program_id, &chained_call.pre_states, &chained_call.instruction_data)?;
let mut program_output = program.execute(
caller_program_id,
&chained_call.pre_states,
&chained_call.instruction_data,
)?;
debug!(
"Program {:?} output: {:?}",
chained_call.program_id, program_output

View File

@ -3523,7 +3523,7 @@ pub mod tests {
let message = public_transaction::Message::try_new(
initiator.id(),
vec![vault_id, receiver_id],
vec![], // no signers — vault is PDA-authorised
vec![], // no signers — vault is PDA-authorised
instruction,
)
.unwrap();
@ -3561,18 +3561,33 @@ pub mod tests {
// Pre-simulated intermediate states:
// After transfer (vault→receiver, amount_out):
let vault_after_transfer = AccountWithMetadata::new(
Account { program_owner: token.id(), balance: initial_balance - amount_out, ..Account::default() },
false, vault_id,
Account {
program_owner: token.id(),
balance: initial_balance - amount_out,
..Account::default()
},
false,
vault_id,
);
let receiver_after_transfer = AccountWithMetadata::new(
Account { program_owner: token.id(), balance: amount_out, ..Account::default() },
false, receiver_id,
Account {
program_owner: token.id(),
balance: amount_out,
..Account::default()
},
false,
receiver_id,
);
// After callback returns funds (receiver→vault, amount_out):
let vault_after_callback = AccountWithMetadata::new(
Account { program_owner: token.id(), balance: initial_balance, ..Account::default() },
false, vault_id,
Account {
program_owner: token.id(),
balance: initial_balance,
..Account::default()
},
false,
vault_id,
);
// Callback instruction: return funds
@ -3581,12 +3596,22 @@ pub mod tests {
token_program_id: token.id(),
amount: amount_out,
vault_after_return: Some(AccountWithMetadata::new(
Account { program_owner: token.id(), balance: initial_balance, ..Account::default() },
false, vault_id,
Account {
program_owner: token.id(),
balance: initial_balance,
..Account::default()
},
false,
vault_id,
)),
receiver_after_return: Some(AccountWithMetadata::new(
Account { program_owner: token.id(), balance: 0, ..Account::default() },
false, receiver_id,
Account {
program_owner: token.id(),
balance: 0,
..Account::default()
},
false,
receiver_id,
)),
};
let cb_data = Program::serialize_instruction(cb_instruction).unwrap();
@ -3639,18 +3664,33 @@ pub mod tests {
// Pre-simulated intermediate states (same as successful case for steps 1-2):
let vault_after_transfer = AccountWithMetadata::new(
Account { program_owner: token.id(), balance: initial_balance - amount_out, ..Account::default() },
false, vault_id,
Account {
program_owner: token.id(),
balance: initial_balance - amount_out,
..Account::default()
},
false,
vault_id,
);
let receiver_after_transfer = AccountWithMetadata::new(
Account { program_owner: token.id(), balance: amount_out, ..Account::default() },
false, receiver_id,
Account {
program_owner: token.id(),
balance: amount_out,
..Account::default()
},
false,
receiver_id,
);
// After callback that does NOT return funds — vault stays drained:
let vault_after_callback = AccountWithMetadata::new(
Account { program_owner: token.id(), balance: initial_balance - amount_out, ..Account::default() },
false, vault_id,
Account {
program_owner: token.id(),
balance: initial_balance - amount_out,
..Account::default()
},
false,
vault_id,
);
// Callback instruction: do NOT return funds
@ -3677,7 +3717,10 @@ pub mod tests {
let result = state.transition_from_public_transaction(&tx, 1, 0);
// Invariant check fails → entire tx rolls back
assert!(result.is_err(), "flash swap should fail when callback keeps funds");
assert!(
result.is_err(),
"flash swap should fail when callback keeps funds"
);
// State unchanged (rollback)
assert_eq!(state.get_account_by_id(vault_id).balance, initial_balance);
@ -3714,17 +3757,32 @@ pub mod tests {
// Zero-amount transfer: states remain unchanged after transfer
let vault_after_transfer = AccountWithMetadata::new(
Account { program_owner: token.id(), balance: initial_balance, ..Account::default() },
false, vault_id,
Account {
program_owner: token.id(),
balance: initial_balance,
..Account::default()
},
false,
vault_id,
);
let receiver_after_transfer = AccountWithMetadata::new(
Account { program_owner: token.id(), balance: 0, ..Account::default() },
false, receiver_id,
Account {
program_owner: token.id(),
balance: 0,
..Account::default()
},
false,
receiver_id,
);
// Callback with zero amount, return_funds=true (no-op effectively)
let vault_after_callback = AccountWithMetadata::new(
Account { program_owner: token.id(), balance: initial_balance, ..Account::default() },
false, vault_id,
Account {
program_owner: token.id(),
balance: initial_balance,
..Account::default()
},
false,
vault_id,
);
let cb_instruction = CallbackInstruction {
@ -3732,12 +3790,22 @@ pub mod tests {
token_program_id: token.id(),
amount: 0,
vault_after_return: Some(AccountWithMetadata::new(
Account { program_owner: token.id(), balance: initial_balance, ..Account::default() },
false, vault_id,
Account {
program_owner: token.id(),
balance: initial_balance,
..Account::default()
},
false,
vault_id,
)),
receiver_after_return: Some(AccountWithMetadata::new(
Account { program_owner: token.id(), balance: 0, ..Account::default() },
false, receiver_id,
Account {
program_owner: token.id(),
balance: 0,
..Account::default()
},
false,
receiver_id,
)),
};
let cb_data = Program::serialize_instruction(cb_instruction).unwrap();
@ -3754,7 +3822,10 @@ pub mod tests {
let tx = build_flash_swap_tx(&initiator, vault_id, receiver_id, instruction);
let result = state.transition_from_public_transaction(&tx, 1, 0);
assert!(result.is_ok(), "zero-amount flash swap should succeed: {result:?}");
assert!(
result.is_ok(),
"zero-amount flash swap should succeed: {result:?}"
);
}
#[test]

View File

@ -13,7 +13,9 @@ use nssa_core::program::{ProgramInput, ProgramOutput, read_nssa_inputs};
fn main() {
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction,
},
@ -152,7 +154,12 @@ fn main() {
}
};
ProgramOutput::new(self_program_id, instruction_words, pre_states_clone, post_states)
.with_chained_calls(chained_calls)
.write();
ProgramOutput::new(
self_program_id,
instruction_words,
pre_states_clone,
post_states,
)
.with_chained_calls(chained_calls)
.write();
}

View File

@ -3,7 +3,9 @@ use nssa_core::program::{ProgramInput, ProgramOutput, read_nssa_inputs};
fn main() {
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction,
},
@ -56,7 +58,12 @@ fn main() {
}
};
ProgramOutput::new(self_program_id, instruction_words, pre_states_clone, post_states)
.with_chained_calls(chained_calls)
.write();
ProgramOutput::new(
self_program_id,
instruction_words,
pre_states_clone,
post_states,
)
.with_chained_calls(chained_calls)
.write();
}

View File

@ -66,7 +66,9 @@ fn transfer(
fn main() {
// Read input accounts.
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: balance_to_move,
},

View File

@ -45,7 +45,9 @@ fn main() {
// Read input accounts.
// It is expected to receive only two accounts: [pinata_account, winner_account]
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: solution,
},

View File

@ -51,7 +51,9 @@ fn main() {
// It is expected to receive three accounts: [pinata_definition, pinata_token_holding,
// winner_token_holding]
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: solution,
},

View File

@ -11,7 +11,9 @@ use token_program::core::Instruction;
fn main() {
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction,
},
@ -81,5 +83,11 @@ fn main() {
}
};
ProgramOutput::new(self_program_id, instruction_words, pre_states_clone, post_states).write();
ProgramOutput::new(
self_program_id,
instruction_words,
pre_states_clone,
post_states,
)
.write();
}

View File

@ -4,7 +4,9 @@ type Instruction = u128;
fn main() {
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: balance_to_burn,
},

View File

@ -12,7 +12,9 @@ type Instruction = (u128, ProgramId, u32, Option<PdaSeed>);
/// program.
fn main() {
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: (balance, auth_transfer_id, num_chain_calls, pda_seed),
},

View File

@ -5,7 +5,9 @@ type Instruction = (Option<Vec<u8>>, bool);
/// A program that optionally modifies the account data and optionally claims it.
fn main() {
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: (data_opt, should_claim),
},
@ -33,5 +35,11 @@ fn main() {
AccountPostState::new(account_post)
};
ProgramOutput::new(self_program_id, instruction_words, vec![pre], vec![post_state]).write();
ProgramOutput::new(
self_program_id,
instruction_words,
vec![pre],
vec![post_state],
)
.write();
}

View File

@ -4,7 +4,9 @@ type Instruction = ();
fn main() {
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: (),
},
@ -17,5 +19,11 @@ fn main() {
let account_post = AccountPostState::new_claimed(pre.account.clone(), Claim::Authorized);
ProgramOutput::new(self_program_id, instruction_words, vec![pre], vec![account_post]).write();
ProgramOutput::new(
self_program_id,
instruction_words,
vec![pre],
vec![account_post],
)
.write();
}

View File

@ -5,7 +5,9 @@ type Instruction = Vec<u8>;
/// A program that modifies the account data by setting bytes sent in instruction.
fn main() {
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: data,
},

View File

@ -6,7 +6,15 @@ use nssa_core::{
type Instruction = ();
fn main() {
let (ProgramInput { self_program_id, caller_program_id: _, pre_states, .. }, instruction_words) = read_nssa_inputs::<Instruction>();
let (
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
..
},
instruction_words,
) = read_nssa_inputs::<Instruction>();
let Ok([pre]) = <[_; 1]>::try_from(pre_states) else {
return;

View File

@ -10,12 +10,12 @@
//! In a real flash swap, this would contain the user's arbitrage or other logic.
//! In this test program, it is controlled by `return_funds`:
//!
//! - `return_funds = true`: emits a token transfer (receiver → vault) to return the funds.
//! The invariant check will pass and the transaction will succeed.
//! - `return_funds = true`: emits a token transfer (receiver → vault) to return the funds. The
//! invariant check will pass and the transaction will succeed.
//!
//! - `return_funds = false`: emits no transfers. Funds stay with the receiver.
//! The invariant check will fail (vault balance < initial), causing full atomic rollback.
//! This simulates a malicious or buggy callback that does not repay the flash loan.
//! - `return_funds = false`: emits no transfers. Funds stay with the receiver. The invariant check
//! will fail (vault balance < initial), causing full atomic rollback. This simulates a malicious
//! or buggy callback that does not repay the flash loan.
//!
//! # Note on caller_program_id
//!

View File

@ -16,18 +16,18 @@
//! 2. User callback (arbitrary logic, e.g. arbitrage)
//! 3. Self-call to `InvariantCheck` (using `self_program_id` to reference itself)
//!
//! - `InvariantCheck` (internal): enforces that the vault balance was restored after
//! the callback. Uses `caller_program_id == Some(self_program_id)` to prevent standalone
//! calls (this is the visibility enforcement mechanism).
//! - `InvariantCheck` (internal): enforces that the vault balance was restored after the callback.
//! Uses `caller_program_id == Some(self_program_id)` to prevent standalone calls (this is the
//! visibility enforcement mechanism).
//!
//! # What this demonstrates
//!
//! - `self_program_id`: enables a program to chain back to itself (step 3 above)
//! - `caller_program_id`: enables a program to restrict which callers can invoke an instruction
//! - Pre-simulated intermediate states: the initiator must compute expected intermediate
//! account states and embed them in the instruction. The node validates them deterministically.
//! - Atomic rollback: if the callback doesn't return funds, the invariant check fails,
//! and all state changes from steps 1 and 2 are rolled back automatically.
//! - Pre-simulated intermediate states: the initiator must compute expected intermediate account
//! states and embed them in the instruction. The node validates them deterministically.
//! - Atomic rollback: if the callback doesn't return funds, the invariant check fails, and all
//! state changes from steps 1 and 2 are rolled back automatically.
//!
//! # Tests
//!
@ -132,8 +132,9 @@ fn main() {
// Chained call 3: Self-call to enforce the invariant.
// Uses `self_program_id` to reference this program, the key feature that enables
// the "prep → callback → assert" pattern without a separate checker program.
// If the callback did not return funds, vault_after_callback.balance < min_vault_balance
// and this call will panic, rolling back the entire transaction.
// If the callback did not return funds, vault_after_callback.balance <
// min_vault_balance and this call will panic, rolling back the entire
// transaction.
let invariant_instruction =
risc0_zkvm::serde::to_vec(&FlashSwapInstruction::InvariantCheck {
min_vault_balance,

View File

@ -13,7 +13,9 @@ type Instruction = (u128, ProgramId);
/// but sets the `is_authorized` field of the first account to true.
fn main() {
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: (balance, transfer_program_id),
},

View File

@ -3,7 +3,15 @@ use nssa_core::program::{AccountPostState, ProgramInput, ProgramOutput, read_nss
type Instruction = ();
fn main() {
let (ProgramInput { self_program_id, caller_program_id: _, pre_states, .. }, instruction_words) = read_nssa_inputs::<Instruction>();
let (
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
..
},
instruction_words,
) = read_nssa_inputs::<Instruction>();
let Ok([pre]) = <[_; 1]>::try_from(pre_states) else {
return;

View File

@ -3,7 +3,15 @@ use nssa_core::program::{AccountPostState, ProgramInput, ProgramOutput, read_nss
type Instruction = ();
fn main() {
let (ProgramInput { self_program_id, caller_program_id: _, pre_states, .. }, instruction_words) = read_nssa_inputs::<Instruction>();
let (
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
..
},
instruction_words,
) = read_nssa_inputs::<Instruction>();
let Ok([pre1, pre2]) = <[_; 2]>::try_from(pre_states) else {
return;

View File

@ -63,7 +63,9 @@ fn transfer(
fn main() {
// Read input accounts.
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: balance_to_move,
},

View File

@ -3,7 +3,15 @@ use nssa_core::program::{AccountPostState, ProgramInput, ProgramOutput, read_nss
type Instruction = ();
fn main() {
let (ProgramInput { self_program_id, caller_program_id: _, pre_states, .. }, instruction_words) = read_nssa_inputs::<Instruction>();
let (
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
..
},
instruction_words,
) = read_nssa_inputs::<Instruction>();
let Ok([pre]) = <[_; 1]>::try_from(pre_states) else {
return;

View File

@ -3,7 +3,15 @@ use nssa_core::program::{AccountPostState, ProgramInput, ProgramOutput, read_nss
type Instruction = ();
fn main() {
let (ProgramInput { self_program_id, caller_program_id: _, pre_states, .. }, instruction_words) = read_nssa_inputs::<Instruction>();
let (
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
..
},
instruction_words,
) = read_nssa_inputs::<Instruction>();
let post_states = pre_states
.iter()

View File

@ -3,7 +3,15 @@ use nssa_core::program::{AccountPostState, ProgramInput, ProgramOutput, read_nss
type Instruction = ();
fn main() {
let (ProgramInput { self_program_id, caller_program_id: _, pre_states, .. }, instruction_words) = read_nssa_inputs::<Instruction>();
let (
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
..
},
instruction_words,
) = read_nssa_inputs::<Instruction>();
let Ok([pre]) = <[_; 1]>::try_from(pre_states) else {
return;

View File

@ -4,7 +4,9 @@ type Instruction = u128;
fn main() {
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: balance,
},

View File

@ -7,7 +7,9 @@ type Instruction = (BlockValidityWindow, TimestampValidityWindow);
fn main() {
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: (block_validity_window, timestamp_validity_window),
},

View File

@ -15,7 +15,9 @@ type Instruction = (BlockValidityWindow, ProgramId, BlockValidityWindow);
fn main() {
let (
ProgramInput { self_program_id, caller_program_id: _,
ProgramInput {
self_program_id,
caller_program_id: _,
pre_states,
instruction: (block_validity_window, chained_program_id, chained_block_validity_window),
},