Merge pull request #240 from logos-blockchain/arjentix/private-acc-init-with-is-authorized

Allow private authorized uninitialized accounts
This commit is contained in:
Daniil Polyakov 2025-12-19 01:39:15 +03:00 committed by GitHub
commit 2b15bed3fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 397 additions and 83 deletions

View File

@ -1681,6 +1681,49 @@ pub fn prepare_function_map() -> HashMap<String, TestFunction> {
info!("Success!");
}
#[nssa_integration_test]
pub async fn test_authenticated_transfer_initialize_function_private() {
info!("########## test initialize private account for authenticated transfer ##########");
let command =
Command::Account(AccountSubcommand::New(NewSubcommand::Private { cci: None }));
let SubcommandReturnValue::RegisterAccount { account_id } =
wallet::cli::execute_subcommand(command).await.unwrap()
else {
panic!("Error creating account");
};
let command = Command::AuthTransfer(AuthTransferSubcommand::Init {
account_id: make_private_account_input_from_str(&account_id.to_string()),
});
wallet::cli::execute_subcommand(command).await.unwrap();
tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await;
info!("Checking correct execution");
let command = Command::Account(AccountSubcommand::SyncPrivate {});
wallet::cli::execute_subcommand(command).await.unwrap();
let wallet_config = fetch_config().await.unwrap();
let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap();
let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config)
.await
.unwrap();
let new_commitment1 = wallet_storage
.get_private_account_commitment(&account_id)
.unwrap();
assert!(verify_commitment_is_in_state(new_commitment1, &seq_client).await);
let account = wallet_storage.get_account_private(&account_id).unwrap();
let expected_program_owner = Program::authenticated_transfer_program().id();
let expected_balance = 0;
assert_eq!(account.program_owner, expected_program_owner);
assert_eq!(account.balance, expected_balance);
assert!(account.data.is_empty());
}
#[nssa_integration_test]
pub async fn test_pinata_private_receiver() {
info!("########## test_pinata_private_receiver ##########");

View File

@ -167,7 +167,8 @@ fn build_privacy_transaction() -> PrivacyPreservingTransaction {
(sender_npk.clone(), sender_ss),
(recipient_npk.clone(), recipient_ss),
],
&[(sender_nsk, proof)],
&[sender_nsk],
&[Some(proof)],
&program.into(),
)
.unwrap();

View File

