mirror of
https://github.com/logos-blockchain/lssa.git
synced 2026-01-02 13:23:10 +00:00
add nullifier for private account initialization
This commit is contained in:
parent
fea132ef24
commit
63436955c6
@ -82,7 +82,7 @@ mod tests {
|
||||
&Account::default(),
|
||||
)],
|
||||
new_nullifiers: vec![(
|
||||
Nullifier::new(
|
||||
Nullifier::for_account_update(
|
||||
&Commitment::new(&NullifierPublicKey::from(&[2; 32]), &Account::default()),
|
||||
&[1; 32],
|
||||
),
|
||||
|
||||
@ -7,9 +7,31 @@ use crate::{NullifierPublicKey, account::Account};
|
||||
#[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq, Hash))]
|
||||
pub struct Commitment(pub(super) [u8; 32]);
|
||||
|
||||
// ```python
|
||||
// from hashlib import sha256
|
||||
// hasher = sha256()
|
||||
// hasher.update(bytes([0] * 32 + [0] * 32 + [0] * 16 + [0] * 16 + list(sha256().digest())))
|
||||
// DUMMY_COMMITMENT = hasher.digest()
|
||||
// ```
|
||||
pub const DUMMY_COMMITMENT: Commitment = Commitment([
|
||||
130, 75, 48, 230, 171, 101, 121, 141, 159, 118, 21, 74, 135, 248, 16, 255, 238, 156, 61, 24,
|
||||
165, 33, 34, 172, 227, 30, 215, 20, 85, 47, 230, 29,
|
||||
]);
|
||||
|
||||
// ```python
|
||||
// from hashlib import sha256
|
||||
// hasher = sha256()
|
||||
// hasher.update(DUMMY_COMMITMENT)
|
||||
// DUMMY_COMMITMENT_HASH = hasher.digest()
|
||||
// ```
|
||||
pub const DUMMY_COMMITMENT_HASH: [u8; 32] = [
|
||||
170, 10, 217, 228, 20, 35, 189, 177, 238, 235, 97, 129, 132, 89, 96, 247, 86, 91, 222, 214, 38,
|
||||
194, 216, 67, 56, 251, 208, 226, 0, 117, 149, 39,
|
||||
];
|
||||
|
||||
impl Commitment {
|
||||
/// Generates the commitment to a private account owned by user for npk:
|
||||
/// SHA256(npk || program_owner || balance || nonce || data)
|
||||
/// SHA256(npk || program_owner || balance || nonce || SHA256(data))
|
||||
pub fn new(npk: &NullifierPublicKey, account: &Account) -> Self {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.extend_from_slice(&npk.to_byte_array());
|
||||
@ -64,3 +86,30 @@ pub fn compute_digest_for_path(
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use risc0_zkvm::sha::{Impl, Sha256};
|
||||
|
||||
use crate::{
|
||||
Commitment, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, NullifierPublicKey, account::Account,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_nothing_up_my_sleeve_dummy_commitment() {
|
||||
let default_account = Account::default();
|
||||
let npk_null = NullifierPublicKey([0; 32]);
|
||||
let expected_dummy_commitment = Commitment::new(&npk_null, &default_account);
|
||||
assert_eq!(DUMMY_COMMITMENT, expected_dummy_commitment);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nothing_up_my_sleeve_dummy_commitment_hash() {
|
||||
let expected_dummy_commitment_hash: [u8; 32] =
|
||||
Impl::hash_bytes(&DUMMY_COMMITMENT.to_byte_array())
|
||||
.as_bytes()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
assert_eq!(DUMMY_COMMITMENT_HASH, expected_dummy_commitment_hash);
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,7 +7,10 @@ mod nullifier;
|
||||
pub mod program;
|
||||
|
||||
pub use circuit_io::{PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput};
|
||||
pub use commitment::{Commitment, CommitmentSetDigest, MembershipProof, compute_digest_for_path};
|
||||
pub use commitment::{
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, MembershipProof,
|
||||
compute_digest_for_path,
|
||||
};
|
||||
pub use encryption::{EncryptionScheme, SharedSecretKey};
|
||||
pub use nullifier::{Nullifier, NullifierPublicKey, NullifierSecretKey};
|
||||
|
||||
|
||||
@ -45,12 +45,20 @@ pub type NullifierSecretKey = [u8; 32];
|
||||
pub struct Nullifier(pub(super) [u8; 32]);
|
||||
|
||||
impl Nullifier {
|
||||
pub fn new(commitment: &Commitment, nsk: &NullifierSecretKey) -> Self {
|
||||
let mut bytes = Vec::new();
|
||||
pub fn for_account_update(commitment: &Commitment, nsk: &NullifierSecretKey) -> Self {
|
||||
const UPDATE_PREFIX: &[u8; 32] = b"/NSSA/v0.1/Nullifier/Update/\x00\x00\x00\x00";
|
||||
let mut bytes = UPDATE_PREFIX.to_vec();
|
||||
bytes.extend_from_slice(&commitment.to_byte_array());
|
||||
bytes.extend_from_slice(nsk);
|
||||
Self(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap())
|
||||
}
|
||||
|
||||
pub fn for_account_initialization(npk: &NullifierPublicKey) -> Self {
|
||||
const INIT_PREFIX: &[u8; 32] = b"/NSSA/v0.1/Nullifier/Initialize/";
|
||||
let mut bytes = INIT_PREFIX.to_vec();
|
||||
bytes.extend_from_slice(&npk.to_byte_array());
|
||||
Self(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -58,14 +66,28 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_constructor() {
|
||||
fn test_constructor_for_account_update() {
|
||||
let commitment = Commitment((0..32u8).collect::<Vec<_>>().try_into().unwrap());
|
||||
let nsk = [0x42; 32];
|
||||
let expected_nullifier = Nullifier([
|
||||
97, 87, 111, 191, 0, 44, 125, 145, 237, 104, 31, 230, 203, 254, 68, 176, 126, 17, 240,
|
||||
205, 249, 143, 11, 43, 15, 198, 189, 219, 191, 49, 36, 61,
|
||||
235, 128, 185, 229, 74, 74, 83, 13, 165, 48, 239, 24, 48, 101, 71, 251, 253, 92, 88,
|
||||
201, 103, 43, 250, 135, 193, 54, 175, 82, 245, 171, 90, 135,
|
||||
]);
|
||||
let nullifier = Nullifier::new(&commitment, &nsk);
|
||||
let nullifier = Nullifier::for_account_update(&commitment, &nsk);
|
||||
assert_eq!(nullifier, expected_nullifier);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_constructor_for_account_initialization() {
|
||||
let npk = NullifierPublicKey([
|
||||
112, 188, 193, 129, 150, 55, 228, 67, 88, 168, 29, 151, 5, 92, 23, 190, 17, 162, 164,
|
||||
255, 29, 105, 42, 186, 43, 11, 157, 168, 132, 225, 17, 163,
|
||||
]);
|
||||
let expected_nullifier = Nullifier([
|
||||
96, 99, 33, 1, 116, 84, 169, 18, 85, 201, 17, 243, 123, 240, 242, 34, 116, 233, 92,
|
||||
203, 247, 92, 161, 162, 135, 66, 127, 108, 230, 149, 105, 157,
|
||||
]);
|
||||
let nullifier = Nullifier::for_account_initialization(&npk);
|
||||
assert_eq!(nullifier, expected_nullifier);
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
use risc0_zkvm::{guest::env, serde::to_vec};
|
||||
|
||||
use nssa_core::{
|
||||
Commitment, CommitmentSetDigest, EncryptionScheme, Nullifier, NullifierPublicKey,
|
||||
PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput,
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, DUMMY_COMMITMENT_HASH, EncryptionScheme,
|
||||
Nullifier, NullifierPublicKey, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput,
|
||||
account::{Account, AccountId, AccountWithMetadata},
|
||||
compute_digest_for_path,
|
||||
encryption::Ciphertext,
|
||||
@ -98,8 +98,8 @@ fn main() {
|
||||
panic!("Pre-state not authorized");
|
||||
}
|
||||
|
||||
// Compute nullifier
|
||||
let nullifier = Nullifier::new(&commitment_pre, nsk);
|
||||
// Compute update nullifier
|
||||
let nullifier = Nullifier::for_account_update(&commitment_pre, nsk);
|
||||
new_nullifiers.push((nullifier, set_digest));
|
||||
} else {
|
||||
if pre_states[i].account != Account::default() {
|
||||
@ -109,6 +109,10 @@ fn main() {
|
||||
if pre_states[i].is_authorized {
|
||||
panic!("Found new private account marked as authorized.");
|
||||
}
|
||||
|
||||
// Compute initialization nullifier
|
||||
let nullifier = Nullifier::for_account_initialization(npk);
|
||||
new_nullifiers.push((nullifier, DUMMY_COMMITMENT_HASH));
|
||||
}
|
||||
|
||||
// Update post-state with new nonce
|
||||
|
||||
@ -91,7 +91,7 @@ impl Proof {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use nssa_core::{
|
||||
Commitment, EncryptionScheme, Nullifier,
|
||||
Commitment, DUMMY_COMMITMENT_HASH, EncryptionScheme, Nullifier,
|
||||
account::{Account, AccountId, AccountWithMetadata},
|
||||
};
|
||||
|
||||
@ -165,7 +165,7 @@ mod tests {
|
||||
assert_eq!(sender_pre, expected_sender_pre);
|
||||
assert_eq!(sender_post, expected_sender_post);
|
||||
assert_eq!(output.new_commitments.len(), 1);
|
||||
assert_eq!(output.new_nullifiers.len(), 0);
|
||||
assert_eq!(output.new_nullifiers.len(), 1);
|
||||
assert_eq!(output.ciphertexts.len(), 1);
|
||||
|
||||
let recipient_post = EncryptionScheme::decrypt(
|
||||
@ -206,10 +206,16 @@ mod tests {
|
||||
let mut commitment_set = CommitmentSet::with_capacity(2);
|
||||
commitment_set.extend(std::slice::from_ref(&commitment_sender));
|
||||
|
||||
let expected_new_nullifiers = vec![(
|
||||
Nullifier::new(&commitment_sender, &sender_keys.nsk),
|
||||
commitment_set.digest(),
|
||||
)];
|
||||
let expected_new_nullifiers = vec![
|
||||
(
|
||||
Nullifier::for_account_update(&commitment_sender, &sender_keys.nsk),
|
||||
commitment_set.digest(),
|
||||
),
|
||||
(
|
||||
Nullifier::for_account_initialization(&recipient_keys.npk()),
|
||||
DUMMY_COMMITMENT_HASH,
|
||||
),
|
||||
];
|
||||
|
||||
let program = Program::authenticated_transfer_program();
|
||||
|
||||
|
||||
@ -125,7 +125,10 @@ pub mod tests {
|
||||
let new_commitments = vec![Commitment::new(&npk2, &account2)];
|
||||
|
||||
let old_commitment = Commitment::new(&npk1, &account1);
|
||||
let new_nullifiers = vec![(Nullifier::new(&old_commitment, &nsk1), [0; 32])];
|
||||
let new_nullifiers = vec![(
|
||||
Nullifier::for_account_update(&old_commitment, &nsk1),
|
||||
[0; 32],
|
||||
)];
|
||||
|
||||
Message {
|
||||
public_addresses: public_addresses.clone(),
|
||||
|
||||
@ -4,7 +4,7 @@ use crate::{
|
||||
public_transaction::PublicTransaction,
|
||||
};
|
||||
use nssa_core::{
|
||||
Commitment, CommitmentSetDigest, MembershipProof, Nullifier,
|
||||
Commitment, CommitmentSetDigest, DUMMY_COMMITMENT, MembershipProof, Nullifier,
|
||||
account::Account,
|
||||
program::{DEFAULT_PROGRAM_ID, ProgramId},
|
||||
};
|
||||
@ -83,6 +83,7 @@ impl V01State {
|
||||
.collect();
|
||||
|
||||
let mut private_state = CommitmentSet::with_capacity(32);
|
||||
private_state.extend(&[DUMMY_COMMITMENT]);
|
||||
private_state.extend(initial_commitments);
|
||||
|
||||
let mut this = Self {
|
||||
@ -1025,7 +1026,8 @@ pub mod tests {
|
||||
);
|
||||
|
||||
let sender_pre_commitment = Commitment::new(&sender_keys.npk(), &sender_private_account);
|
||||
let expected_new_nullifier = Nullifier::new(&sender_pre_commitment, &sender_keys.nsk);
|
||||
let expected_new_nullifier =
|
||||
Nullifier::for_account_update(&sender_pre_commitment, &sender_keys.nsk);
|
||||
|
||||
let expected_new_commitment_2 = Commitment::new(
|
||||
&recipient_keys.npk(),
|
||||
@ -1099,7 +1101,8 @@ pub mod tests {
|
||||
);
|
||||
|
||||
let sender_pre_commitment = Commitment::new(&sender_keys.npk(), &sender_private_account);
|
||||
let expected_new_nullifier = Nullifier::new(&sender_pre_commitment, &sender_keys.nsk);
|
||||
let expected_new_nullifier =
|
||||
Nullifier::for_account_update(&sender_pre_commitment, &sender_keys.nsk);
|
||||
|
||||
assert!(state.private_state.0.contains(&sender_pre_commitment));
|
||||
assert!(!state.private_state.0.contains(&expected_new_commitment));
|
||||
@ -1940,4 +1943,59 @@ pub mod tests {
|
||||
|
||||
assert!(matches!(result, Err(NssaError::CircuitProvingError(_))));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_private_accounts_can_only_be_initialized_once() {
|
||||
let sender_keys = test_private_account_keys_1();
|
||||
let sender_private_account = Account {
|
||||
program_owner: Program::authenticated_transfer_program().id(),
|
||||
balance: 100,
|
||||
nonce: 0xdeadbeef,
|
||||
data: vec![],
|
||||
};
|
||||
let recipient_keys = test_private_account_keys_2();
|
||||
|
||||
let mut state = V01State::new_with_genesis_accounts(&[], &[])
|
||||
.with_private_account(&sender_keys, &sender_private_account);
|
||||
|
||||
let balance_to_move = 37;
|
||||
|
||||
let tx = private_balance_transfer_for_tests(
|
||||
&sender_keys,
|
||||
&sender_private_account,
|
||||
&recipient_keys,
|
||||
balance_to_move,
|
||||
[0xcafecafe, 0xfecafeca],
|
||||
&state,
|
||||
);
|
||||
|
||||
state
|
||||
.transition_from_privacy_preserving_transaction(&tx)
|
||||
.unwrap();
|
||||
|
||||
let sender_private_account = Account {
|
||||
program_owner: Program::authenticated_transfer_program().id(),
|
||||
balance: 100 - balance_to_move,
|
||||
nonce: 0xcafecafe,
|
||||
data: vec![],
|
||||
};
|
||||
|
||||
let tx = private_balance_transfer_for_tests(
|
||||
&sender_keys,
|
||||
&sender_private_account,
|
||||
&recipient_keys,
|
||||
balance_to_move,
|
||||
[0x1234, 0x5678],
|
||||
&state,
|
||||
);
|
||||
|
||||
let result = state.transition_from_privacy_preserving_transaction(&tx);
|
||||
|
||||
assert!(matches!(result, Err(NssaError::InvalidInput(_))));
|
||||
let NssaError::InvalidInput(error_message) = result.err().unwrap() else {
|
||||
panic!("Incorrect message error");
|
||||
};
|
||||
let expected_error_message = "Nullifier already seen".to_string();
|
||||
assert_eq!(error_message, expected_error_message);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user