update from main

This commit is contained in:
Marvin Jones 2026-05-29 09:43:30 -04:00
commit 9981d0dee6
46 changed files with 34 additions and 9 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -7,8 +7,9 @@ use crate::account::{Account, AccountId};
/// A commitment to all zero data.
/// ```python
/// from hashlib import sha256
/// prefix = b"/LEE/v0.3/Commitment/\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
/// hasher = sha256()
/// hasher.update(bytes([0] * 32 + [0] * 32 + [0] * 16 + [0] * 16 + list(sha256().digest())))
/// hasher.update(prefix + bytes([0] * 32 + [0] * 32 + [0] * 16 + [0] * 16 + list(sha256().digest())))
/// DUMMY_COMMITMENT = hasher.digest()
/// ```
pub const DUMMY_COMMITMENT: Commitment = Commitment([

View File

@ -96,6 +96,9 @@ pub enum InvalidProgramBehaviorError {
#[error("Unauthorized account marked as authorized")]
InvalidAccountAuthorization { account_id: AccountId },
#[error("Authorized account marked as not authorized")]
AuthorizedAccountMarkedAsNotAuthorized { account_id: AccountId },
#[error("Program ID mismatch: expected {expected:?}, actual {actual:?}")]
MismatchedProgramId {
expected: ProgramId,

View File

@ -173,12 +173,18 @@ impl ValidatedStateDiff {
);
// Check that the program output pre_states marked as authorized are indeed
// authorized.
// authorized, and vice-versa.
let is_indeed_authorized = is_authorized(&account_id);
ensure!(
!pre.is_authorized || is_indeed_authorized,
InvalidProgramBehaviorError::InvalidAccountAuthorization { account_id }
);
ensure!(
pre.is_authorized || !is_indeed_authorized,
InvalidProgramBehaviorError::AuthorizedAccountMarkedAsNotAuthorized {
account_id
}
);
}
// Verify that the program output's self_program_id matches the expected program ID.
@ -269,11 +275,20 @@ impl ValidatedStateDiff {
// the loop above already gates program_output's `is_authorized` via the
// `!pre.is_authorized || is_indeed_authorized` check, while `chained_call.
// pre_states` is caller-controlled and can be forged (audit-issue 91).
let authorized_accounts: HashSet<_> = program_output
.pre_states
.iter()
.filter(|pre| pre.is_authorized)
.map(|pre| pre.account_id)
//
// Union with the caller's authorized set so that authorization is monotonically
// growing: once an account is authorized at any point in the chain it remains
// authorized for all subsequent calls.
let authorized_accounts: HashSet<_> = caller_data
.authorized_accounts
.into_iter()
.chain(
program_output
.pre_states
.iter()
.filter(|pre| pre.is_authorized)
.map(|pre| pre.account_id),
)
.collect();
for new_call in program_output.chained_calls.into_iter().rev() {
chained_calls.push_front((
@ -341,7 +356,13 @@ impl ValidatedStateDiff {
// Check there are no duplicate nullifiers in the new_nullifiers list
ensure!(
n_unique(&message.new_nullifiers) == message.new_nullifiers.len(),
n_unique(
&message
.new_nullifiers
.iter()
.map(|(n, _)| n)
.collect::<Vec<_>>()
) == message.new_nullifiers.len(),
NssaError::InvalidInput("Duplicate nullifiers found in message".into())
);

View File

@ -62,7 +62,7 @@ pub fn compute_circuit_output(
Nullifier::for_account_initialization(&account_id),
DUMMY_COMMITMENT_HASH,
);
let new_nonce = pre_state.account.nonce.private_account_nonce_increment(nsk);
let new_nonce = Nonce::private_account_nonce_init(&account_id);
emit_private_output(
&mut output,