diff --git a/Cargo.toml b/Cargo.toml index fe70572..8eefa02 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,7 +53,7 @@ version = "0.8.5" [workspace.dependencies.k256] features = ["ecdsa-core", "arithmetic", "expose-field", "serde", "pem"] -version = "0.13.4" +version = "0.13.3" [workspace.dependencies.elliptic-curve] features = ["arithmetic"] diff --git a/nssa/core/Cargo.toml b/nssa/core/Cargo.toml index 1cdc78f..de7e4d6 100644 --- a/nssa/core/Cargo.toml +++ b/nssa/core/Cargo.toml @@ -4,10 +4,12 @@ version = "0.1.0" edition = "2024" [dependencies] -risc0-zkvm = "2.3.1" +risc0-zkvm = { version = "2.3.1"} serde = { version = "1.0", default-features = false } thiserror = { version = "2.0.12", optional = true } bytemuck = { version = "1.13", optional = true } +chacha20 = { version = "0.9", default-features = false } +k256 = "0.13.3" [features] default = [] diff --git a/nssa/core/src/lib.rs b/nssa/core/src/lib.rs index 56de12c..ba57f63 100644 --- a/nssa/core/src/lib.rs +++ b/nssa/core/src/lib.rs @@ -1,3 +1,7 @@ +use chacha20::{ + ChaCha20, + cipher::{KeyIvInit, StreamCipher}, +}; use risc0_zkvm::{ serde::to_vec, sha::{Impl, Sha256}, @@ -16,11 +20,19 @@ use crate::{ }; #[cfg(feature = "host")] -use std::io::Cursor; +use std::io::{Cursor, Read}; pub mod account; pub mod program; +use k256::{ + AffinePoint, EncodedPoint, FieldBytes, ProjectivePoint, PublicKey, Scalar, + elliptic_curve::{ + PrimeField, + sec1::{FromEncodedPoint, ToEncodedPoint}, + }, +}; + #[cfg(feature = "host")] pub mod error; @@ -55,49 +67,167 @@ pub fn verify_membership_proof( &result == digest } -pub type IncomingViewingPublicKey = [u8; 32]; +pub type EphemeralPublicKey = Secp256k1Point; +pub type IncomingViewingPublicKey = Secp256k1Point; + pub type EphemeralSecretKey = [u8; 32]; -pub struct EphemeralPublicKey; impl From<&EphemeralSecretKey> for EphemeralPublicKey { fn from(value: &EphemeralSecretKey) -> Self { - todo!() + Secp256k1Point::from_scalar(*value) } } -pub struct Tag(u8); -impl Tag { - pub fn new(Npk: &NullifierPublicKey, Ipk: &IncomingViewingPublicKey) -> Self { - todo!() +#[derive(Serialize, Deserialize, Clone)] +#[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))] +pub struct Secp256k1Point(pub Vec); +impl Secp256k1Point { + pub fn from_scalar(value: [u8; 32]) -> Secp256k1Point { + let x_bytes: FieldBytes = value.into(); + let x = Scalar::from_repr(x_bytes).unwrap(); + + let p = ProjectivePoint::GENERATOR * x; + let q = AffinePoint::from(p); + let enc = q.to_encoded_point(true); + + Self(enc.as_bytes().to_vec()) } } #[derive(Serialize, Deserialize)] #[cfg_attr(any(feature = "host", test), derive(Debug, Clone, PartialEq, Eq))] -pub struct EncryptedAccountData(u8); +pub struct EncryptedAccountData { + ciphertext: Vec, + epk: EphemeralPublicKey, + view_tag: u8, +} impl EncryptedAccountData { + #[cfg(feature = "host")] + pub fn decrypt(self, isk: &[u8; 32], output_index: u32) -> Option { + let ss_bytes = Self::ecdh(isk, &self.epk.0.clone().try_into().unwrap()); + let ipk = IncomingViewingPublicKey::from_scalar(*isk); + + let key = Self::kdf( + ss_bytes, + &self.epk, + &ipk, + // &commitment.to_byte_array(), + output_index, + ); + let mut cipher = ChaCha20::new(&key.into(), &[0; 12].into()); + let mut buffer = self.ciphertext; + + cipher.apply_keystream(&mut buffer); + let mut cursor = Cursor::new(buffer.as_slice()); + Account::from_cursor(&mut cursor).ok() + } + pub fn new( account: &Account, + // commitment: &Commitment, esk: &EphemeralSecretKey, - Npk: &NullifierPublicKey, - Ivk: &IncomingViewingPublicKey, + npk: &NullifierPublicKey, + ipk: &IncomingViewingPublicKey, + output_index: u32, ) -> Self { - // TODO: implement - Self(0) + let mut buffer = account.to_bytes().to_vec(); + + let ss_bytes = Self::ecdh(esk, &ipk.0.clone().try_into().unwrap()); + let epk = EphemeralPublicKey::from(esk); + + let key = Self::kdf( + ss_bytes, + &epk, + ipk, + // &commitment.to_byte_array(), + output_index, + ); + let mut cipher = ChaCha20::new(&key.into(), &[0; 12].into()); + cipher.apply_keystream(&mut buffer); + + let view_tag = Self::view_tag(&npk, &ipk); + Self { + ciphertext: buffer, + epk, + view_tag, + } + } + + pub fn kdf( + ss_bytes: [u8; 32], + epk: &EphemeralPublicKey, + ipk: &IncomingViewingPublicKey, + // commitment: &[u8; 32], + output_index: u32, + ) -> [u8; 32] { + let mut bytes = Vec::new(); + + bytes.extend_from_slice(b"NSSA/v0.1/KDF-SHA256"); + bytes.extend_from_slice(&ss_bytes); + bytes.extend_from_slice(&epk.0[..]); + bytes.extend_from_slice(&ipk.0[..]); + // bytes.extend_from_slice(&commitment[..]); + bytes.extend_from_slice(&output_index.to_le_bytes()); + + Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap() + } + + pub fn ecdh(scalar: &[u8; 32], point: &[u8; 33]) -> [u8; 32] { + let scalar = Scalar::from_repr((*scalar).into()).unwrap(); + + let encoded = EncodedPoint::from_bytes(point).unwrap(); + let pubkey_affine = AffinePoint::from_encoded_point(&encoded).unwrap(); + + let shared = ProjectivePoint::from(pubkey_affine) * scalar; + let shared_affine = shared.to_affine(); + + let encoded = shared_affine.to_encoded_point(false); + let x_bytes_slice = encoded.x().unwrap(); + let mut x_bytes = [0u8; 32]; + x_bytes.copy_from_slice(x_bytes_slice); + + x_bytes } #[cfg(feature = "host")] pub fn from_cursor(cursor: &mut Cursor<&[u8]>) -> Result { - let dummy_value = EncryptedAccountData(0); - Ok(dummy_value) + let mut u32_bytes = [0; 4]; + + cursor.read_exact(&mut u32_bytes)?; + let ciphertext_lenght = u32::from_le_bytes(u32_bytes); + let mut ciphertext = vec![0; ciphertext_lenght as usize]; + cursor.read_exact(&mut ciphertext)?; + + let mut epk_bytes = vec![0; 33]; + cursor.read_exact(&mut epk_bytes)?; + + let mut tag_bytes = [0; 1]; + cursor.read_exact(&mut tag_bytes)?; + + Ok(Self { + ciphertext, + epk: Secp256k1Point(epk_bytes), + view_tag: tag_bytes[0], + }) + } + + fn view_tag(npk: &NullifierPublicKey, ipk: &&IncomingViewingPublicKey) -> u8 { + // TODO: implement + 0 } } impl EncryptedAccountData { pub fn to_bytes(&self) -> Vec { - // TODO: implement - vec![0] + let mut bytes = Vec::new(); + let ciphertext_length: u32 = self.ciphertext.len() as u32; + bytes.extend_from_slice(&ciphertext_length.to_le_bytes()); + bytes.extend_from_slice(&self.ciphertext); + bytes.extend_from_slice(&self.epk.0); + bytes.push(self.view_tag); + + bytes } } @@ -136,10 +266,12 @@ impl PrivacyPreservingCircuitOutput { #[cfg(test)] mod tests { + use std::io::Cursor; + use risc0_zkvm::serde::from_slice; use crate::{ - EncryptedAccountData, PrivacyPreservingCircuitOutput, + EncryptedAccountData, EphemeralPublicKey, PrivacyPreservingCircuitOutput, Secp256k1Point, account::{Account, AccountWithMetadata, Commitment, Nullifier, NullifierPublicKey}, }; @@ -172,7 +304,11 @@ mod tests { data: b"post state data".to_vec(), nonce: 18446744073709551615, }], - encrypted_private_post_states: vec![EncryptedAccountData(0)], + encrypted_private_post_states: vec![EncryptedAccountData { + ciphertext: vec![255, 255, 1, 1, 2, 2], + epk: EphemeralPublicKey::from_scalar([123; 32]), + view_tag: 1, + }], new_commitments: vec![Commitment::new( &NullifierPublicKey::from(&[1; 32]), &Account::default(), @@ -187,4 +323,17 @@ mod tests { let output_from_slice: PrivacyPreservingCircuitOutput = from_slice(&bytes).unwrap(); assert_eq!(output, output_from_slice); } + + #[test] + fn test_encrypted_account_data_to_bytes_roundtrip() { + let data = EncryptedAccountData { + ciphertext: vec![255, 255, 1, 1, 2, 2], + epk: EphemeralPublicKey::from_scalar([123; 32]), + view_tag: 95, + }; + let bytes = data.to_bytes(); + let mut cursor = Cursor::new(bytes.as_slice()); + let data_from_cursor = EncryptedAccountData::from_cursor(&mut cursor).unwrap(); + assert_eq!(data, data_from_cursor); + } } diff --git a/nssa/program_methods/Cargo.toml b/nssa/program_methods/Cargo.toml index ea7e36b..0f31c9b 100644 --- a/nssa/program_methods/Cargo.toml +++ b/nssa/program_methods/Cargo.toml @@ -8,3 +8,4 @@ risc0-build = { version = "2.3.1" } [package.metadata.risc0] methods = ["guest"] + diff --git a/nssa/program_methods/guest/Cargo.lock b/nssa/program_methods/guest/Cargo.lock index 1d3063f..380583f 100644 --- a/nssa/program_methods/guest/Cargo.lock +++ b/nssa/program_methods/guest/Cargo.lock @@ -297,12 +297,24 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + [[package]] name = "bincode" version = "1.3.3" @@ -484,6 +496,27 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +[[package]] +name = "chacha20" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + [[package]] name = "cobs" version = "0.3.0" @@ -535,6 +568,18 @@ dependencies = [ "libc", ] +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.1.6" @@ -580,6 +625,16 @@ dependencies = [ "syn 2.0.104", ] +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid", + "zeroize", +] + [[package]] name = "derivative" version = "2.2.0" @@ -709,6 +764,20 @@ dependencies = [ "proc-macro-error", ] +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + [[package]] name = "educe" version = "0.6.0" @@ -733,6 +802,25 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4445909572dbd556c457c849c4ca58623d84b27c8fff1e74b0b4227d8b90d17b" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "embedded-io" version = "0.4.0" @@ -796,6 +884,16 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "fnv" version = "1.0.7" @@ -908,6 +1006,7 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", + "zeroize", ] [[package]] @@ -943,6 +1042,17 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -994,6 +1104,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "http" version = "1.3.1" @@ -1223,6 +1342,15 @@ dependencies = [ "hashbrown 0.15.4", ] +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + [[package]] name = "io-uring" version = "0.7.9" @@ -1275,6 +1403,20 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "k256" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6e3919bbaa2945715f0bb6d3934a173d1e9a59ac23767fbaaef277265a7411b" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + [[package]] name = "keccak" version = "0.1.5" @@ -1445,6 +1587,8 @@ checksum = "a5b0c77c1b780822bc749a33e39aeb2c07584ab93332303babeabb645298a76e" name = "nssa-core" version = "0.1.0" dependencies = [ + "chacha20", + "k256", "risc0-zkvm", "serde", ] @@ -1531,6 +1675,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + [[package]] name = "postcard" version = "1.1.3" @@ -1749,6 +1903,9 @@ name = "rand_core" version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.16", +] [[package]] name = "rand_core" @@ -1841,6 +1998,16 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + [[package]] name = "ring" version = "0.17.14" @@ -2162,6 +2329,20 @@ dependencies = [ "yaml-rust2", ] +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "semver" version = "1.0.26" @@ -2241,6 +2422,16 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest", + "rand_core 0.6.4", +] + [[package]] name = "slab" version = "0.4.10" @@ -2279,6 +2470,16 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stability" version = "0.2.1" @@ -3193,3 +3394,8 @@ dependencies = [ "quote", "syn 2.0.104", ] + +[[patch.unused]] +name = "k256" +version = "0.13.3" +source = "git+https://github.com/risc0/RustCrypto-elliptic-curves?tag=k256%2Fv0.13.3-risczero.1#ff5d67b095cfcc2569b7789f2079ed87ef2c7756" diff --git a/nssa/program_methods/guest/Cargo.toml b/nssa/program_methods/guest/Cargo.toml index 4b377c8..79a0c1a 100644 --- a/nssa/program_methods/guest/Cargo.toml +++ b/nssa/program_methods/guest/Cargo.toml @@ -8,3 +8,7 @@ edition = "2021" [dependencies] risc0-zkvm = { version = "2.3.1", default-features = false, features = ['std'] } nssa-core = { path = "../../core" } + +[patch.crates-io] +k256 = { git = "https://github.com/risc0/RustCrypto-elliptic-curves", tag = "k256/v0.13.3-risczero.1" } + diff --git a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs index e5f64b3..181bdc9 100644 --- a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -4,7 +4,7 @@ use nssa_core::{ account::{Account, AccountWithMetadata, Commitment, Nullifier, NullifierPublicKey}, program::{validate_execution, ProgramOutput, DEFAULT_PROGRAM_ID}, verify_membership_proof, EncryptedAccountData, EphemeralPublicKey, EphemeralSecretKey, - IncomingViewingPublicKey, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, Tag, + IncomingViewingPublicKey, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, }; fn main() { @@ -50,12 +50,18 @@ fn main() { let mut private_keys_iter = private_account_keys.iter(); let mut private_auth_iter = private_account_auth.iter(); + let mut output_index = 0; for i in 0..n_accounts { match visibility_mask[i] { 0 => { + let mut post = post_states[i].clone(); + if post.program_owner == DEFAULT_PROGRAM_ID { + // Claim account + post.program_owner = program_id; + } // Public account public_pre_states.push(pre_states[i].clone()); - public_post_states.push(post_states[i].clone()); + public_post_states.push(post); } 1 | 2 => { let new_nonce = private_nonces_iter.next().expect("Missing private nonce"); @@ -111,12 +117,20 @@ fn main() { // Compute commitment let commitment_post = Commitment::new(Npk, &post_with_updated_values); - new_commitments.push(commitment_post); // Encrypt and push post state - let encrypted_account = - EncryptedAccountData::new(&post_with_updated_values, esk, Npk, Ipk); + let encrypted_account = EncryptedAccountData::new( + &post_with_updated_values, + // &commitment_post, + esk, + Npk, + Ipk, + output_index, + ); + + new_commitments.push(commitment_post); encrypted_private_post_states.push(encrypted_account); + output_index += 1; } _ => panic!("Invalid visibility mask value"), } diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index 16a5d0d..8bbdb79 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -117,6 +117,7 @@ mod tests { #[test] fn prove_privacy_preserving_execution_circuit_public_and_private_pre_accounts() { + let program = Program::authenticated_transfer_program(); let sender = AccountWithMetadata { account: Account { balance: 100, @@ -133,17 +134,26 @@ mod tests { let balance_to_move: u128 = 37; let expected_sender_post = Account { + program_owner: program.id(), balance: 100 - balance_to_move, - ..Default::default() + ..Account::default() + }; + + let expected_recipient_post = Account { + program_owner: program.id(), + balance: balance_to_move, + nonce: 0xdeadbeef, + data: vec![], }; let expected_sender_pre = sender.clone(); + let recipient_keys = test_private_account_keys_1(); let (output, proof) = execute_and_prove( &[sender, recipient], &Program::serialize_instruction(balance_to_move).unwrap(), &[0, 2], &[0xdeadbeef], - &[(NullifierPublicKey::from(&[1; 32]), [2; 32], [3; 32])], + &[(recipient_keys.npk(), recipient_keys.ivk(), [3; 32])], &[], &Program::authenticated_transfer_program(), &[99; 32], @@ -160,8 +170,12 @@ mod tests { assert_eq!(output.new_nullifiers.len(), 0); assert_eq!(output.commitment_set_digest, [99; 32]); assert_eq!(output.encrypted_private_post_states.len(), 1); - // TODO: replace with real assertion when encryption is implemented - assert_eq!(output.encrypted_private_post_states[0].to_bytes(), vec![0]); + + let recipient_post = output.encrypted_private_post_states[0] + .clone() + .decrypt(&recipient_keys.isk, 0) + .unwrap(); + assert_eq!(recipient_post, expected_recipient_post); } #[test] @@ -210,8 +224,8 @@ mod tests { &[1, 2], &[0xdeadbeef1, 0xdeadbeef2], &[ - (sender_keys.npk(), sender_keys.ivk, [3; 32]), - (recipient_keys.npk(), recipient_keys.ivk, [5; 32]), + (sender_keys.npk(), sender_keys.ivk(), [3; 32]), + (recipient_keys.npk(), recipient_keys.ivk(), [5; 32]), ], &[( sender_keys.nsk, @@ -228,9 +242,18 @@ mod tests { assert_eq!(output.new_commitments, expected_new_commitments); assert_eq!(output.new_nullifiers, expected_new_nullifiers); assert_eq!(output.commitment_set_digest, commitment_set.digest()); - // TODO: replace with real assertion when encryption is implemented assert_eq!(output.encrypted_private_post_states.len(), 2); - assert_eq!(output.encrypted_private_post_states[0].to_bytes(), vec![0]); - assert_eq!(output.encrypted_private_post_states[1].to_bytes(), vec![0]); + + let recipient_post_1 = output.encrypted_private_post_states[0] + .clone() + .decrypt(&sender_keys.isk, 0) + .unwrap(); + assert_eq!(recipient_post_1, expected_private_account_1); + + let recipient_post_2 = output.encrypted_private_post_states[1] + .clone() + .decrypt(&recipient_keys.isk, 1) + .unwrap(); + assert_eq!(recipient_post_2, expected_private_account_2); } } diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 350743f..fc7bcaf 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -711,26 +711,30 @@ pub mod tests { pub struct TestPrivateKeys { pub nsk: NullifierSecretKey, - pub ivk: IncomingViewingPublicKey, + pub isk: [u8; 32], } impl TestPrivateKeys { pub fn npk(&self) -> NullifierPublicKey { NullifierPublicKey::from(&self.nsk) } + + pub fn ivk(&self) -> IncomingViewingPublicKey { + IncomingViewingPublicKey::from_scalar(self.isk) + } } pub fn test_private_account_keys_1() -> TestPrivateKeys { TestPrivateKeys { nsk: [13; 32], - ivk: [31; 32], + isk: [31; 32], } } pub fn test_private_account_keys_2() -> TestPrivateKeys { TestPrivateKeys { nsk: [38; 32], - ivk: [83; 32], + isk: [83; 32], } } @@ -757,7 +761,7 @@ pub mod tests { &Program::serialize_instruction(balance_to_move).unwrap(), &[0, 2], &[0xdeadbeef], - &[(recipient_keys.npk(), recipient_keys.ivk, esk)], + &[(recipient_keys.npk(), recipient_keys.ivk(), esk)], &[], &Program::authenticated_transfer_program(), &state.commitment_set_digest(), @@ -802,8 +806,8 @@ pub mod tests { &[1, 2], &new_nonces, &[ - (sender_keys.npk(), sender_keys.ivk, [3; 32]), - (recipient_keys.npk(), recipient_keys.ivk, [4; 32]), + (sender_keys.npk(), sender_keys.ivk(), [3; 32]), + (recipient_keys.npk(), recipient_keys.ivk(), [4; 32]), ], &[( sender_keys.nsk, @@ -856,7 +860,7 @@ pub mod tests { &Program::serialize_instruction(balance_to_move).unwrap(), &[1, 0], &[new_nonce], - &[(sender_keys.npk(), sender_keys.ivk, [3; 32])], + &[(sender_keys.npk(), sender_keys.ivk(), [3; 32])], &[( sender_keys.nsk, state