small refactor

This commit is contained in:
Sergio Chouhy 2026-04-02 17:40:58 -03:00
parent aa157bfbe7
commit 29d66d2c2d
17 changed files with 63 additions and 68 deletions

1
Cargo.lock generated
View File

@ -7146,6 +7146,7 @@ dependencies = [
"borsh",
"bytesize",
"chrono",
"clock_core",
"common",
"futures",
"humantime-serde",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -47,17 +47,6 @@ impl PrivacyPreservingTransaction {
.collect()
}
/// Returns the post-state the transaction declares for `account_id`, or `None` if the account
/// is not part of this transaction's public execution.
#[must_use]
pub fn public_post_state_for(&self, account_id: &AccountId) -> Option<&Account> {
self.message
.public_account_ids
.iter()
.position(|id| id == account_id)
.map(|i| &self.message.public_post_states[i])
}
#[must_use]
pub fn affected_public_account_ids(&self) -> Vec<AccountId> {
let mut acc_set = self

View File

@ -68,7 +68,7 @@ pub mod tests {
error::NssaError,
program::Program,
public_transaction::{Message, WitnessSet},
state_diff::ValidatedStateDiff,
validated_state_diff::ValidatedStateDiff,
};
fn keys_for_tests() -> (PrivateKey, PrivateKey, AccountId, AccountId) {

View File

@ -186,19 +186,20 @@ impl V03State {
}
pub fn apply_state_diff(&mut self, diff: ValidatedStateDiff) {
let (signer_account_ids, public_diff, new_commitments, new_nullifiers, program) = diff.into_parts();
#[expect(
clippy::iter_over_hash_type,
reason = "Iteration order doesn't matter here"
)]
for (account_id, account) in diff.public_diff {
for (account_id, account) in public_diff {
*self.get_account_by_id_mut(account_id) = account;
}
for account_id in diff.signer_account_ids {
for account_id in signer_account_ids {
self.get_account_by_id_mut(account_id).nonce.public_account_nonce_increment();
}
self.private_state.0.extend(&diff.new_commitments);
self.private_state.1.extend(&diff.new_nullifiers);
if let Some(program) = diff.program {
self.private_state.0.extend(&new_commitments);
self.private_state.1.extend(&new_nullifiers);
if let Some(program) =program {
self.insert_program(program);
}
}

View File

@ -29,11 +29,11 @@ use crate::{
/// Can only be constructed by the transaction validation functions inside this crate, ensuring the
/// diff has been cryptographically checked before any state mutation occurs.
pub struct ValidatedStateDiff {
pub(crate) signer_account_ids: Vec<AccountId>,
pub(crate) public_diff: HashMap<AccountId, Account>,
pub(crate) new_commitments: Vec<Commitment>,
pub(crate) new_nullifiers: Vec<Nullifier>,
pub(crate) program: Option<Program>,
signer_account_ids: Vec<AccountId>,
public_diff: HashMap<AccountId, Account>,
new_commitments: Vec<Commitment>,
new_nullifiers: Vec<Nullifier>,
program: Option<Program>,
}
impl ValidatedStateDiff {
@ -385,9 +385,19 @@ impl ValidatedStateDiff {
/// Used by callers (e.g. the sequencer) to inspect the diff before committing it, for example
/// to enforce that system accounts are not modified by user transactions.
#[must_use]
pub fn public_diff(&self) -> &HashMap<AccountId, Account> {
&self.public_diff
pub fn public_diff(&self) -> HashMap<AccountId, Account> {
self.public_diff.clone()
}
pub(crate) fn into_parts(self) -> (
Vec<AccountId>,
HashMap<AccountId, Account>,
Vec<Commitment>,
Vec<Nullifier>,
Option<Program>,
) {
(self.signer_account_ids, self.public_diff, self.new_commitments, self.new_nullifiers, self.program)
}
}
fn check_privacy_preserving_circuit_proof_is_valid(

View File

@ -41,3 +41,4 @@ mock = []
[dev-dependencies]
futures.workspace = true
test_program_methods.workspace = true
clock_core.workspace = true

View File

@ -450,6 +450,8 @@ mod tests {
use common::{test_utils::sequencer_sign_key_for_testing, transaction::NSSATransaction};
use logos_blockchain_core::mantle::ops::channel::ChannelId;
use mempool::MemPoolHandle;
use nssa::{execute_and_prove, program::Program};
use nssa_core::account::AccountWithMetadata;
use testnet_initial_state::{initial_accounts, initial_pub_accounts_private_keys};
use crate::{
@ -979,50 +981,6 @@ mod tests {
);
}
#[tokio::test]
async fn privacy_preserving_tx_touching_clock_account_is_dropped() {
let (mut sequencer, mempool_handle) = common_setup().await;
// Craft a PP transaction that declares a modified post-state for a clock account.
let crafted_pp_tx = {
let message = nssa::privacy_preserving_transaction::Message {
public_account_ids: vec![nssa::CLOCK_01_PROGRAM_ACCOUNT_ID],
nonces: vec![],
public_post_states: vec![nssa::Account::default()],
encrypted_private_post_states: vec![],
new_commitments: vec![nssa_core::Commitment::new([0_u8; 32])], // required: at least one commitment or nullifier
new_nullifiers: vec![],
block_validity_window: (..).into(),
timestamp_validity_window: (..).into(),
};
let witness_set = nssa::privacy_preserving_transaction::WitnessSet::from_raw_parts(
vec![],
nssa::privacy_preserving_transaction::circuit::Proof::from_inner(vec![]),
);
NSSATransaction::PrivacyPreserving(nssa::PrivacyPreservingTransaction::new(
message,
witness_set,
))
};
mempool_handle.push(crafted_pp_tx).await.unwrap();
sequencer
.produce_new_block_with_mempool_transactions()
.unwrap();
let block = sequencer
.store
.get_block_at_id(sequencer.chain_height)
.unwrap()
.unwrap();
// The PP tx was dropped. Only the system-appended clock tx remains.
assert_eq!(
block.body.transactions,
vec![NSSATransaction::clock_invocation(block.header.timestamp)]
);
}
#[tokio::test]
async fn start_from_config_uses_db_height_not_config_genesis() {
let mut config = setup_sequencer_config();

View File

@ -0,0 +1,35 @@
use nssa_core::program::{
AccountPostState, ChainedCall, ProgramId, ProgramInput, ProgramOutput, read_nssa_inputs,
};
use risc0_zkvm::serde::to_vec;
type Instruction = (ProgramId, u64); // (clock_program_id, timestamp)
/// A program that chain-calls the clock program with the clock accounts it received as pre-states.
/// Used in tests to verify that user transactions cannot modify clock accounts, even indirectly
/// via chain calls.
fn main() {
let (
ProgramInput {
pre_states,
instruction: (clock_program_id, timestamp),
},
instruction_words,
) = read_nssa_inputs::<Instruction>();
let post_states: Vec<_> = pre_states
.iter()
.map(|pre| AccountPostState::new(pre.account.clone()))
.collect();
let chained_call = ChainedCall {
program_id: clock_program_id,
instruction_data: to_vec(&timestamp).unwrap(),
pre_states: pre_states.clone(),
pda_seeds: vec![],
};
ProgramOutput::new(instruction_words, pre_states, post_states)
.with_chained_calls(vec![chained_call])
.write();
}