@ -10,11 +10,23 @@ use crate::{
#[derive(Serialize, Deserialize)]
pub struct PrivacyPreservingCircuitInput {
/// Outputs of the program execution.
pub program_outputs: Vec<ProgramOutput>,
/// Visibility mask for accounts.
///
/// - `0` - public account
/// - `1` - private account with authentication
/// - `2` - private account without authentication
pub visibility_mask: Vec<u8>,
/// Nonces of private accounts.
pub private_account_nonces: Vec<Nonce>,
/// Public keys of private accounts.
pub private_account_keys: Vec<(NullifierPublicKey, SharedSecretKey)>,
pub private_account_auth: Vec<(NullifierSecretKey, MembershipProof)>,
/// Nullifier secret keys for authorized private accounts.
pub private_account_nsks: Vec<NullifierSecretKey>,
/// Membership proofs for private accounts. Can be [`None`] for uninitialized accounts.
pub private_account_membership_proofs: Vec<Option<MembershipProof>>,
/// Program ID.
pub program_id: ProgramId,
}

View File

@ -17,7 +17,7 @@ fn initialize_account(pre_state: AccountWithMetadata) -> AccountPostState {
// Continue only if the owner authorized this operation
if !is_authorized {
panic!("Invalid input");
panic!("Account must be authorized");
}
account_to_claim
@ -31,12 +31,12 @@ fn transfer(
) -> Vec<AccountPostState> {
// Continue only if the sender has authorized this operation
if !sender.is_authorized {
panic!("Invalid input");
panic!("Sender must be authorized");
}
// Continue only if the sender has enough balance
if sender.account.balance < balance_to_move {
panic!("Invalid input");
panic!("Sender has insufficient balance");
}
// Create accounts post states, with updated balances

View File

@ -16,7 +16,8 @@ fn main() {
visibility_mask,
private_account_nonces,
private_account_keys,
private_account_auth,
private_account_nsks,
private_account_membership_proofs,
mut program_id,
} = env::read();
@ -63,7 +64,8 @@ fn main() {
for (i, program_output) in program_outputs.iter().enumerate() {
let mut program_output = program_output.clone();
// Check that `program_output` is consistent with the execution of the corresponding program.
// Check that `program_output` is consistent with the execution of the corresponding
// program.
let program_output_words =
&to_vec(&program_output).expect("program_output must be serializable");
env::verify(program_id, program_output_words)
@ -131,7 +133,8 @@ fn main() {
let mut private_nonces_iter = private_account_nonces.iter();
let mut private_keys_iter = private_account_keys.iter();
let mut private_auth_iter = private_account_auth.iter();
let mut private_nsks_iter = private_account_nsks.iter();
let mut private_membership_proofs_iter = private_account_membership_proofs.iter();
let mut output_index = 0;
for i in 0..n_accounts {
@ -158,8 +161,7 @@ fn main() {
if visibility_mask[i] == 1 {
// Private account with authentication
let (nsk, membership_proof) =
private_auth_iter.next().expect("Missing private auth");
let nsk = private_nsks_iter.next().expect("Missing nsk");
// Verify the nullifier public key
let expected_npk = NullifierPublicKey::from(nsk);
@ -167,19 +169,38 @@ fn main() {
panic!("Nullifier public key mismatch");
}
// Compute commitment set digest associated with provided auth path
let commitment_pre = Commitment::new(npk, &pre_states[i].account);
let set_digest = compute_digest_for_path(&commitment_pre, membership_proof);
// Check pre_state authorization
if !pre_states[i].is_authorized {
panic!("Pre-state not authorized");
}
// Compute update nullifier
let nullifier = Nullifier::for_account_update(&commitment_pre, nsk);
let membership_proof_opt = private_membership_proofs_iter
.next()
.expect("Missing membership proof");
let (nullifier, set_digest) = membership_proof_opt
.as_ref()
.map(|membership_proof| {
// Compute commitment set digest associated with provided auth path
let commitment_pre = Commitment::new(npk, &pre_states[i].account);
let set_digest =
compute_digest_for_path(&commitment_pre, membership_proof);
// Compute update nullifier
let nullifier = Nullifier::for_account_update(&commitment_pre, nsk);
(nullifier, set_digest)
})
.unwrap_or_else(|| {
if pre_states[i].account != Account::default() {
panic!("Found new private account with non default values.");
}
// Compute initialization nullifier
let nullifier = Nullifier::for_account_initialization(npk);
(nullifier, DUMMY_COMMITMENT_HASH)
});
new_nullifiers.push((nullifier, set_digest));
} else {
// Private account without authentication
if pre_states[i].account != Account::default() {
panic!("Found new private account with non default values.");
}
@ -188,7 +209,13 @@ fn main() {
panic!("Found new private account marked as authorized.");
}
// Compute initialization nullifier
let membership_proof_opt = private_membership_proofs_iter
.next()
.expect("Missing membership proof");
assert!(
membership_proof_opt.is_none(),
"Membership proof must be None for unauthorized accounts"
);
let nullifier = Nullifier::for_account_initialization(npk);
new_nullifiers.push((nullifier, DUMMY_COMMITMENT_HASH));
}
@ -223,15 +250,19 @@ fn main() {
}
if private_nonces_iter.next().is_some() {
panic!("Too many nonces.");
panic!("Too many nonces");
}
if private_keys_iter.next().is_some() {
panic!("Too many private account keys.");
panic!("Too many private account keys");
}
if private_auth_iter.next().is_some() {
panic!("Too many private account authentication keys.");
if private_nsks_iter.next().is_some() {
panic!("Too many private account authentication keys");
}
if private_membership_proofs_iter.next().is_some() {
panic!("Too many private account membership proofs");
}
let output = PrivacyPreservingCircuitOutput {

View File

@ -44,13 +44,15 @@ impl From<Program> for ProgramWithDependencies {
/// Generates a proof of the execution of a NSSA program inside the privacy preserving execution
/// circuit
#[expect(clippy::too_many_arguments, reason = "TODO: fix later")]
pub fn execute_and_prove(
pre_states: &[AccountWithMetadata],
instruction_data: &InstructionData,
visibility_mask: &[u8],
private_account_nonces: &[u128],
private_account_keys: &[(NullifierPublicKey, SharedSecretKey)],
private_account_auth: &[(NullifierSecretKey, MembershipProof)],
private_account_nsks: &[NullifierSecretKey],
private_account_membership_proofs: &[Option<MembershipProof>],
program_with_dependencies: &ProgramWithDependencies,
) -> Result<(PrivacyPreservingCircuitOutput, Proof), NssaError> {
let mut program = &program_with_dependencies.program;
@ -105,7 +107,8 @@ pub fn execute_and_prove(
visibility_mask: visibility_mask.to_vec(),
private_account_nonces: private_account_nonces.to_vec(),
private_account_keys: private_account_keys.to_vec(),
private_account_auth: private_account_auth.to_vec(),
private_account_nsks: private_account_nsks.to_vec(),
private_account_membership_proofs: private_account_membership_proofs.to_vec(),
program_id: program_with_dependencies.program.id(),
};
@ -218,6 +221,7 @@ mod tests {
&[0xdeadbeef],
&[(recipient_keys.npk(), shared_secret.clone())],
&[],
&[None],
&Program::authenticated_transfer_program().into(),
)
.unwrap();
@ -315,10 +319,8 @@ mod tests {
(sender_keys.npk(), shared_secret_1.clone()),
(recipient_keys.npk(), shared_secret_2.clone()),
],
&[(
sender_keys.nsk,
commitment_set.get_proof_for(&commitment_sender).unwrap(),
)],
&[sender_keys.nsk],
&[commitment_set.get_proof_for(&commitment_sender), None],
&program.into(),
)
.unwrap();

View File

@ -222,6 +222,15 @@ mod tests {
}
}
pub fn noop() -> Self {
use test_program_methods::{NOOP_ELF, NOOP_ID};
Program {
id: NOOP_ID,
elf: NOOP_ELF.to_vec(),
}
}
pub fn modified_transfer_program() -> Self {
use test_program_methods::MODIFIED_TRANSFER_ELF;
// This unwrap won't panic since the `MODIFIED_TRANSFER_ELF` comes from risc0 build of

View File

@ -868,6 +868,7 @@ pub mod tests {
&[0xdeadbeef],
&[(recipient_keys.npk(), shared_secret)],
&[],
&[None],
&Program::authenticated_transfer_program().into(),
)
.unwrap();
@ -916,10 +917,8 @@ pub mod tests {
(sender_keys.npk(), shared_secret_1),
(recipient_keys.npk(), shared_secret_2),
],
&[(
sender_keys.nsk,
state.get_proof_for_commitment(&sender_commitment).unwrap(),
)],
&[sender_keys.nsk],
&[state.get_proof_for_commitment(&sender_commitment), None],
&program.into(),
)
.unwrap();
@ -968,10 +967,8 @@ pub mod tests {
&[1, 0],
&[new_nonce],
&[(sender_keys.npk(), shared_secret)],
&[(
sender_keys.nsk,
state.get_proof_for_commitment(&sender_commitment).unwrap(),
)],
&[sender_keys.nsk],
&[state.get_proof_for_commitment(&sender_commitment)],
&program.into(),
)
.unwrap();
@ -1185,6 +1182,7 @@ pub mod tests {
&[],
&[],
&[],
&[],
&program.into(),
);
@ -1211,6 +1209,7 @@ pub mod tests {
&[],
&[],
&[],
&[],
&program.into(),
);
@ -1237,6 +1236,7 @@ pub mod tests {
&[],
&[],
&[],
&[],
&program.into(),
);
@ -1263,6 +1263,7 @@ pub mod tests {
&[],
&[],
&[],
&[],
&program.into(),
);
@ -1291,6 +1292,7 @@ pub mod tests {
&[],
&[],
&[],
&[],
&program.to_owned().into(),
);
@ -1317,6 +1319,7 @@ pub mod tests {
&[],
&[],
&[],
&[],
&program.into(),
);
@ -1352,6 +1355,7 @@ pub mod tests {
&[],
&[],
&[],
&[],
&program.into(),
);
@ -1378,6 +1382,7 @@ pub mod tests {
&[],
&[],
&[],
&[],
&program.into(),
);
@ -1413,6 +1418,7 @@ pub mod tests {
&[],
&[],
&[],
&[],
&program.into(),
);
@ -1450,6 +1456,7 @@ pub mod tests {
&[],
&[],
&[],
&[],
&program.into(),
);
@ -1490,7 +1497,8 @@ pub mod tests {
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&[sender_keys.nsk],
&[Some((0, vec![]))],
&program.into(),
);
@ -1524,7 +1532,50 @@ pub mod tests {
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&private_account_keys,
&[(sender_keys.nsk, (0, vec![]))],
&[sender_keys.nsk],
&[Some((0, vec![]))],
&program.into(),
);
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
}
#[test]
fn test_circuit_fails_if_insufficient_commitment_proofs_are_provided() {
let program = Program::simple_balance_transfer();
let sender_keys = test_private_account_keys_1();
let recipient_keys = test_private_account_keys_2();
let private_account_1 = AccountWithMetadata::new(
Account {
program_owner: program.id(),
balance: 100,
..Account::default()
},
true,
&sender_keys.npk(),
);
let private_account_2 =
AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk());
// Setting no second commitment proof.
let private_account_membership_proofs = [Some((0, vec![]))];
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&[
(
sender_keys.npk(),
SharedSecretKey::new(&[55; 32], &sender_keys.ivk()),
),
(
recipient_keys.npk(),
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[sender_keys.nsk],
&private_account_membership_proofs,
&program.into(),
);
@ -1549,7 +1600,7 @@ pub mod tests {
AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk());
// Setting no auth key for an execution with one non default private accounts.
let private_account_auth = [];
let private_account_nsks = [];
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
@ -1565,7 +1616,8 @@ pub mod tests {
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&private_account_auth,
&private_account_nsks,
&[],
&program.into(),
);
@ -1601,19 +1653,20 @@ pub mod tests {
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
];
let private_account_auth = [
// Setting the recipient key to authorize the sender.
// This should be set to the sender private account in
// a normal circumstance. The recipient can't authorize this.
(recipient_keys.nsk, (0, vec![])),
];
// Setting the recipient key to authorize the sender.
// This should be set to the sender private account in
// a normal circumstance. The recipient can't authorize this.
let private_account_nsks = [recipient_keys.nsk];
let private_account_membership_proofs = [Some((0, vec![]))];
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&private_account_keys,
&private_account_auth,
&private_account_nsks,
&private_account_membership_proofs,
&program.into(),
);
@ -1659,7 +1712,8 @@ pub mod tests {
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&[sender_keys.nsk],
&[Some((0, vec![]))],
&program.into(),
);
@ -1706,7 +1760,8 @@ pub mod tests {
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&[sender_keys.nsk],
&[Some((0, vec![]))],
&program.into(),
);
@ -1752,7 +1807,8 @@ pub mod tests {
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&[sender_keys.nsk],
&[Some((0, vec![]))],
&program.into(),
);
@ -1798,7 +1854,8 @@ pub mod tests {
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&[sender_keys.nsk],
&[Some((0, vec![]))],
&program.into(),
);
@ -1842,7 +1899,8 @@ pub mod tests {
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&[sender_keys.nsk],
&[Some((0, vec![]))],
&program.into(),
);
@ -1872,6 +1930,7 @@ pub mod tests {
&[],
&[],
&[],
&[],
&program.into(),
);
@ -1913,7 +1972,8 @@ pub mod tests {
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&[(sender_keys.nsk, (0, vec![]))],
&[sender_keys.nsk],
&[Some((0, vec![]))],
&program.into(),
);
@ -1959,7 +2019,8 @@ pub mod tests {
&[1, 2],
&[0xdeadbeef1, 0xdeadbeef2],
&private_account_keys,
&[(sender_keys.nsk, (0, vec![]))],
&[sender_keys.nsk],
&[Some((0, vec![]))],
&program.into(),
);
@ -1986,10 +2047,8 @@ pub mod tests {
// Setting two private account keys for a circuit execution with only one non default
// private account (visibility mask equal to 1 means that auth keys are expected).
let visibility_mask = [1, 2];
let private_account_auth = [
(sender_keys.nsk, (0, vec![])),
(recipient_keys.nsk, (1, vec![])),
];
let private_account_nsks = [sender_keys.nsk, recipient_keys.nsk];
let private_account_membership_proofs = [Some((0, vec![])), Some((1, vec![]))];
let result = execute_and_prove(
&[private_account_1, private_account_2],
&Program::serialize_instruction(10u128).unwrap(),
@ -2005,7 +2064,8 @@ pub mod tests {
SharedSecretKey::new(&[56; 32], &recipient_keys.ivk()),
),
],
&private_account_auth,
&private_account_nsks,
&private_account_membership_proofs,
&program.into(),
);
@ -2082,10 +2142,8 @@ pub mod tests {
);
let visibility_mask = [1, 1];
let private_account_auth = [
(sender_keys.nsk, (1, vec![])),
(sender_keys.nsk, (1, vec![])),
];
let private_account_nsks = [sender_keys.nsk, sender_keys.nsk];
let private_account_membership_proofs = [Some((1, vec![])), Some((1, vec![]))];
let shared_secret = SharedSecretKey::new(&[55; 32], &sender_keys.ivk());
let result = execute_and_prove(
&[private_account_1.clone(), private_account_1],
@ -2096,7 +2154,8 @@ pub mod tests {
(sender_keys.npk(), shared_secret.clone()),
(sender_keys.npk(), shared_secret),
],
&private_account_auth,
&private_account_nsks,
&private_account_membership_proofs,
&program.into(),
);
@ -2397,15 +2456,10 @@ pub mod tests {
&[1, 1],
&[from_new_nonce, to_new_nonce],
&[(from_keys.npk(), to_ss), (to_keys.npk(), from_ss)],
&[from_keys.nsk, to_keys.nsk],
&[
(
from_keys.nsk,
state.get_proof_for_commitment(&from_commitment).unwrap(),
),
(
to_keys.nsk,
state.get_proof_for_commitment(&to_commitment).unwrap(),
),
state.get_proof_for_commitment(&from_commitment),
state.get_proof_for_commitment(&to_commitment),
],
&program_with_deps,
)
@ -2612,4 +2666,143 @@ pub mod tests {
assert!(expected_sender_post == sender_post);
assert!(expected_recipient_post == recipient_post);
}
#[test]
fn test_private_authorized_uninitialized_account() {
let mut state = V02State::new_with_genesis_accounts(&[], &[]);
// Set up keys for the authorized private account
let private_keys = test_private_account_keys_1();
// Create an authorized private account with default values (new account being initialized)
let authorized_account =
AccountWithMetadata::new(Account::default(), true, &private_keys.npk());
let program = Program::authenticated_transfer_program();
// Set up parameters for the new account
let esk = [3; 32];
let shared_secret = SharedSecretKey::new(&esk, &private_keys.ivk());
let epk = EphemeralPublicKey::from_scalar(esk);
// Balance to initialize the account with (0 for a new account)
let balance: u128 = 0;
let nonce = 0xdeadbeef1;
// Execute and prove the circuit with the authorized account but no commitment proof
let (output, proof) = execute_and_prove(
std::slice::from_ref(&authorized_account),
&Program::serialize_instruction(balance).unwrap(),
&[1],
&[nonce],
&[(private_keys.npk(), shared_secret)],
&[private_keys.nsk],
&[None],
&program.into(),
)
.unwrap();
// Create message from circuit output
let message = Message::try_from_circuit_output(
vec![],
vec![],
vec![(private_keys.npk(), private_keys.ivk(), epk)],
output,
)
.unwrap();
let witness_set = WitnessSet::for_message(&message, proof, &[]);
let tx = PrivacyPreservingTransaction::new(message, witness_set);
let result = state.transition_from_privacy_preserving_transaction(&tx);
assert!(result.is_ok());
let nullifier = Nullifier::for_account_initialization(&private_keys.npk());
assert!(state.private_state.1.contains(&nullifier));
}
#[test]
fn test_private_account_claimed_then_used_without_init_flag_should_fail() {
let mut state = V02State::new_with_genesis_accounts(&[], &[]).with_test_programs();
// Set up keys for the private account
let private_keys = test_private_account_keys_1();
// Step 1: Create a new private account with authorization
let authorized_account =
AccountWithMetadata::new(Account::default(), true, &private_keys.npk());
let claimer_program = Program::claimer();
// Set up parameters for claiming the new account
let esk = [3; 32];
let shared_secret = SharedSecretKey::new(&esk, &private_keys.ivk());
let epk = EphemeralPublicKey::from_scalar(esk);
let balance: u128 = 0;
let nonce = 0xdeadbeef1;
// Step 2: Execute claimer program to claim the account with authentication
let (output, proof) = execute_and_prove(
std::slice::from_ref(&authorized_account),
&Program::serialize_instruction(balance).unwrap(),
&[1],
&[nonce],
&[(private_keys.npk(), shared_secret)],
&[private_keys.nsk],
&[None],
&claimer_program.into(),
)
.unwrap();
let message = Message::try_from_circuit_output(
vec![],
vec![],
vec![(private_keys.npk(), private_keys.ivk(), epk)],
output,
)
.unwrap();
let witness_set = WitnessSet::for_message(&message, proof, &[]);
let tx = PrivacyPreservingTransaction::new(message, witness_set);
// Claim should succeed
assert!(
state
.transition_from_privacy_preserving_transaction(&tx)
.is_ok()
);
// Verify the account is now initialized (nullifier exists)
let nullifier = Nullifier::for_account_initialization(&private_keys.npk());
assert!(state.private_state.1.contains(&nullifier));
// Prepare new state of account
let account_metadata = {
let mut acc = authorized_account.clone();
acc.account.program_owner = Program::claimer().id();
acc
};
let noop_program = Program::noop();
let esk2 = [4; 32];
let shared_secret2 = SharedSecretKey::new(&esk2, &private_keys.ivk());
let nonce2 = 0xdeadbeef2;
// Step 3: Try to execute noop program with authentication but without initialization
let res = execute_and_prove(
std::slice::from_ref(&account_metadata),
&Program::serialize_instruction(()).unwrap(),
&[1],
&[nonce2],
&[(private_keys.npk(), shared_secret2)],
&[private_keys.nsk],
&[None],
&noop_program.into(),
);
assert!(matches!(res, Err(NssaError::CircuitProvingError(_))));
}
}

View File

@ -0,0 +1,12 @@
use nssa_core::program::{read_nssa_inputs, write_nssa_outputs, ProgramInput, AccountPostState};
type Instruction = ();
fn main() {
let (ProgramInput { pre_states, .. }, instruction_words) = read_nssa_inputs::<Instruction>();
let post_states = pre_states.iter().map(|account| {
AccountPostState::new(account.account.clone())
}).collect();
write_nssa_outputs(instruction_words, pre_states, post_states);
}

View File

@ -283,6 +283,7 @@ impl WalletCore {
.map(|keys| (keys.npk.clone(), keys.ssk.clone()))
.collect::<Vec<_>>(),
&acc_manager.private_account_auth(),
&acc_manager.private_account_membership_proofs(),
&program.to_owned().into(),
)
.unwrap();
@ -303,7 +304,7 @@ impl WalletCore {
nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message(
&message,
proof,
&acc_manager.witness_signing_keys(),
&acc_manager.public_account_auth(),
);
let tx = PrivacyPreservingTransaction::new(message, witness_set);

View File

@ -58,7 +58,8 @@ impl WalletCore {
&[0, 1],
&produce_random_nonces(1),
&[(winner_npk.clone(), shared_secret_winner.clone())],
&[(winner_nsk.unwrap(), winner_proof)],
&[(winner_nsk.unwrap())],
&[winner_proof],
&program.into(),
)
.unwrap();
@ -125,6 +126,7 @@ impl WalletCore {
&produce_random_nonces(1),
&[(winner_npk.clone(), shared_secret_winner.clone())],
&[],
&[]
&program.into(),
)
.unwrap();

View File

@ -133,11 +133,21 @@ impl AccountManager {
.collect()
}
pub fn private_account_auth(&self) -> Vec<(NullifierSecretKey, MembershipProof)> {
pub fn private_account_auth(&self) -> Vec<NullifierSecretKey> {
self.states
.iter()
.filter_map(|state| match state {
State::Private(pre) => Some((pre.nsk?, pre.proof.clone()?)),
State::Private(pre) => pre.nsk,
_ => None,
})
.collect()
}
pub fn private_account_membership_proofs(&self) -> Vec<Option<MembershipProof>> {
self.states
.iter()
.filter_map(|state| match state {
State::Private(pre) => Some(pre.proof.clone()),
_ => None,
})
.collect()
@ -153,7 +163,7 @@ impl AccountManager {
.collect()
}
pub fn witness_signing_keys(&self) -> Vec<&PrivateKey> {
pub fn public_account_auth(&self) -> Vec<&PrivateKey> {
self.states
.iter()
.filter_map(|state| match state {
@ -185,7 +195,7 @@ async fn private_acc_preparation(
return Err(ExecutionFailureKind::KeyNotFoundError);
};
let mut nsk = Some(from_keys.private_key_holder.nullifier_secret_key);
let nsk = from_keys.private_key_holder.nullifier_secret_key;
let from_npk = from_keys.nullifer_public_key;
let from_ipk = from_keys.incoming_viewing_public_key;
@ -196,14 +206,12 @@ async fn private_acc_preparation(
.await
.unwrap();
if proof.is_none() {
nsk = None;
}
let sender_pre = AccountWithMetadata::new(from_acc.clone(), proof.is_some(), &from_npk);
// TODO: Technically we could allow unauthorized owned accounts, but currently we don't have
// support from that in the wallet.
let sender_pre = AccountWithMetadata::new(from_acc.clone(), true, &from_npk);
Ok(AccountPreparedData {
nsk,
nsk: Some(nsk),
npk: from_npk,
ipk: from_ipk,
pre_state: sender_pre,