diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f3b7c8a..071fa8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,4 +33,4 @@ jobs: run: chmod 777 ./ci_scripts/lint-ubuntu.sh && ./ci_scripts/lint-ubuntu.sh - name: test ubuntu-latest if: success() || failure() - run: chmod 777 ./ci_scripts/test-ubuntu.sh && ./ci_scripts/test-ubuntu.sh + run: chmod 777 ./ci_scripts/test-ubuntu.sh && ./ci_scripts/test-ubuntu.sh \ No newline at end of file diff --git a/.gitignore b/.gitignore index fbe892d..61e1536 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,4 @@ data/ .idea/ .vscode/ rocksdb -Cargo.lock +Cargo.lock \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 11e3144..38fc6c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ ark-bn254 = "0.5.0" ark-ff = "0.5.0" tiny-keccak = { version = "2.0.2", features = ["keccak"] } base64 = "0.22.1" +chrono = "0.4.41" bip39 = "2.2.0" hmac-sha512 = "1.1.7" chrono = "0.4.41" diff --git a/ci_scripts/test-ubuntu.sh b/ci_scripts/test-ubuntu.sh old mode 100644 new mode 100755 index 6a1710b..5c19b36 --- a/ci_scripts/test-ubuntu.sh +++ b/ci_scripts/test-ubuntu.sh @@ -5,7 +5,15 @@ curl -L https://risczero.com/install | bash source env.sh RISC0_DEV_MODE=1 cargo test --release + cd integration_tests export NSSA_WALLET_HOME_DIR=$(pwd)/configs/debug/wallet/ export RUST_LOG=info cargo run $(pwd)/configs/debug all +echo "Try test valid proof at least once" +cargo run $(pwd)/configs/debug test_success_private_transfer_to_another_owned_account +echo "Continuing in dev mode" +RISC0_DEV_MODE=1 cargo run $(pwd)/configs/debug all +cd .. + +cd nssa/program_methods/guest && cargo test --release diff --git a/common/Cargo.toml b/common/Cargo.toml index 4725386..d438d38 100644 --- a/common/Cargo.toml +++ b/common/Cargo.toml @@ -16,6 +16,7 @@ sha2.workspace = true log.workspace = true elliptic-curve.workspace = true hex.workspace = true +nssa-core = { path = "../nssa/core", features = ["host"] } borsh.workspace = true diff --git a/common/src/rpc_primitives/requests.rs b/common/src/rpc_primitives/requests.rs index f62d567..616d482 100644 --- a/common/src/rpc_primitives/requests.rs +++ b/common/src/rpc_primitives/requests.rs @@ -53,6 +53,11 @@ pub struct GetAccountRequest { pub address: String, } +#[derive(Serialize, Deserialize, Debug)] +pub struct GetProofByCommitmentRequest { + pub commitment: nssa_core::Commitment, +} + parse_request!(HelloRequest); parse_request!(RegisterAccountRequest); parse_request!(SendTxRequest); @@ -63,6 +68,7 @@ parse_request!(GetInitialTestnetAccountsRequest); parse_request!(GetAccountBalanceRequest); parse_request!(GetTransactionByHashRequest); parse_request!(GetAccountsNoncesRequest); +parse_request!(GetProofByCommitmentRequest); parse_request!(GetAccountRequest); #[derive(Serialize, Deserialize, Debug)] @@ -115,3 +121,8 @@ pub struct GetTransactionByHashResponse { pub struct GetAccountResponse { pub account: nssa::Account, } + +#[derive(Serialize, Deserialize, Debug)] +pub struct GetProofByCommitmentResponse { + pub membership_proof: Option, +} diff --git a/common/src/sequencer_client/mod.rs b/common/src/sequencer_client/mod.rs index a75649d..abd0e55 100644 --- a/common/src/sequencer_client/mod.rs +++ b/common/src/sequencer_client/mod.rs @@ -9,7 +9,8 @@ use serde_json::Value; use crate::rpc_primitives::requests::{ GetAccountRequest, GetAccountResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse, - GetTransactionByHashRequest, GetTransactionByHashResponse, + GetProofByCommitmentRequest, GetProofByCommitmentResponse, GetTransactionByHashRequest, + GetTransactionByHashResponse, }; use crate::sequencer_client::json::AccountInitialData; use crate::transaction::{EncodedTransaction, NSSATransaction}; @@ -162,6 +163,26 @@ impl SequencerClient { Ok(resp_deser) } + ///Send transaction to sequencer + pub async fn send_tx_private( + &self, + transaction: nssa::PrivacyPreservingTransaction, + ) -> Result { + let transaction = EncodedTransaction::from(NSSATransaction::PrivacyPreserving(transaction)); + + let tx_req = SendTxRequest { + transaction: transaction.to_bytes(), + }; + + let req = serde_json::to_value(tx_req)?; + + let resp = self.call_method_with_payload("send_tx", req).await?; + + let resp_deser = serde_json::from_value(resp)?; + + Ok(resp_deser) + } + ///Get genesis id from sequencer pub async fn get_genesis_id(&self) -> Result { let genesis_req = GetGenesisIdRequest {}; @@ -195,4 +216,25 @@ impl SequencerClient { Ok(resp_deser) } + + ///Get proof for commitment + pub async fn get_proof_for_commitment( + &self, + commitment: nssa_core::Commitment, + ) -> Result, SequencerClientError> { + let acc_req = GetProofByCommitmentRequest { commitment }; + + let req = serde_json::to_value(acc_req).unwrap(); + + let resp = self + .call_method_with_payload("get_proof_for_commitment", req) + .await + .unwrap(); + + let resp_deser = serde_json::from_value::(resp) + .unwrap() + .membership_proof; + + Ok(resp_deser) + } } diff --git a/integration_tests/Cargo.toml b/integration_tests/Cargo.toml index d566bbe..6d64f98 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -8,12 +8,15 @@ anyhow.workspace = true env_logger.workspace = true log.workspace = true actix.workspace = true +bytemuck = "1.23.2" actix-web.workspace = true tokio.workspace = true hex.workspace = true tempfile.workspace = true +nssa-core = { path = "../nssa/core", features = ["host"] } + [dependencies.clap] features = ["derive", "env"] workspace = true diff --git a/integration_tests/configs/debug/sequencer/sequencer_config.json b/integration_tests/configs/debug/sequencer/sequencer_config.json index 3317a11..824d0a7 100644 --- a/integration_tests/configs/debug/sequencer/sequencer_config.json +++ b/integration_tests/configs/debug/sequencer/sequencer_config.json @@ -16,6 +16,142 @@ "balance": 20000 } ], - "signing_key": [37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, -37, 37, 37, 37, 37, 37] -} + "initial_commitments": [ + { + "npk": [ + 193, + 209, + 150, + 113, + 47, + 241, + 48, + 145, + 250, + 79, + 235, + 51, + 119, + 40, + 184, + 232, + 5, + 221, + 36, + 21, + 201, + 106, + 90, + 210, + 129, + 106, + 71, + 99, + 208, + 153, + 75, + 215 + ], + "account": { + "program_owner": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "balance": 10000, + "data": [], + "nonce": 0 + } + }, + { + "npk": [ + 27, + 250, + 136, + 142, + 88, + 128, + 138, + 21, + 49, + 183, + 118, + 160, + 117, + 114, + 110, + 47, + 136, + 87, + 60, + 70, + 59, + 60, + 18, + 223, + 23, + 147, + 241, + 5, + 184, + 103, + 225, + 105 + ], + "account": { + "program_owner": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "balance": 20000, + "data": [], + "nonce": 0 + } + } + ], + "signing_key": [ + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37, + 37 + ] +} \ No newline at end of file diff --git a/integration_tests/configs/debug/wallet/wallet_config.json b/integration_tests/configs/debug/wallet/wallet_config.json index e1edc05..f30c9a5 100644 --- a/integration_tests/configs/debug/wallet/wallet_config.json +++ b/integration_tests/configs/debug/wallet/wallet_config.json @@ -8,109 +8,540 @@ "seq_poll_retry_delay_millis": 500, "initial_accounts": [ { - "address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", - "pub_sign_key": [ - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1, - 1 - ], - "account": { - "program_owner": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "balance": 10000, - "nonce": 0, - "data": [] + "Public": { + "address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", + "pub_sign_key": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] } }, { - "address": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", - "pub_sign_key": [ - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2, - 2 - ], - "account": { - "program_owner": [ - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0 - ], - "balance": 20000, - "nonce": 0, - "data": [] + "Public": { + "address": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", + "pub_sign_key": [ + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2, + 2 + ] + } + }, + { + "Private": { + "address": "6ffe0893c4b2c956fdb769b11fe4e3b2dd36ac4bd0ad90c810844051747c8c04", + "account": { + "program_owner": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "balance": 10000, + "data": [], + "nonce": 0 + }, + "key_chain": { + "secret_spending_key": [ + 10, + 125, + 171, + 38, + 201, + 35, + 164, + 43, + 7, + 80, + 7, + 215, + 97, + 42, + 48, + 229, + 101, + 216, + 140, + 21, + 170, + 214, + 82, + 53, + 116, + 22, + 62, + 79, + 61, + 76, + 71, + 79 + ], + "private_key_holder": { + "nullifier_secret_key": [ + 228, + 136, + 4, + 156, + 33, + 40, + 194, + 172, + 95, + 168, + 201, + 33, + 24, + 30, + 126, + 197, + 156, + 113, + 64, + 162, + 131, + 210, + 110, + 60, + 24, + 154, + 86, + 59, + 184, + 95, + 245, + 176 + ], + "incoming_viewing_secret_key": [ + 197, + 33, + 51, + 200, + 1, + 121, + 60, + 52, + 233, + 234, + 12, + 166, + 196, + 227, + 187, + 1, + 10, + 101, + 183, + 105, + 140, + 28, + 152, + 217, + 109, + 220, + 112, + 103, + 253, + 110, + 98, + 6 + ], + "outgoing_viewing_secret_key": [ + 147, + 34, + 193, + 29, + 39, + 173, + 222, + 30, + 118, + 199, + 44, + 204, + 43, + 232, + 107, + 223, + 249, + 207, + 245, + 183, + 63, + 209, + 129, + 48, + 254, + 66, + 22, + 199, + 81, + 145, + 126, + 92 + ] + }, + "nullifer_public_key": [ + 193, + 209, + 150, + 113, + 47, + 241, + 48, + 145, + 250, + 79, + 235, + 51, + 119, + 40, + 184, + 232, + 5, + 221, + 36, + 21, + 201, + 106, + 90, + 210, + 129, + 106, + 71, + 99, + 208, + 153, + 75, + 215 + ], + "incoming_viewing_public_key": [ + 3, + 78, + 177, + 87, + 193, + 219, + 230, + 160, + 222, + 38, + 182, + 100, + 101, + 223, + 204, + 223, + 198, + 140, + 253, + 94, + 16, + 98, + 77, + 79, + 114, + 30, + 158, + 104, + 34, + 152, + 189, + 31, + 95 + ] + } + } + }, + { + "Private": { + "address": "4ee9de60e33da96fd72929f1485fb365bcc9c1634dd44e4ba55b1ab96692674b", + "account": { + "program_owner": [ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 + ], + "balance": 20000, + "data": [], + "nonce": 0 + }, + "key_chain": { + "secret_spending_key": [ + 153, + 109, + 202, + 226, + 97, + 212, + 77, + 147, + 75, + 107, + 153, + 106, + 89, + 167, + 49, + 230, + 122, + 78, + 167, + 146, + 14, + 180, + 206, + 107, + 96, + 193, + 255, + 122, + 207, + 30, + 142, + 99 + ], + "private_key_holder": { + "nullifier_secret_key": [ + 128, + 215, + 147, + 175, + 119, + 16, + 140, + 219, + 155, + 134, + 27, + 81, + 64, + 40, + 196, + 240, + 61, + 144, + 232, + 164, + 181, + 57, + 139, + 96, + 137, + 121, + 140, + 29, + 169, + 68, + 187, + 65 + ], + "incoming_viewing_secret_key": [ + 185, + 121, + 146, + 213, + 13, + 3, + 93, + 206, + 25, + 127, + 155, + 21, + 155, + 115, + 130, + 27, + 57, + 5, + 116, + 80, + 62, + 214, + 67, + 228, + 147, + 189, + 28, + 200, + 62, + 152, + 178, + 103 + ], + "outgoing_viewing_secret_key": [ + 163, + 58, + 118, + 160, + 175, + 86, + 72, + 91, + 81, + 69, + 150, + 154, + 113, + 211, + 118, + 110, + 25, + 156, + 250, + 67, + 212, + 198, + 147, + 231, + 213, + 136, + 212, + 198, + 192, + 255, + 126, + 122 + ] + }, + "nullifer_public_key": [ + 27, + 250, + 136, + 142, + 88, + 128, + 138, + 21, + 49, + 183, + 118, + 160, + 117, + 114, + 110, + 47, + 136, + 87, + 60, + 70, + 59, + 60, + 18, + 223, + 23, + 147, + 241, + 5, + 184, + 103, + 225, + 105 + ], + "incoming_viewing_public_key": [ + 2, + 56, + 160, + 1, + 22, + 197, + 187, + 214, + 204, + 221, + 84, + 87, + 12, + 204, + 0, + 119, + 116, + 176, + 6, + 149, + 145, + 100, + 211, + 162, + 19, + 158, + 197, + 112, + 142, + 172, + 1, + 98, + 226 + ] + } } } ] -} - +} \ No newline at end of file diff --git a/integration_tests/src/lib.rs b/integration_tests/src/lib.rs index f4fa565..da4f7c5 100644 --- a/integration_tests/src/lib.rs +++ b/integration_tests/src/lib.rs @@ -6,13 +6,15 @@ use clap::Parser; use common::sequencer_client::SequencerClient; use log::{info, warn}; use nssa::program::Program; +use nssa_core::{NullifierPublicKey, encryption::shared_key_derivation::Secp256k1Point}; use sequencer_core::config::SequencerConfig; use sequencer_runner::startup_sequencer; use tempfile::TempDir; use tokio::task::JoinHandle; use wallet::{ - Command, - helperfunctions::{fetch_config, fetch_persistent_accounts}, + Command, SubcommandReturnValue, WalletCore, + config::PersistentAccountData, + helperfunctions::{fetch_config, fetch_persistent_accounts, produce_account_addr_from_hex}, }; #[derive(Parser, Debug)] @@ -27,6 +29,11 @@ struct Args { pub const ACC_SENDER: &str = "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f"; pub const ACC_RECEIVER: &str = "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766"; +pub const ACC_SENDER_PRIVATE: &str = + "6ffe0893c4b2c956fdb769b11fe4e3b2dd36ac4bd0ad90c810844051747c8c04"; +pub const ACC_RECEIVER_PRIVATE: &str = + "4ee9de60e33da96fd72929f1485fb365bcc9c1634dd44e4ba55b1ab96692674b"; + pub const TIME_TO_WAIT_FOR_BLOCK_SECONDS: u64 = 12; #[allow(clippy::type_complexity)] @@ -83,7 +90,8 @@ pub async fn post_test(residual: (ServerHandle, JoinHandle>, TempDir) } pub async fn test_success() { - let command = Command::SendNativeTokenTransfer { + info!("test_success"); + let command = Command::SendNativeTokenTransferPublic { from: ACC_SENDER.to_string(), to: ACC_RECEIVER.to_string(), amount: 100, @@ -118,7 +126,8 @@ pub async fn test_success() { } pub async fn test_success_move_to_another_account() { - let command = Command::RegisterAccount {}; + info!("test_success_move_to_another_account"); + let command = Command::RegisterAccountPublic {}; let wallet_config = fetch_config().unwrap(); @@ -131,10 +140,10 @@ pub async fn test_success_move_to_another_account() { let mut new_persistent_account_addr = String::new(); for per_acc in persistent_accounts { - if (per_acc.address.to_string() != ACC_RECEIVER) - && (per_acc.address.to_string() != ACC_SENDER) + if (per_acc.address().to_string() != ACC_RECEIVER) + && (per_acc.address().to_string() != ACC_SENDER) { - new_persistent_account_addr = per_acc.address.to_string(); + new_persistent_account_addr = per_acc.address().to_string(); } } @@ -142,7 +151,7 @@ pub async fn test_success_move_to_another_account() { panic!("Failed to produce new account, not present in persistent accounts"); } - let command = Command::SendNativeTokenTransfer { + let command = Command::SendNativeTokenTransferPublic { from: ACC_SENDER.to_string(), to: new_persistent_account_addr.clone(), amount: 100, @@ -173,7 +182,8 @@ pub async fn test_success_move_to_another_account() { } pub async fn test_failure() { - let command = Command::SendNativeTokenTransfer { + info!("test_failure"); + let command = Command::SendNativeTokenTransferPublic { from: ACC_SENDER.to_string(), to: ACC_RECEIVER.to_string(), amount: 1000000, @@ -210,7 +220,8 @@ pub async fn test_failure() { } pub async fn test_success_two_transactions() { - let command = Command::SendNativeTokenTransfer { + info!("test_success_two_transactions"); + let command = Command::SendNativeTokenTransferPublic { from: ACC_SENDER.to_string(), to: ACC_RECEIVER.to_string(), amount: 100, @@ -243,7 +254,7 @@ pub async fn test_success_two_transactions() { info!("First TX Success!"); - let command = Command::SendNativeTokenTransfer { + let command = Command::SendNativeTokenTransferPublic { from: ACC_SENDER.to_string(), to: ACC_RECEIVER.to_string(), amount: 100, @@ -274,6 +285,7 @@ pub async fn test_success_two_transactions() { } pub async fn test_get_account() { + info!("test_get_account"); let wallet_config = fetch_config().unwrap(); let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); @@ -292,7 +304,629 @@ pub async fn test_get_account() { assert_eq!(account.nonce, 0); } +/// This test creates a new token using the token program. After creating the token, the test executes a +/// token transfer to a new account. +pub async fn test_success_token_program() { + let wallet_config = fetch_config().unwrap(); + + // Create new account for the token definition + wallet::execute_subcommand(Command::RegisterAccountPublic {}) + .await + .unwrap(); + // Create new account for the token supply holder + wallet::execute_subcommand(Command::RegisterAccountPublic {}) + .await + .unwrap(); + // Create new account for receiving a token transaction + wallet::execute_subcommand(Command::RegisterAccountPublic {}) + .await + .unwrap(); + + let persistent_accounts = fetch_persistent_accounts().unwrap(); + + let mut new_persistent_accounts_addr = Vec::new(); + + for per_acc in persistent_accounts { + match per_acc { + PersistentAccountData::Public(per_acc) => { + if (per_acc.address.to_string() != ACC_RECEIVER) + && (per_acc.address.to_string() != ACC_SENDER) + { + new_persistent_accounts_addr.push(per_acc.address); + } + } + _ => continue, + } + } + + let [definition_addr, supply_addr, recipient_addr] = new_persistent_accounts_addr + .try_into() + .expect("Failed to produce new account, not present in persistent accounts"); + + // Create new token + let command = Command::CreateNewToken { + definition_addr: definition_addr.to_string(), + supply_addr: supply_addr.to_string(), + name: "A NAME".to_string(), + total_supply: 37, + }; + wallet::execute_subcommand(command).await.unwrap(); + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + + // Check the status of the token definition account is the expected after the execution + let definition_acc = seq_client + .get_account(definition_addr.to_string()) + .await + .unwrap() + .account; + + assert_eq!(definition_acc.program_owner, Program::token().id()); + // The data of a token definition account has the following layout: + // [ 0x00 || name (6 bytes) || total supply (little endian 16 bytes) ] + assert_eq!( + definition_acc.data, + vec![ + 0, 65, 32, 78, 65, 77, 69, 37, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + + // Check the status of the token holding account with the total supply is the expected after the execution + let supply_acc = seq_client + .get_account(supply_addr.to_string()) + .await + .unwrap() + .account; + + // The account must be owned by the token program + assert_eq!(supply_acc.program_owner, Program::token().id()); + // The data of a token definition account has the following layout: + // [ 0x01 || corresponding_token_definition_id (32 bytes) || balance (little endian 16 bytes) ] + // First byte of the data equal to 1 means it's a token holding account + assert_eq!(supply_acc.data[0], 1); + // Bytes from 1 to 33 represent the id of the token this account is associated with. + // In this example, this is a token account of the newly created token, so it is expected + // to be equal to the address of the token definition account. + assert_eq!( + &supply_acc.data[1..33], + nssa::AccountId::from(&definition_addr).to_bytes() + ); + assert_eq!( + u128::from_le_bytes(supply_acc.data[33..].try_into().unwrap()), + 37 + ); + + // Transfer 7 tokens from `supply_acc` to the account at address `recipient_addr` + let command = Command::TransferToken { + sender_addr: supply_addr.to_string(), + recipient_addr: recipient_addr.to_string(), + balance_to_move: 7, + }; + wallet::execute_subcommand(command).await.unwrap(); + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + // Check the status of the account at `supply_addr` is the expected after the execution + let supply_acc = seq_client + .get_account(supply_addr.to_string()) + .await + .unwrap() + .account; + // The account must be owned by the token program + assert_eq!(supply_acc.program_owner, Program::token().id()); + // First byte equal to 1 means it's a token holding account + assert_eq!(supply_acc.data[0], 1); + // Bytes from 1 to 33 represent the id of the token this account is associated with. + assert_eq!( + &supply_acc.data[1..33], + nssa::AccountId::from(&definition_addr).to_bytes() + ); + assert_eq!( + u128::from_le_bytes(supply_acc.data[33..].try_into().unwrap()), + 30 + ); + + // Check the status of the account at `recipient_addr` is the expected after the execution + let recipient_acc = seq_client + .get_account(recipient_addr.to_string()) + .await + .unwrap() + .account; + + // The account must be owned by the token program + assert_eq!(recipient_acc.program_owner, Program::token().id()); + // First byte equal to 1 means it's a token holding account + assert_eq!(recipient_acc.data[0], 1); + // Bytes from 1 to 33 represent the id of the token this account is associated with. + assert_eq!( + &recipient_acc.data[1..33], + nssa::AccountId::from(&definition_addr).to_bytes() + ); + assert_eq!( + u128::from_le_bytes(recipient_acc.data[33..].try_into().unwrap()), + 7 + ); +} + +pub async fn test_success_private_transfer_to_another_owned_account() { + info!("test_success_private_transfer_to_another_owned_account"); + let command = Command::SendNativeTokenTransferPrivate { + from: ACC_SENDER_PRIVATE.to_string(), + to: ACC_RECEIVER_PRIVATE.to_string(), + amount: 100, + }; + + let from = produce_account_addr_from_hex(ACC_SENDER_PRIVATE.to_string()).unwrap(); + let to = produce_account_addr_from_hex(ACC_RECEIVER_PRIVATE.to_string()).unwrap(); + + let wallet_config = fetch_config().unwrap(); + + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + + let mut wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + + wallet::execute_subcommand(command).await.unwrap(); + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment1 = { + let from_acc = wallet_storage + .storage + .user_data + .get_private_account_mut(&from) + .unwrap(); + + from_acc.1.program_owner = nssa::program::Program::authenticated_transfer_program().id(); + from_acc.1.balance -= 100; + from_acc.1.nonce += 1; + + nssa_core::Commitment::new(&from_acc.0.nullifer_public_key, &from_acc.1) + }; + + let new_commitment2 = { + let to_acc = wallet_storage + .storage + .user_data + .get_private_account_mut(&to) + .unwrap(); + + to_acc.1.program_owner = nssa::program::Program::authenticated_transfer_program().id(); + to_acc.1.balance += 100; + to_acc.1.nonce += 1; + + nssa_core::Commitment::new(&to_acc.0.nullifer_public_key, &to_acc.1) + }; + + let proof1 = seq_client + .get_proof_for_commitment(new_commitment1) + .await + .unwrap() + .unwrap(); + let proof2 = seq_client + .get_proof_for_commitment(new_commitment2) + .await + .unwrap() + .unwrap(); + + println!("New proof is {proof1:#?}"); + println!("New proof is {proof2:#?}"); + + info!("Success!"); +} + +pub async fn test_success_private_transfer_to_another_foreign_account() { + info!("test_success_private_transfer_to_another_foreign_account"); + let to_npk_orig = NullifierPublicKey([42; 32]); + let to_npk = hex::encode(to_npk_orig.0); + let to_ipk = Secp256k1Point::from_scalar(to_npk_orig.0); + + let command = Command::SendNativeTokenTransferPrivateForeignAccount { + from: ACC_SENDER_PRIVATE.to_string(), + to_npk, + to_ipk: hex::encode(to_ipk.0), + amount: 100, + }; + + let from = produce_account_addr_from_hex(ACC_SENDER_PRIVATE.to_string()).unwrap(); + + let wallet_config = fetch_config().unwrap(); + + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + + let mut wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + + let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + + println!("SUB RET is {sub_ret:#?}"); + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment1 = { + let from_acc = wallet_storage + .storage + .user_data + .get_private_account_mut(&from) + .unwrap(); + + from_acc.1.program_owner = nssa::program::Program::authenticated_transfer_program().id(); + from_acc.1.balance -= 100; + from_acc.1.nonce += 1; + + nssa_core::Commitment::new(&from_acc.0.nullifer_public_key, &from_acc.1) + }; + + let new_commitment2 = { + let to_acc = nssa_core::account::Account { + program_owner: nssa::program::Program::authenticated_transfer_program().id(), + balance: 100, + data: vec![], + nonce: 1, + }; + + nssa_core::Commitment::new(&to_npk_orig, &to_acc) + }; + + let proof1 = seq_client + .get_proof_for_commitment(new_commitment1) + .await + .unwrap() + .unwrap(); + let proof2 = seq_client + .get_proof_for_commitment(new_commitment2) + .await + .unwrap() + .unwrap(); + + println!("New proof is {proof1:#?}"); + println!("New proof is {proof2:#?}"); + + info!("Success!"); +} + +pub async fn test_success_private_transfer_to_another_owned_account_claiming_path() { + info!("test_success_private_transfer_to_another_owned_account_claiming_path"); + let command = Command::RegisterAccountPrivate {}; + + let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + + let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else { + panic!("FAILED TO REGISTER ACCOUNT"); + }; + + let wallet_config = fetch_config().unwrap(); + + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + + let mut wallet_storage = + WalletCore::start_from_config_update_chain(wallet_config.clone()).unwrap(); + + let (to_keys, mut to_acc) = wallet_storage + .storage + .user_data + .user_private_accounts + .get(&to_addr) + .cloned() + .unwrap(); + + let command = Command::SendNativeTokenTransferPrivateForeignAccount { + from: ACC_SENDER_PRIVATE.to_string(), + to_npk: hex::encode(to_keys.nullifer_public_key.0), + to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0), + amount: 100, + }; + + let from = produce_account_addr_from_hex(ACC_SENDER_PRIVATE.to_string()).unwrap(); + + let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else { + panic!("FAILED TO SEND TX"); + }; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment1 = { + let from_acc = wallet_storage + .storage + .user_data + .get_private_account_mut(&from) + .unwrap(); + + from_acc.1.program_owner = nssa::program::Program::authenticated_transfer_program().id(); + from_acc.1.balance -= 100; + from_acc.1.nonce += 1; + + nssa_core::Commitment::new(&from_acc.0.nullifer_public_key, &from_acc.1) + }; + + let new_commitment2 = { + to_acc.program_owner = nssa::program::Program::authenticated_transfer_program().id(); + to_acc.balance = 100; + to_acc.nonce = 1; + + nssa_core::Commitment::new(&to_keys.nullifer_public_key, &to_acc) + }; + + let proof1 = seq_client + .get_proof_for_commitment(new_commitment1) + .await + .unwrap() + .unwrap(); + let proof2 = seq_client + .get_proof_for_commitment(new_commitment2) + .await + .unwrap() + .unwrap(); + + println!("New proof is {proof1:#?}"); + println!("New proof is {proof2:#?}"); + + let command = Command::ClaimPrivateAccount { + tx_hash, + acc_addr: hex::encode(to_addr), + ciph_id: 1, + }; + + wallet::execute_subcommand(command).await.unwrap(); + + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + + let (_, to_res_acc) = wallet_storage + .storage + .user_data + .get_private_account(&to_addr) + .unwrap(); + + assert_eq!(to_res_acc.balance, 100); + + info!("Success!"); +} + +pub async fn test_success_deshielded_transfer_to_another_account() { + info!("test_success_deshielded_transfer_to_another_account"); + let command = Command::SendNativeTokenTransferDeshielded { + from: ACC_SENDER_PRIVATE.to_string(), + to: ACC_RECEIVER.to_string(), + amount: 100, + }; + + let from = produce_account_addr_from_hex(ACC_SENDER_PRIVATE.to_string()).unwrap(); + + let wallet_config = fetch_config().unwrap(); + + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + + let mut wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + + wallet::execute_subcommand(command).await.unwrap(); + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment1 = { + let from_acc = wallet_storage + .storage + .user_data + .get_private_account_mut(&from) + .unwrap(); + + from_acc.1.program_owner = nssa::program::Program::authenticated_transfer_program().id(); + from_acc.1.balance -= 100; + from_acc.1.nonce += 1; + + nssa_core::Commitment::new(&from_acc.0.nullifer_public_key, &from_acc.1) + }; + + let proof1 = seq_client + .get_proof_for_commitment(new_commitment1) + .await + .unwrap() + .unwrap(); + + let acc_2_balance = seq_client + .get_account_balance(ACC_RECEIVER.to_string()) + .await + .unwrap(); + + println!("New proof is {proof1:#?}"); + assert_eq!(acc_2_balance.balance, 20100); + + info!("Success!"); +} + +pub async fn test_success_shielded_transfer_to_another_owned_account() { + info!("test_success_shielded_transfer_to_another_owned_account"); + let command = Command::SendNativeTokenTransferShielded { + from: ACC_SENDER.to_string(), + to: ACC_RECEIVER_PRIVATE.to_string(), + amount: 100, + }; + + let to = produce_account_addr_from_hex(ACC_RECEIVER_PRIVATE.to_string()).unwrap(); + + let wallet_config = fetch_config().unwrap(); + + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + + let mut wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + + wallet::execute_subcommand(command).await.unwrap(); + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment2 = { + let to_acc = wallet_storage + .storage + .user_data + .get_private_account_mut(&to) + .unwrap(); + + to_acc.1.program_owner = nssa::program::Program::authenticated_transfer_program().id(); + to_acc.1.balance += 100; + to_acc.1.nonce += 1; + + nssa_core::Commitment::new(&to_acc.0.nullifer_public_key, &to_acc.1) + }; + + let acc_1_balance = seq_client + .get_account_balance(ACC_SENDER.to_string()) + .await + .unwrap(); + + let proof2 = seq_client + .get_proof_for_commitment(new_commitment2) + .await + .unwrap() + .unwrap(); + + assert_eq!(acc_1_balance.balance, 9900); + + println!("New proof is {proof2:#?}"); + + info!("Success!"); +} + +pub async fn test_success_shielded_transfer_to_another_foreign_account() { + info!("test_success_shielded_transfer_to_another_foreign_account"); + let to_npk_orig = NullifierPublicKey([42; 32]); + let to_npk = hex::encode(to_npk_orig.0); + let to_ipk = Secp256k1Point::from_scalar(to_npk_orig.0); + + let command = Command::SendNativeTokenTransferShieldedForeignAccount { + from: ACC_SENDER.to_string(), + to_npk, + to_ipk: hex::encode(to_ipk.0), + amount: 100, + }; + + let wallet_config = fetch_config().unwrap(); + + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + + wallet::execute_subcommand(command).await.unwrap(); + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment2 = { + let to_acc = nssa_core::account::Account { + program_owner: nssa::program::Program::authenticated_transfer_program().id(), + balance: 100, + data: vec![], + nonce: 1, + }; + + nssa_core::Commitment::new(&to_npk_orig, &to_acc) + }; + + let acc_1_balance = seq_client + .get_account_balance(ACC_SENDER.to_string()) + .await + .unwrap(); + + let proof2 = seq_client + .get_proof_for_commitment(new_commitment2) + .await + .unwrap() + .unwrap(); + + assert_eq!(acc_1_balance.balance, 9900); + println!("New proof is {proof2:#?}"); + + info!("Success!"); +} + +pub async fn test_success_shielded_transfer_to_another_owned_account_claiming_path() { + info!("test_success_shielded_transfer_to_another_owned_account_claiming_path"); + let command = Command::RegisterAccountPrivate {}; + + let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + + let SubcommandReturnValue::RegisterAccount { addr: to_addr } = sub_ret else { + panic!("FAILED TO REGISTER ACCOUNT"); + }; + + let wallet_config = fetch_config().unwrap(); + + let seq_client = SequencerClient::new(wallet_config.sequencer_addr.clone()).unwrap(); + + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config.clone()).unwrap(); + + let (to_keys, mut to_acc) = wallet_storage + .storage + .user_data + .user_private_accounts + .get(&to_addr) + .cloned() + .unwrap(); + + let command = Command::SendNativeTokenTransferShieldedForeignAccount { + from: ACC_SENDER.to_string(), + to_npk: hex::encode(to_keys.nullifer_public_key.0), + to_ipk: hex::encode(to_keys.incoming_viewing_public_key.0), + amount: 100, + }; + + let sub_ret = wallet::execute_subcommand(command).await.unwrap(); + + let SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } = sub_ret else { + panic!("FAILED TO SEND TX"); + }; + + info!("Waiting for next block creation"); + tokio::time::sleep(Duration::from_secs(TIME_TO_WAIT_FOR_BLOCK_SECONDS)).await; + + let new_commitment2 = { + to_acc.program_owner = nssa::program::Program::authenticated_transfer_program().id(); + to_acc.balance = 100; + to_acc.nonce = 1; + + nssa_core::Commitment::new(&to_keys.nullifer_public_key, &to_acc) + }; + + let acc_1_balance = seq_client + .get_account_balance(ACC_SENDER.to_string()) + .await + .unwrap(); + + let proof2 = seq_client + .get_proof_for_commitment(new_commitment2) + .await + .unwrap() + .unwrap(); + + assert_eq!(acc_1_balance.balance, 9900); + println!("New proof is {proof2:#?}"); + + let command = Command::ClaimPrivateAccount { + tx_hash, + acc_addr: hex::encode(to_addr), + ciph_id: 0, + }; + + wallet::execute_subcommand(command).await.unwrap(); + + let wallet_storage = WalletCore::start_from_config_update_chain(wallet_config).unwrap(); + + let (_, to_res_acc) = wallet_storage + .storage + .user_data + .get_private_account(&to_addr) + .unwrap(); + + assert_eq!(to_res_acc.balance, 100); + + info!("Success!"); +} + pub async fn test_pinata() { + info!("test_pinata"); let pinata_addr = "cafe".repeat(16); let pinata_prize = 150; let solution = 989106; @@ -359,6 +993,9 @@ pub async fn main_tests_runner() -> Result<()> { } = args; match test_name.as_str() { + "test_success_token_program" => { + test_cleanup_wrap!(home_dir, test_success_token_program); + } "test_success_move_to_another_account" => { test_cleanup_wrap!(home_dir, test_success_move_to_another_account); } @@ -368,12 +1005,54 @@ pub async fn main_tests_runner() -> Result<()> { "test_failure" => { test_cleanup_wrap!(home_dir, test_failure); } - "test_get_account_wallet_command" => { + "test_get_account" => { test_cleanup_wrap!(home_dir, test_get_account); } "test_success_two_transactions" => { test_cleanup_wrap!(home_dir, test_success_two_transactions); } + "test_success_private_transfer_to_another_owned_account" => { + test_cleanup_wrap!( + home_dir, + test_success_private_transfer_to_another_owned_account + ); + } + "test_success_private_transfer_to_another_foreign_account" => { + test_cleanup_wrap!( + home_dir, + test_success_private_transfer_to_another_foreign_account + ); + } + "test_success_private_transfer_to_another_owned_account_claiming_path" => { + test_cleanup_wrap!( + home_dir, + test_success_private_transfer_to_another_owned_account_claiming_path + ); + } + "test_success_deshielded_transfer_to_another_account" => { + test_cleanup_wrap!( + home_dir, + test_success_deshielded_transfer_to_another_account + ); + } + "test_success_shielded_transfer_to_another_owned_account" => { + test_cleanup_wrap!( + home_dir, + test_success_shielded_transfer_to_another_owned_account + ); + } + "test_success_shielded_transfer_to_another_foreign_account" => { + test_cleanup_wrap!( + home_dir, + test_success_shielded_transfer_to_another_foreign_account + ); + } + "test_success_shielded_transfer_to_another_owned_account_claiming_path" => { + test_cleanup_wrap!( + home_dir, + test_success_shielded_transfer_to_another_owned_account_claiming_path + ); + } "test_pinata" => { test_cleanup_wrap!(home_dir, test_pinata); } @@ -382,8 +1061,36 @@ pub async fn main_tests_runner() -> Result<()> { test_cleanup_wrap!(home_dir, test_success); test_cleanup_wrap!(home_dir, test_failure); test_cleanup_wrap!(home_dir, test_success_two_transactions); + test_cleanup_wrap!(home_dir, test_success_token_program); + test_cleanup_wrap!( + home_dir, + test_success_private_transfer_to_another_owned_account + ); + test_cleanup_wrap!( + home_dir, + test_success_private_transfer_to_another_foreign_account + ); + test_cleanup_wrap!( + home_dir, + test_success_deshielded_transfer_to_another_account + ); + test_cleanup_wrap!( + home_dir, + test_success_shielded_transfer_to_another_owned_account + ); + test_cleanup_wrap!( + home_dir, + test_success_shielded_transfer_to_another_foreign_account + ); + test_cleanup_wrap!( + home_dir, + test_success_private_transfer_to_another_owned_account_claiming_path + ); + test_cleanup_wrap!( + home_dir, + test_success_shielded_transfer_to_another_owned_account_claiming_path + ); test_cleanup_wrap!(home_dir, test_pinata); - test_cleanup_wrap!(home_dir, test_get_account); } _ => { anyhow::bail!("Unknown test name"); diff --git a/key_protocol/src/key_management/ephemeral_key_holder.rs b/key_protocol/src/key_management/ephemeral_key_holder.rs index 9ae3842..d7a0ee7 100644 --- a/key_protocol/src/key_management/ephemeral_key_holder.rs +++ b/key_protocol/src/key_management/ephemeral_key_holder.rs @@ -2,6 +2,7 @@ use nssa_core::{ NullifierPublicKey, SharedSecretKey, encryption::{EphemeralPublicKey, EphemeralSecretKey, IncomingViewingPublicKey}, }; +use rand::{RngCore, rngs::OsRng}; use sha2::Digest; use crate::key_management::secret_holders::OutgoingViewingSecretKey; @@ -12,6 +13,17 @@ pub struct EphemeralKeyHolder { ephemeral_secret_key: EphemeralSecretKey, } +pub fn produce_one_sided_shared_secret_receiver( + ipk: &IncomingViewingPublicKey, +) -> (SharedSecretKey, EphemeralPublicKey) { + let mut esk = [0; 32]; + OsRng.fill_bytes(&mut esk); + ( + SharedSecretKey::new(&esk, ipk), + EphemeralPublicKey::from_scalar(esk), + ) +} + impl EphemeralKeyHolder { pub fn new( receiver_nullifier_public_key: NullifierPublicKey, diff --git a/key_protocol/src/key_management/mod.rs b/key_protocol/src/key_management/mod.rs index e31b373..a0f720f 100644 --- a/key_protocol/src/key_management/mod.rs +++ b/key_protocol/src/key_management/mod.rs @@ -21,6 +21,18 @@ pub struct KeyChain { pub incoming_viewing_public_key: IncomingViewingPublicKey, } +pub fn produce_user_address_foreign_account( + npk: &NullifierPublicKey, + ipk: &IncomingViewingPublicKey, +) -> [u8; 32] { + let mut hasher = sha2::Sha256::new(); + + hasher.update(npk); + hasher.update(ipk.to_bytes()); + + ::from(hasher.finalize_fixed()) +} + impl KeyChain { pub fn new_os_random() -> Self { //Currently dropping SeedHolder at the end of initialization. diff --git a/key_protocol/src/key_management/secret_holders.rs b/key_protocol/src/key_management/secret_holders.rs index 80ec1b0..89da95c 100644 --- a/key_protocol/src/key_management/secret_holders.rs +++ b/key_protocol/src/key_management/secret_holders.rs @@ -26,9 +26,9 @@ pub type OutgoingViewingSecretKey = Scalar; #[derive(Serialize, Deserialize, Debug, Clone)] ///Private key holder. Produces public keys. Can produce address. Can produce shared secret for recepient. pub struct PrivateKeyHolder { - pub(crate) nullifier_secret_key: NullifierSecretKey, + pub nullifier_secret_key: NullifierSecretKey, pub(crate) incoming_viewing_secret_key: IncomingViewingSecretKey, - pub(crate) outgoing_viewing_secret_key: OutgoingViewingSecretKey, + pub outgoing_viewing_secret_key: OutgoingViewingSecretKey, } impl SeedHolder { diff --git a/key_protocol/src/key_protocol_core/mod.rs b/key_protocol/src/key_protocol_core/mod.rs index 58f60ce..24bb26e 100644 --- a/key_protocol/src/key_protocol_core/mod.rs +++ b/key_protocol/src/key_protocol_core/mod.rs @@ -13,7 +13,7 @@ pub struct NSSAUserData { ///Map for all user public accounts pub pub_account_signing_keys: HashMap, ///Map for all user private accounts - user_private_accounts: HashMap, + pub user_private_accounts: HashMap, } impl NSSAUserData { @@ -30,10 +30,10 @@ impl NSSAUserData { } fn valid_private_key_transaction_pairing_check( - accounts_keys_map: &HashMap, + accounts_keys_map: &HashMap, ) -> bool { let mut check_res = true; - for (addr, key) in accounts_keys_map { + for (addr, (key, _)) in accounts_keys_map { if nssa::Address::new(key.produce_user_address()) != *addr { check_res = false; } @@ -43,7 +43,7 @@ impl NSSAUserData { pub fn new_with_accounts( accounts_keys: HashMap, - accounts_key_chains: HashMap, + accounts_key_chains: HashMap, ) -> Result { if !Self::valid_public_key_transaction_pairing_check(&accounts_keys) { anyhow::bail!( @@ -90,15 +90,27 @@ impl NSSAUserData { let key_chain = KeyChain::new_os_random(); let address = nssa::Address::new(key_chain.produce_user_address()); - self.user_private_accounts.insert(address, key_chain); + self.user_private_accounts + .insert(address, (key_chain, nssa_core::account::Account::default())); address } /// Returns the signing key for public transaction signatures - pub fn get_private_account_key_chain(&self, address: &nssa::Address) -> Option<&KeyChain> { + pub fn get_private_account( + &self, + address: &nssa::Address, + ) -> Option<&(KeyChain, nssa_core::account::Account)> { self.user_private_accounts.get(address) } + + /// Returns the signing key for public transaction signatures + pub fn get_private_account_mut( + &mut self, + address: &nssa::Address, + ) -> Option<&mut (KeyChain, nssa_core::account::Account)> { + self.user_private_accounts.get_mut(address) + } } impl Default for NSSAUserData { @@ -123,9 +135,7 @@ mod tests { assert!(is_private_key_generated); - let is_key_chain_generated = user_data - .get_private_account_key_chain(&addr_private) - .is_some(); + let is_key_chain_generated = user_data.get_private_account(&addr_private).is_some(); assert!(is_key_chain_generated); } diff --git a/nssa/Cargo.toml b/nssa/Cargo.toml index 68d26fc..4d21e46 100644 --- a/nssa/Cargo.toml +++ b/nssa/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] thiserror = "2.0.12" -risc0-zkvm = "3.0.3" +risc0-zkvm = { version = "3.0.3", features = ['std'] } nssa-core = { path = "core", features = ["host"] } program-methods = { path = "program_methods" } serde = "1.0.219" diff --git a/nssa/core/Cargo.toml b/nssa/core/Cargo.toml index 2eb0ce2..3b841fd 100644 --- a/nssa/core/Cargo.toml +++ b/nssa/core/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -risc0-zkvm = { version = "3.0.3" } +risc0-zkvm = { version = "3.0.3", features = ['std'] } serde = { version = "1.0", default-features = false } thiserror = { version = "2.0.12", optional = true } bytemuck = { version = "1.13", optional = true } diff --git a/nssa/core/src/account.rs b/nssa/core/src/account.rs index 688611e..ddae797 100644 --- a/nssa/core/src/account.rs +++ b/nssa/core/src/account.rs @@ -2,7 +2,7 @@ use crate::program::ProgramId; use serde::{Deserialize, Serialize}; pub type Nonce = u128; -type Data = Vec; +pub type Data = Vec; /// Account to be used both in public and private contexts #[derive(Serialize, Deserialize, Clone, Default, PartialEq, Eq)] @@ -14,11 +14,34 @@ pub struct Account { pub nonce: Nonce, } +/// A fingerprint of the owner of an account. This can be, for example, an `Address` in case the account +/// is public, or a `NullifierPublicKey` in case the account is private. +#[derive(Serialize, Deserialize, Clone, PartialEq, Eq)] +#[cfg_attr(any(feature = "host", test), derive(Debug))] +pub struct AccountId(pub(super) [u8; 32]); +impl AccountId { + pub fn new(value: [u8; 32]) -> Self { + Self(value) + } +} + #[derive(Serialize, Deserialize, Clone)] #[cfg_attr(any(feature = "host", test), derive(Debug, PartialEq, Eq))] pub struct AccountWithMetadata { pub account: Account, pub is_authorized: bool, + pub account_id: AccountId, +} + +#[cfg(feature = "host")] +impl AccountWithMetadata { + pub fn new(account: Account, is_authorized: bool, account_id: impl Into) -> Self { + Self { + account, + is_authorized, + account_id: account_id.into(), + } + } } #[cfg(test)] @@ -54,4 +77,21 @@ mod tests { assert_eq!(new_acc.program_owner, DEFAULT_PROGRAM_ID); } + + #[cfg(feature = "host")] + #[test] + fn test_account_with_metadata_constructor() { + let account = Account { + program_owner: [1, 2, 3, 4, 5, 6, 7, 8], + balance: 1337, + data: b"testing_account_with_metadata_constructor".to_vec(), + nonce: 0xdeadbeef, + }; + let fingerprint = AccountId::new([8; 32]); + let new_acc_with_metadata = + AccountWithMetadata::new(account.clone(), true, fingerprint.clone()); + assert_eq!(new_acc_with_metadata.account, account); + assert!(new_acc_with_metadata.is_authorized); + assert_eq!(new_acc_with_metadata.account_id, fingerprint); + } } diff --git a/nssa/core/src/circuit_io.rs b/nssa/core/src/circuit_io.rs index e619b2d..14feef7 100644 --- a/nssa/core/src/circuit_io.rs +++ b/nssa/core/src/circuit_io.rs @@ -41,7 +41,7 @@ mod tests { use super::*; use crate::{ Commitment, Nullifier, NullifierPublicKey, - account::{Account, AccountWithMetadata}, + account::{Account, AccountId, AccountWithMetadata}, }; use risc0_zkvm::serde::from_slice; @@ -49,24 +49,26 @@ mod tests { fn test_privacy_preserving_circuit_output_to_bytes_is_compatible_with_from_slice() { let output = PrivacyPreservingCircuitOutput { public_pre_states: vec![ - AccountWithMetadata { - account: Account { + AccountWithMetadata::new( + Account { program_owner: [1, 2, 3, 4, 5, 6, 7, 8], balance: 12345678901234567890, data: b"test data".to_vec(), nonce: 18446744073709551614, }, - is_authorized: true, - }, - AccountWithMetadata { - account: Account { + true, + AccountId::new([0; 32]), + ), + AccountWithMetadata::new( + Account { program_owner: [9, 9, 9, 8, 8, 8, 7, 7], balance: 123123123456456567112, data: b"test data".to_vec(), nonce: 9999999999999999999999, }, - is_authorized: false, - }, + false, + AccountId::new([1; 32]), + ), ], public_post_states: vec![Account { program_owner: [1, 2, 3, 4, 5, 6, 7, 8], diff --git a/nssa/core/src/encoding.rs b/nssa/core/src/encoding.rs index dd586de..d7fc8b8 100644 --- a/nssa/core/src/encoding.rs +++ b/nssa/core/src/encoding.rs @@ -7,6 +7,7 @@ use std::io::Read; use crate::account::Account; +use crate::account::AccountId; #[cfg(feature = "host")] use crate::encryption::shared_key_derivation::Secp256k1Point; @@ -137,6 +138,12 @@ impl Secp256k1Point { } } +impl AccountId { + pub fn to_bytes(&self) -> [u8; 32] { + self.0 + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/nssa/core/src/encryption/mod.rs b/nssa/core/src/encryption/mod.rs index 7b8d51f..f2dc18e 100644 --- a/nssa/core/src/encryption/mod.rs +++ b/nssa/core/src/encryption/mod.rs @@ -6,7 +6,7 @@ use risc0_zkvm::sha::{Impl, Sha256}; use serde::{Deserialize, Serialize}; #[cfg(feature = "host")] -pub(crate) mod shared_key_derivation; +pub mod shared_key_derivation; #[cfg(feature = "host")] pub use shared_key_derivation::{EphemeralPublicKey, EphemeralSecretKey, IncomingViewingPublicKey}; @@ -16,7 +16,7 @@ use crate::{Commitment, account::Account}; pub type Scalar = [u8; 32]; #[derive(Serialize, Deserialize, Clone)] -pub struct SharedSecretKey([u8; 32]); +pub struct SharedSecretKey(pub [u8; 32]); pub struct EncryptionScheme; diff --git a/nssa/core/src/encryption/shared_key_derivation.rs b/nssa/core/src/encryption/shared_key_derivation.rs index a889123..d40b63e 100644 --- a/nssa/core/src/encryption/shared_key_derivation.rs +++ b/nssa/core/src/encryption/shared_key_derivation.rs @@ -11,7 +11,7 @@ use k256::{ use crate::{SharedSecretKey, encryption::Scalar}; #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] -pub struct Secp256k1Point(pub(crate) Vec); +pub struct Secp256k1Point(pub Vec); impl Secp256k1Point { pub fn from_scalar(value: Scalar) -> Secp256k1Point { diff --git a/nssa/core/src/nullifier.rs b/nssa/core/src/nullifier.rs index bc73d8c..200b3a4 100644 --- a/nssa/core/src/nullifier.rs +++ b/nssa/core/src/nullifier.rs @@ -1,11 +1,22 @@ use risc0_zkvm::sha::{Impl, Sha256}; use serde::{Deserialize, Serialize}; -use crate::Commitment; +use crate::{Commitment, account::AccountId}; #[derive(Serialize, Deserialize, PartialEq, Eq)] #[cfg_attr(any(feature = "host", test), derive(Debug, Clone, Hash))] -pub struct NullifierPublicKey(pub(super) [u8; 32]); +pub struct NullifierPublicKey(pub [u8; 32]); + +impl From<&NullifierPublicKey> for AccountId { + fn from(value: &NullifierPublicKey) -> Self { + const PRIVATE_ACCOUNT_ID_PREFIX: &[u8; 32] = b"/NSSA/v0.1/AccountId/Private/\x00\x00\x00"; + + let mut bytes = [0; 64]; + bytes[0..32].copy_from_slice(PRIVATE_ACCOUNT_ID_PREFIX); + bytes[32..].copy_from_slice(&value.0); + AccountId::new(Impl::hash_bytes(&bytes).as_bytes().try_into().unwrap()) + } +} impl AsRef<[u8]> for NullifierPublicKey { fn as_ref(&self) -> &[u8] { @@ -71,4 +82,21 @@ mod tests { let npk = NullifierPublicKey::from(&nsk); assert_eq!(npk, expected_npk); } + + #[test] + fn test_account_id_from_nullifier_public_key() { + let nsk = [ + 57, 5, 64, 115, 153, 56, 184, 51, 207, 238, 99, 165, 147, 214, 213, 151, 30, 251, 30, + 196, 134, 22, 224, 211, 237, 120, 136, 225, 188, 220, 249, 28, + ]; + let npk = NullifierPublicKey::from(&nsk); + let expected_account_id = AccountId::new([ + 69, 160, 50, 67, 12, 56, 150, 116, 62, 145, 17, 161, 17, 45, 24, 53, 33, 167, 83, 178, + 47, 114, 111, 233, 251, 30, 54, 244, 184, 22, 100, 236, + ]); + + let account_id = AccountId::from(&npk); + + assert_eq!(account_id, expected_account_id); + } } diff --git a/nssa/core/src/program.rs b/nssa/core/src/program.rs index d284bbc..82023f3 100644 --- a/nssa/core/src/program.rs +++ b/nssa/core/src/program.rs @@ -21,8 +21,8 @@ pub struct ProgramOutput { pub fn read_nssa_inputs() -> ProgramInput { let pre_states: Vec = env::read(); - let words: InstructionData = env::read(); - let instruction = T::deserialize(&mut Deserializer::new(words.as_ref())).unwrap(); + let instruction_words: InstructionData = env::read(); + let instruction = T::deserialize(&mut Deserializer::new(instruction_words.as_ref())).unwrap(); ProgramInput { pre_states, instruction, @@ -59,20 +59,23 @@ pub fn validate_execution( return false; } - // 3. Ownership change only allowed from default accounts - if pre.account.program_owner != post.program_owner && pre.account != Account::default() { + // 3. Program ownership changes are not allowed + if pre.account.program_owner != post.program_owner { return false; } + let account_program_owner = pre.account.program_owner; + // 4. Decreasing balance only allowed if owned by executing program - if post.balance < pre.account.balance && pre.account.program_owner != executing_program_id { + if post.balance < pre.account.balance && account_program_owner != executing_program_id { return false; } - // 5. Data changes only allowed if owned by executing program + // 5. Data changes only allowed if owned by executing program or if account pre state has + // default values if pre.account.data != post.data - && (executing_program_id != pre.account.program_owner - || executing_program_id != post.program_owner) + && pre.account != Account::default() + && account_program_owner != executing_program_id { return false; } diff --git a/nssa/program_methods/guest/Cargo.toml b/nssa/program_methods/guest/Cargo.toml index da4dbe8..9e5f543 100644 --- a/nssa/program_methods/guest/Cargo.toml +++ b/nssa/program_methods/guest/Cargo.toml @@ -6,5 +6,6 @@ edition = "2024" [workspace] [dependencies] -risc0-zkvm = { version = "3.0.3", default-features = false, features = ['std'] } +risc0-zkvm = { version = "3.0.3", features = ['std'] } nssa-core = { path = "../../core" } +serde = { version = "1.0.219", default-features = false } diff --git a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs index 89b84fd..210e3f0 100644 --- a/nssa/program_methods/guest/src/bin/authenticated_transfer.rs +++ b/nssa/program_methods/guest/src/bin/authenticated_transfer.rs @@ -1,4 +1,4 @@ -use nssa_core::program::{read_nssa_inputs, write_nssa_outputs, ProgramInput}; +use nssa_core::program::{ProgramInput, read_nssa_inputs, write_nssa_outputs}; /// A transfer of balance program. /// To be used both in public and private contexts. 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 61f431c..235b5c8 100644 --- a/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs +++ b/nssa/program_methods/guest/src/bin/privacy_preserving_circuit.rs @@ -1,12 +1,12 @@ use risc0_zkvm::{guest::env, serde::to_vec}; use nssa_core::{ - account::{Account, AccountWithMetadata}, - compute_digest_for_path, - encryption::Ciphertext, - program::{validate_execution, ProgramOutput, DEFAULT_PROGRAM_ID}, Commitment, CommitmentSetDigest, EncryptionScheme, Nullifier, NullifierPublicKey, PrivacyPreservingCircuitInput, PrivacyPreservingCircuitOutput, + account::{Account, AccountId, AccountWithMetadata}, + compute_digest_for_path, + encryption::Ciphertext, + program::{DEFAULT_PROGRAM_ID, ProgramOutput, validate_execution}, }; fn main() { @@ -74,6 +74,10 @@ fn main() { let new_nonce = private_nonces_iter.next().expect("Missing private nonce"); let (npk, shared_secret) = private_keys_iter.next().expect("Missing keys"); + if AccountId::from(npk) != pre_states[i].account_id { + panic!("AccountId mismatch"); + } + if visibility_mask[i] == 1 { // Private account with authentication let (nsk, membership_proof) = diff --git a/nssa/program_methods/guest/src/bin/token.rs b/nssa/program_methods/guest/src/bin/token.rs new file mode 100644 index 0000000..e5680be --- /dev/null +++ b/nssa/program_methods/guest/src/bin/token.rs @@ -0,0 +1,554 @@ +use nssa_core::{ + account::{Account, AccountId, AccountWithMetadata, Data}, + program::{ProgramInput, read_nssa_inputs, write_nssa_outputs}, +}; + +// The token program has two functions: +// 1. New token definition. +// Arguments to this function are: +// * Two **default** accounts: [definition_account, holding_account]. +// The first default account will be initialized with the token definition account values. The second account will +// be initialized to a token holding account for the new token, holding the entire total supply. +// * An instruction data of 23-bytes, indicating the total supply and the token name, with +// the following layout: +// [0x00 || total_supply (little-endian 16 bytes) || name (6 bytes)] +// The name cannot be equal to [0x00, 0x00, 0x00, 0x00, 0x00, 0x00] +// 2. Token transfer +// Arguments to this function are: +// * Two accounts: [sender_account, recipient_account]. +// * An instruction data byte string of length 23, indicating the total supply with the following layout +// [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00]. + +const TOKEN_DEFINITION_TYPE: u8 = 0; +const TOKEN_DEFINITION_DATA_SIZE: usize = 23; + +const TOKEN_HOLDING_TYPE: u8 = 1; +const TOKEN_HOLDING_DATA_SIZE: usize = 49; + +struct TokenDefinition { + account_type: u8, + name: [u8; 6], + total_supply: u128, +} + +struct TokenHolding { + account_type: u8, + definition_id: AccountId, + balance: u128, +} + +impl TokenDefinition { + fn into_data(self) -> Vec { + let mut bytes = [0; TOKEN_DEFINITION_DATA_SIZE]; + bytes[0] = self.account_type; + bytes[1..7].copy_from_slice(&self.name); + bytes[7..].copy_from_slice(&self.total_supply.to_le_bytes()); + bytes.into() + } +} + +impl TokenHolding { + fn new(definition_id: &AccountId) -> Self { + Self { + account_type: TOKEN_HOLDING_TYPE, + definition_id: definition_id.clone(), + balance: 0, + } + } + + fn parse(data: &[u8]) -> Option { + if data.len() != TOKEN_HOLDING_DATA_SIZE || data[0] != TOKEN_HOLDING_TYPE { + None + } else { + let account_type = data[0]; + let definition_id = AccountId::new(data[1..33].try_into().unwrap()); + let balance = u128::from_le_bytes(data[33..].try_into().unwrap()); + Some(Self { + definition_id, + balance, + account_type, + }) + } + } + + fn into_data(self) -> Data { + let mut bytes = [0; TOKEN_HOLDING_DATA_SIZE]; + bytes[0] = self.account_type; + bytes[1..33].copy_from_slice(&self.definition_id.to_bytes()); + bytes[33..].copy_from_slice(&self.balance.to_le_bytes()); + bytes.into() + } +} + +fn transfer(pre_states: &[AccountWithMetadata], balance_to_move: u128) -> Vec { + if pre_states.len() != 2 { + panic!("Invalid number of input accounts"); + } + let sender = &pre_states[0]; + let recipient = &pre_states[1]; + + let mut sender_holding = + TokenHolding::parse(&sender.account.data).expect("Invalid sender data"); + let mut recipient_holding = if recipient.account == Account::default() { + TokenHolding::new(&sender_holding.definition_id) + } else { + TokenHolding::parse(&recipient.account.data).expect("Invalid recipient data") + }; + + if sender_holding.definition_id != recipient_holding.definition_id { + panic!("Sender and recipient definition id mismatch"); + } + + if sender_holding.balance < balance_to_move { + panic!("Insufficient balance"); + } + + if !sender.is_authorized { + panic!("Sender authorization is missing"); + } + + sender_holding.balance -= balance_to_move; + recipient_holding.balance = recipient_holding + .balance + .checked_add(balance_to_move) + .expect("Recipient balance overflow."); + + let sender_post = { + let mut this = sender.account.clone(); + this.data = sender_holding.into_data(); + this + }; + let recipient_post = { + let mut this = recipient.account.clone(); + this.data = recipient_holding.into_data(); + this + }; + + vec![sender_post, recipient_post] +} + +fn new_definition( + pre_states: &[AccountWithMetadata], + name: [u8; 6], + total_supply: u128, +) -> Vec { + if pre_states.len() != 2 { + panic!("Invalid number of input accounts"); + } + let definition_target_account = &pre_states[0]; + let holding_target_account = &pre_states[1]; + + if definition_target_account.account != Account::default() { + panic!("Definition target account must have default values"); + } + + if holding_target_account.account != Account::default() { + panic!("Holding target account must have default values"); + } + + let token_definition = TokenDefinition { + account_type: TOKEN_DEFINITION_TYPE, + name, + total_supply, + }; + + let token_holding = TokenHolding { + account_type: TOKEN_HOLDING_TYPE, + definition_id: definition_target_account.account_id.clone(), + balance: total_supply, + }; + + let mut definition_target_account_post = definition_target_account.account.clone(); + definition_target_account_post.data = token_definition.into_data(); + + let mut holding_target_account_post = holding_target_account.account.clone(); + holding_target_account_post.data = token_holding.into_data(); + + vec![definition_target_account_post, holding_target_account_post] +} + +type Instruction = [u8; 23]; + +fn main() { + let ProgramInput { + pre_states, + instruction, + } = read_nssa_inputs::(); + + match instruction[0] { + 0 => { + // Parse instruction + let total_supply = u128::from_le_bytes(instruction[1..17].try_into().unwrap()); + let name: [u8; 6] = instruction[17..].try_into().unwrap(); + assert_ne!(name, [0; 6]); + + // Execute + let post_states = new_definition(&pre_states, name, total_supply); + write_nssa_outputs(pre_states, post_states); + } + 1 => { + // Parse instruction + let balance_to_move = u128::from_le_bytes(instruction[1..17].try_into().unwrap()); + let name: [u8; 6] = instruction[17..].try_into().unwrap(); + assert_eq!(name, [0; 6]); + + // Execute + let post_states = transfer(&pre_states, balance_to_move); + write_nssa_outputs(pre_states, post_states); + } + _ => panic!("Invalid instruction"), + }; +} + +#[cfg(test)] +mod tests { + use nssa_core::account::{Account, AccountId, AccountWithMetadata}; + + use crate::{new_definition, transfer, TOKEN_HOLDING_DATA_SIZE, TOKEN_HOLDING_TYPE}; + + #[should_panic(expected = "Invalid number of input accounts")] + #[test] + fn test_call_new_definition_with_invalid_number_of_accounts_1() { + let pre_states = vec![AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([1; 32]), + }]; + let _post_states = new_definition(&pre_states, [0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe], 10); + } + + #[should_panic(expected = "Invalid number of input accounts")] + #[test] + fn test_call_new_definition_with_invalid_number_of_accounts_2() { + let pre_states = vec![ + AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([1; 32]), + }, + AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([2; 32]), + }, + AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([3; 32]), + }, + ]; + let _post_states = new_definition(&pre_states, [0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe], 10); + } + + #[should_panic(expected = "Definition target account must have default values")] + #[test] + fn test_new_definition_non_default_first_account_should_fail() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + program_owner: [1, 2, 3, 4, 5, 6, 7, 8], + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([1; 32]), + }, + AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([2; 32]), + }, + ]; + let _post_states = new_definition(&pre_states, [0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe], 10); + } + + #[should_panic(expected = "Holding target account must have default values")] + #[test] + fn test_new_definition_non_default_second_account_should_fail() { + let pre_states = vec![ + AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([1; 32]), + }, + AccountWithMetadata { + account: Account { + program_owner: [1, 2, 3, 4, 5, 6, 7, 8], + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([2; 32]), + }, + ]; + let _post_states = new_definition(&pre_states, [0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe], 10); + } + + #[test] + fn test_new_definition_with_valid_inputs_succeeds() { + let pre_states = vec![ + AccountWithMetadata { + account: Account::default(), + is_authorized: false, + account_id: AccountId::new([ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + ]), + }, + AccountWithMetadata { + account: Account { + ..Account::default() + }, + is_authorized: false, + account_id: AccountId::new([2; 32]), + }, + ]; + + let post_states = new_definition(&pre_states, [0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe], 10); + let [definition_account, holding_account] = post_states.try_into().ok().unwrap(); + assert_eq!( + definition_account.data, + vec![ + 0, 0xca, 0xfe, 0xca, 0xfe, 0xca, 0xfe, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 + ] + ); + assert_eq!( + holding_account.data, + vec![ + 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, + 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0 + ] + ); + } + + #[should_panic(expected = "Invalid number of input accounts")] + #[test] + fn test_call_transfer_with_invalid_number_of_accounts_1() { + let pre_states = vec![AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([1; 32]), + }]; + let _post_states = transfer(&pre_states, 10); + } + + #[should_panic(expected = "Invalid number of input accounts")] + #[test] + fn test_call_transfer_with_invalid_number_of_accounts_2() { + let pre_states = vec![ + AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([1; 32]), + }, + AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([2; 32]), + }, + AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([3; 32]), + }, + ]; + let _post_states = transfer(&pre_states, 10); + } + + #[should_panic(expected = "Invalid sender data")] + #[test] + fn test_transfer_invalid_instruction_type_should_fail() { + let invalid_type = TOKEN_HOLDING_TYPE ^ 1; + let pre_states = vec![ + AccountWithMetadata { + account: Account { + // First byte should be `TOKEN_HOLDING_TYPE` for token holding accounts + data: vec![invalid_type; TOKEN_HOLDING_DATA_SIZE], + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([1; 32]), + }, + AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([2; 32]), + }, + ]; + let _post_states = transfer(&pre_states, 10); + } + + #[should_panic(expected = "Invalid sender data")] + #[test] + fn test_transfer_invalid_data_size_should_fail_1() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + // Data must be of exact length `TOKEN_HOLDING_DATA_SIZE` + data: vec![1; TOKEN_HOLDING_DATA_SIZE - 1], + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([1; 32]), + }, + AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([2; 32]), + }, + ]; + let _post_states = transfer(&pre_states, 10); + } + + #[should_panic(expected = "Invalid sender data")] + #[test] + fn test_transfer_invalid_data_size_should_fail_2() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + // Data must be of exact length `TOKEN_HOLDING_DATA_SIZE` + data: vec![1; TOKEN_HOLDING_DATA_SIZE + 1], + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([1; 32]), + }, + AccountWithMetadata { + account: Account::default(), + is_authorized: true, + account_id: AccountId::new([2; 32]), + }, + ]; + let _post_states = transfer(&pre_states, 10); + } + + #[should_panic(expected = "Sender and recipient definition id mismatch")] + #[test] + fn test_transfer_with_different_definition_ids_should_fail() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + data: vec![1; TOKEN_HOLDING_DATA_SIZE], + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([1; 32]), + }, + AccountWithMetadata { + account: Account { + data: vec![1] + .into_iter() + .chain(vec![2; TOKEN_HOLDING_DATA_SIZE - 1]) + .collect(), + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([2; 32]), + }, + ]; + let _post_states = transfer(&pre_states, 10); + } + + #[should_panic(expected = "Insufficient balance")] + #[test] + fn test_transfer_with_insufficient_balance_should_fail() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + // Account with balance 37 + data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] + .into_iter() + .chain(u128::to_le_bytes(37)) + .collect(), + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([1; 32]), + }, + AccountWithMetadata { + account: Account { + data: vec![1; TOKEN_HOLDING_DATA_SIZE], + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([2; 32]), + }, + ]; + // Attempt to transfer 38 tokens + let _post_states = transfer(&pre_states, 38); + } + + #[should_panic(expected = "Sender authorization is missing")] + #[test] + fn test_transfer_without_sender_authorization_should_fail() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + // Account with balance 37 + data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] + .into_iter() + .chain(u128::to_le_bytes(37)) + .collect(), + ..Account::default() + }, + is_authorized: false, + account_id: AccountId::new([1; 32]), + }, + AccountWithMetadata { + account: Account { + data: vec![1; TOKEN_HOLDING_DATA_SIZE], + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([2; 32]), + }, + ]; + let _post_states = transfer(&pre_states, 37); + } + + #[test] + fn test_transfer_with_valid_inputs_succeeds() { + let pre_states = vec![ + AccountWithMetadata { + account: Account { + // Account with balance 37 + data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] + .into_iter() + .chain(u128::to_le_bytes(37)) + .collect(), + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([1; 32]), + }, + AccountWithMetadata { + account: Account { + // Account with balance 255 + data: vec![1; TOKEN_HOLDING_DATA_SIZE - 16] + .into_iter() + .chain(u128::to_le_bytes(255)) + .collect(), + ..Account::default() + }, + is_authorized: true, + account_id: AccountId::new([2; 32]), + }, + ]; + let post_states = transfer(&pre_states, 11); + let [sender_post, recipient_post] = post_states.try_into().ok().unwrap(); + assert_eq!( + sender_post.data, + vec![ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + assert_eq!( + recipient_post.data, + vec![ + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 10, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ); + } +} diff --git a/nssa/src/address.rs b/nssa/src/address.rs index 93304d5..72144b2 100644 --- a/nssa/src/address.rs +++ b/nssa/src/address.rs @@ -1,6 +1,8 @@ use std::{fmt::Display, str::FromStr}; +use nssa_core::account::AccountId; use serde::{Deserialize, Serialize}; +use sha2::{Digest, Sha256}; use crate::signature::PublicKey; @@ -81,8 +83,21 @@ impl<'de> Deserialize<'de> for Address { } } +impl From<&Address> for AccountId { + fn from(address: &Address) -> Self { + const PUBLIC_ACCOUNT_ID_PREFIX: &[u8; 32] = b"/NSSA/v0.1/AccountId/Public/\x00\x00\x00\x00"; + + let mut hasher = Sha256::new(); + hasher.update(PUBLIC_ACCOUNT_ID_PREFIX); + hasher.update(address.value); + AccountId::new(hasher.finalize().into()) + } +} + #[cfg(test)] mod tests { + use nssa_core::account::AccountId; + use crate::{Address, address::AddressError}; #[test] @@ -112,4 +127,17 @@ mod tests { let result = hex_str.parse::
().unwrap_err(); assert!(matches!(result, AddressError::InvalidLength(_))); } + + #[test] + fn test_account_id_from_address() { + let address: Address = "37".repeat(32).parse().unwrap(); + let expected_account_id = AccountId::new([ + 93, 223, 66, 245, 78, 230, 157, 188, 110, 161, 134, 255, 137, 177, 220, 88, 37, 44, + 243, 91, 236, 4, 36, 147, 185, 112, 21, 49, 234, 4, 107, 185, + ]); + + let account_id = AccountId::from(&address); + + assert_eq!(account_id, expected_account_id); + } } diff --git a/nssa/src/lib.rs b/nssa/src/lib.rs index afa4980..31591ad 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -2,14 +2,14 @@ pub mod address; pub mod encoding; pub mod error; mod merkle_tree; -mod privacy_preserving_transaction; +pub mod privacy_preserving_transaction; pub mod program; pub mod public_transaction; mod signature; mod state; pub use address::Address; -pub use nssa_core::account::Account; +pub use nssa_core::account::{Account, AccountId}; pub use privacy_preserving_transaction::{ PrivacyPreservingTransaction, circuit::execute_and_prove, }; diff --git a/nssa/src/privacy_preserving_transaction/circuit.rs b/nssa/src/privacy_preserving_transaction/circuit.rs index 8a62087..8575bfe 100644 --- a/nssa/src/privacy_preserving_transaction/circuit.rs +++ b/nssa/src/privacy_preserving_transaction/circuit.rs @@ -92,7 +92,7 @@ impl Proof { mod tests { use nssa_core::{ Commitment, EncryptionScheme, Nullifier, - account::{Account, AccountWithMetadata}, + account::{Account, AccountId, AccountWithMetadata}, }; use crate::{ @@ -108,20 +108,23 @@ mod tests { #[test] fn prove_privacy_preserving_execution_circuit_public_and_private_pre_accounts() { + let recipient_keys = test_private_account_keys_1(); let program = Program::authenticated_transfer_program(); - let sender = AccountWithMetadata { - account: Account { + let sender = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; + true, + AccountId::new([0; 32]), + ); - let recipient = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; + let recipient = AccountWithMetadata::new( + Account::default(), + false, + AccountId::from(&recipient_keys.npk()), + ); let balance_to_move: u128 = 37; @@ -140,7 +143,6 @@ mod tests { }; let expected_sender_pre = sender.clone(); - let recipient_keys = test_private_account_keys_1(); let esk = [3; 32]; let shared_secret = SharedSecretKey::new(&esk, &recipient_keys.ivk()); @@ -179,23 +181,26 @@ mod tests { #[test] fn prove_privacy_preserving_execution_circuit_fully_private() { let program = Program::authenticated_transfer_program(); - let sender_pre = AccountWithMetadata { - account: Account { + let sender_keys = test_private_account_keys_1(); + let recipient_keys = test_private_account_keys_2(); + + let sender_pre = AccountWithMetadata::new( + Account { balance: 100, nonce: 0xdeadbeef, program_owner: program.id(), data: vec![], }, - is_authorized: true, - }; - let sender_keys = test_private_account_keys_1(); - let recipient_keys = test_private_account_keys_2(); + true, + AccountId::from(&sender_keys.npk()), + ); let commitment_sender = Commitment::new(&sender_keys.npk(), &sender_pre.account); - let recipient = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; + let recipient = AccountWithMetadata::new( + Account::default(), + false, + AccountId::from(&recipient_keys.npk()), + ); let balance_to_move: u128 = 37; let mut commitment_set = CommitmentSet::with_capacity(2); diff --git a/nssa/src/privacy_preserving_transaction/message.rs b/nssa/src/privacy_preserving_transaction/message.rs index a769c72..d2227a8 100644 --- a/nssa/src/privacy_preserving_transaction/message.rs +++ b/nssa/src/privacy_preserving_transaction/message.rs @@ -11,9 +11,9 @@ pub type ViewTag = u8; #[derive(Debug, Clone, PartialEq, Eq)] pub struct EncryptedAccountData { - pub(crate) ciphertext: Ciphertext, - pub(crate) epk: EphemeralPublicKey, - pub(crate) view_tag: ViewTag, + pub ciphertext: Ciphertext, + pub epk: EphemeralPublicKey, + pub view_tag: ViewTag, } impl EncryptedAccountData { @@ -47,8 +47,8 @@ pub struct Message { pub(crate) public_addresses: Vec
, pub(crate) nonces: Vec, pub(crate) public_post_states: Vec, - pub(crate) encrypted_private_post_states: Vec, - pub(crate) new_commitments: Vec, + pub encrypted_private_post_states: Vec, + pub new_commitments: Vec, pub(crate) new_nullifiers: Vec<(Nullifier, CommitmentSetDigest)>, } diff --git a/nssa/src/privacy_preserving_transaction/transaction.rs b/nssa/src/privacy_preserving_transaction/transaction.rs index 4ecac7f..7e75d35 100644 --- a/nssa/src/privacy_preserving_transaction/transaction.rs +++ b/nssa/src/privacy_preserving_transaction/transaction.rs @@ -15,7 +15,7 @@ use super::witness_set::WitnessSet; #[derive(Debug, Clone, PartialEq, Eq)] pub struct PrivacyPreservingTransaction { - message: Message, + pub message: Message, witness_set: WitnessSet, } @@ -90,9 +90,12 @@ impl PrivacyPreservingTransaction { let public_pre_states: Vec<_> = message .public_addresses .iter() - .map(|address| AccountWithMetadata { - account: state.get_account_by_address(address), - is_authorized: signer_addresses.contains(address), + .map(|address| { + AccountWithMetadata::new( + state.get_account_by_address(address), + signer_addresses.contains(address), + address, + ) }) .collect(); diff --git a/nssa/src/program.rs b/nssa/src/program.rs index 9577411..5abc153 100644 --- a/nssa/src/program.rs +++ b/nssa/src/program.rs @@ -3,7 +3,8 @@ use nssa_core::{ program::{InstructionData, ProgramId, ProgramOutput}, }; use program_methods::{ - AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, PINATA_ELF, PINATA_ID, + AUTHENTICATED_TRANSFER_ELF, AUTHENTICATED_TRANSFER_ID, PINATA_ELF, PINATA_ID, TOKEN_ELF, + TOKEN_ID, }; use risc0_zkvm::{ExecutorEnv, ExecutorEnvBuilder, default_executor, serde::to_vec}; use serde::Serialize; @@ -75,6 +76,13 @@ impl Program { elf: AUTHENTICATED_TRANSFER_ELF, } } + + pub fn token() -> Self { + Self { + id: TOKEN_ID, + elf: TOKEN_ELF, + } + } } // TODO: Testnet only. Refactor to prevent compilation on mainnet. @@ -89,7 +97,7 @@ impl Program { #[cfg(test)] mod tests { - use nssa_core::account::{Account, AccountWithMetadata}; + use nssa_core::account::{Account, AccountId, AccountWithMetadata}; use crate::program::Program; @@ -180,17 +188,16 @@ mod tests { let program = Program::simple_balance_transfer(); let balance_to_move: u128 = 11223344556677; let instruction_data = Program::serialize_instruction(balance_to_move).unwrap(); - let sender = AccountWithMetadata { - account: Account { + let sender = AccountWithMetadata::new( + Account { balance: 77665544332211, ..Account::default() }, - is_authorized: false, - }; - let recipient = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; + true, + AccountId::new([0; 32]), + ); + let recipient = + AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); let expected_sender_post = Account { balance: 77665544332211 - balance_to_move, diff --git a/nssa/src/public_transaction/transaction.rs b/nssa/src/public_transaction/transaction.rs index 20b2729..8af9212 100644 --- a/nssa/src/public_transaction/transaction.rs +++ b/nssa/src/public_transaction/transaction.rs @@ -91,9 +91,12 @@ impl PublicTransaction { let pre_states: Vec<_> = message .addresses .iter() - .map(|address| AccountWithMetadata { - account: state.get_account_by_address(address), - is_authorized: signer_addresses.contains(address), + .map(|address| { + AccountWithMetadata::new( + state.get_account_by_address(address), + signer_addresses.contains(address), + address, + ) }) .collect(); @@ -138,7 +141,7 @@ pub mod tests { fn state_for_tests() -> V01State { let (_, _, addr1, addr2) = keys_for_tests(); let initial_data = [(addr1, 10000), (addr2, 20000)]; - V01State::new_with_genesis_accounts(&initial_data) + V01State::new_with_genesis_accounts(&initial_data, &[]) } fn transaction_for_tests() -> PublicTransaction { diff --git a/nssa/src/state.rs b/nssa/src/state.rs index 9245324..65b440f 100644 --- a/nssa/src/state.rs +++ b/nssa/src/state.rs @@ -64,7 +64,10 @@ pub struct V01State { } impl V01State { - pub fn new_with_genesis_accounts(initial_data: &[(Address, u128)]) -> Self { + pub fn new_with_genesis_accounts( + initial_data: &[(Address, u128)], + initial_commitments: &[nssa_core::Commitment], + ) -> Self { let authenticated_transfer_program = Program::authenticated_transfer_program(); let public_state = initial_data .iter() @@ -79,13 +82,17 @@ impl V01State { }) .collect(); + let mut private_state = CommitmentSet::with_capacity(32); + private_state.extend(initial_commitments); + let mut this = Self { public_state, - private_state: (CommitmentSet::with_capacity(32), NullifierSet::new()), + private_state: (private_state, NullifierSet::new()), builtin_programs: HashMap::new(), }; this.insert_program(Program::authenticated_transfer_program()); + this.insert_program(Program::token()); this } @@ -242,7 +249,7 @@ pub mod tests { use nssa_core::{ Commitment, Nullifier, NullifierPublicKey, NullifierSecretKey, SharedSecretKey, - account::{Account, AccountWithMetadata, Nonce}, + account::{Account, AccountId, AccountWithMetadata, Nonce}, encryption::{EphemeralPublicKey, IncomingViewingPublicKey, Scalar}, }; @@ -269,14 +276,14 @@ pub mod tests { let addr1 = Address::from(&PublicKey::new_from_private_key(&key1)); let addr2 = Address::from(&PublicKey::new_from_private_key(&key2)); let initial_data = [(addr1, 100u128), (addr2, 151u128)]; - let program = Program::authenticated_transfer_program(); + let authenticated_transfers_program = Program::authenticated_transfer_program(); let expected_public_state = { let mut this = HashMap::new(); this.insert( addr1, Account { balance: 100, - program_owner: program.id(), + program_owner: authenticated_transfers_program.id(), ..Account::default() }, ); @@ -284,7 +291,7 @@ pub mod tests { addr2, Account { balance: 151, - program_owner: program.id(), + program_owner: authenticated_transfers_program.id(), ..Account::default() }, ); @@ -292,11 +299,15 @@ pub mod tests { }; let expected_builtin_programs = { let mut this = HashMap::new(); - this.insert(program.id(), program); + this.insert( + authenticated_transfers_program.id(), + authenticated_transfers_program, + ); + this.insert(Program::token().id(), Program::token()); this }; - let state = V01State::new_with_genesis_accounts(&initial_data); + let state = V01State::new_with_genesis_accounts(&initial_data, &[]); assert_eq!(state.public_state, expected_public_state); assert_eq!(state.builtin_programs, expected_builtin_programs); @@ -304,7 +315,7 @@ pub mod tests { #[test] fn test_insert_program() { - let mut state = V01State::new_with_genesis_accounts(&[]); + let mut state = V01State::new_with_genesis_accounts(&[], &[]); let program_to_insert = Program::simple_balance_transfer(); let program_id = program_to_insert.id(); assert!(!state.builtin_programs.contains_key(&program_id)); @@ -319,7 +330,7 @@ pub mod tests { let key = PrivateKey::try_new([1; 32]).unwrap(); let addr = Address::from(&PublicKey::new_from_private_key(&key)); let initial_data = [(addr, 100u128)]; - let state = V01State::new_with_genesis_accounts(&initial_data); + let state = V01State::new_with_genesis_accounts(&initial_data, &[]); let expected_account = state.public_state.get(&addr).unwrap(); let account = state.get_account_by_address(&addr); @@ -330,7 +341,7 @@ pub mod tests { #[test] fn test_get_account_by_address_default_account() { let addr2 = Address::new([0; 32]); - let state = V01State::new_with_genesis_accounts(&[]); + let state = V01State::new_with_genesis_accounts(&[], &[]); let expected_account = Account::default(); let account = state.get_account_by_address(&addr2); @@ -340,7 +351,7 @@ pub mod tests { #[test] fn test_builtin_programs_getter() { - let state = V01State::new_with_genesis_accounts(&[]); + let state = V01State::new_with_genesis_accounts(&[], &[]); let builtin_programs = state.builtin_programs(); @@ -352,7 +363,7 @@ pub mod tests { let key = PrivateKey::try_new([1; 32]).unwrap(); let address = Address::from(&PublicKey::new_from_private_key(&key)); let initial_data = [(address, 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data); + let mut state = V01State::new_with_genesis_accounts(&initial_data, &[]); let from = address; let to = Address::new([2; 32]); assert_eq!(state.get_account_by_address(&to), Account::default()); @@ -372,7 +383,7 @@ pub mod tests { let key = PrivateKey::try_new([1; 32]).unwrap(); let address = Address::from(&PublicKey::new_from_private_key(&key)); let initial_data = [(address, 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data); + let mut state = V01State::new_with_genesis_accounts(&initial_data, &[]); let from = address; let from_key = key; let to = Address::new([2; 32]); @@ -396,7 +407,7 @@ pub mod tests { let address1 = Address::from(&PublicKey::new_from_private_key(&key1)); let address2 = Address::from(&PublicKey::new_from_private_key(&key2)); let initial_data = [(address1, 100), (address2, 200)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data); + let mut state = V01State::new_with_genesis_accounts(&initial_data, &[]); let from = address2; let from_key = key2; let to = address1; @@ -419,7 +430,7 @@ pub mod tests { let key2 = PrivateKey::try_new([2; 32]).unwrap(); let address2 = Address::from(&PublicKey::new_from_private_key(&key2)); let initial_data = [(address1, 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data); + let mut state = V01State::new_with_genesis_accounts(&initial_data, &[]); let address3 = Address::new([3; 32]); let balance_to_move = 5; @@ -503,7 +514,8 @@ pub mod tests { #[test] fn test_program_should_fail_if_modifies_nonces() { let initial_data = [(Address::new([1; 32]), 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let mut state = + V01State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs(); let addresses = vec![Address::new([1; 32])]; let program_id = Program::nonce_changer_program().id(); let message = @@ -519,7 +531,8 @@ pub mod tests { #[test] fn test_program_should_fail_if_output_accounts_exceed_inputs() { let initial_data = [(Address::new([1; 32]), 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let mut state = + V01State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs(); let addresses = vec![Address::new([1; 32])]; let program_id = Program::extra_output_program().id(); let message = @@ -535,7 +548,8 @@ pub mod tests { #[test] fn test_program_should_fail_with_missing_output_accounts() { let initial_data = [(Address::new([1; 32]), 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let mut state = + V01State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs(); let addresses = vec![Address::new([1; 32]), Address::new([2; 32])]; let program_id = Program::missing_output_program().id(); let message = @@ -551,7 +565,8 @@ pub mod tests { #[test] fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_program_owner() { let initial_data = [(Address::new([1; 32]), 0)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let mut state = + V01State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs(); let address = Address::new([1; 32]); let account = state.get_account_by_address(&address); // Assert the target account only differs from the default account in the program owner field @@ -573,7 +588,7 @@ pub mod tests { #[test] fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_balance() { let initial_data = []; - let mut state = V01State::new_with_genesis_accounts(&initial_data) + let mut state = V01State::new_with_genesis_accounts(&initial_data, &[]) .with_test_programs() .with_non_default_accounts_but_default_program_owners(); let address = Address::new([255; 32]); @@ -597,7 +612,7 @@ pub mod tests { #[test] fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_nonce() { let initial_data = []; - let mut state = V01State::new_with_genesis_accounts(&initial_data) + let mut state = V01State::new_with_genesis_accounts(&initial_data, &[]) .with_test_programs() .with_non_default_accounts_but_default_program_owners(); let address = Address::new([254; 32]); @@ -621,7 +636,7 @@ pub mod tests { #[test] fn test_program_should_fail_if_modifies_program_owner_with_only_non_default_data() { let initial_data = []; - let mut state = V01State::new_with_genesis_accounts(&initial_data) + let mut state = V01State::new_with_genesis_accounts(&initial_data, &[]) .with_test_programs() .with_non_default_accounts_but_default_program_owners(); let address = Address::new([253; 32]); @@ -645,7 +660,8 @@ pub mod tests { #[test] fn test_program_should_fail_if_transfers_balance_from_non_owned_account() { let initial_data = [(Address::new([1; 32]), 100)]; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let mut state = + V01State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs(); let sender_address = Address::new([1; 32]); let receiver_address = Address::new([2; 32]); let balance_to_move: u128 = 1; @@ -672,12 +688,13 @@ pub mod tests { #[test] fn test_program_should_fail_if_modifies_data_of_non_owned_account() { let initial_data = []; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); - let address = Address::new([1; 32]); + let mut state = V01State::new_with_genesis_accounts(&initial_data, &[]) + .with_test_programs() + .with_non_default_accounts_but_default_program_owners(); + let address = Address::new([255; 32]); let program_id = Program::data_changer().id(); - // Consider the extreme case where the target account is the default account - assert_eq!(state.get_account_by_address(&address), Account::default()); + assert_ne!(state.get_account_by_address(&address), Account::default()); assert_ne!( state.get_account_by_address(&address).program_owner, program_id @@ -695,7 +712,8 @@ pub mod tests { #[test] fn test_program_should_fail_if_does_not_preserve_total_balance_by_minting() { let initial_data = []; - let mut state = V01State::new_with_genesis_accounts(&initial_data).with_test_programs(); + let mut state = + V01State::new_with_genesis_accounts(&initial_data, &[]).with_test_programs(); let address = Address::new([1; 32]); let program_id = Program::minter().id(); @@ -712,7 +730,7 @@ pub mod tests { #[test] fn test_program_should_fail_if_does_not_preserve_total_balance_by_burning() { let initial_data = []; - let mut state = V01State::new_with_genesis_accounts(&initial_data) + let mut state = V01State::new_with_genesis_accounts(&initial_data, &[]) .with_test_programs() .with_account_owned_by_burner_program(); let program_id = Program::burner().id(); @@ -789,17 +807,15 @@ pub mod tests { balance_to_move: u128, state: &V01State, ) -> PrivacyPreservingTransaction { - let sender = AccountWithMetadata { - account: state.get_account_by_address(&sender_keys.address()), - is_authorized: true, - }; + let sender = AccountWithMetadata::new( + state.get_account_by_address(&sender_keys.address()), + true, + &sender_keys.address(), + ); let sender_nonce = sender.account.nonce; - let recipient = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; + let recipient = AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk()); let esk = [3; 32]; let shared_secret = SharedSecretKey::new(&esk, &recipient_keys.ivk()); @@ -838,14 +854,10 @@ pub mod tests { ) -> PrivacyPreservingTransaction { let program = Program::authenticated_transfer_program(); let sender_commitment = Commitment::new(&sender_keys.npk(), sender_private_account); - let sender_pre = AccountWithMetadata { - account: sender_private_account.clone(), - is_authorized: true, - }; - let recipient_pre = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; + let sender_pre = + AccountWithMetadata::new(sender_private_account.clone(), true, &sender_keys.npk()); + let recipient_pre = + AccountWithMetadata::new(Account::default(), false, &recipient_keys.npk()); let esk_1 = [3; 32]; let shared_secret_1 = SharedSecretKey::new(&esk_1, &sender_keys.ivk()); @@ -898,14 +910,13 @@ pub mod tests { ) -> PrivacyPreservingTransaction { let program = Program::authenticated_transfer_program(); let sender_commitment = Commitment::new(&sender_keys.npk(), sender_private_account); - let sender_pre = AccountWithMetadata { - account: sender_private_account.clone(), - is_authorized: true, - }; - let recipient_pre = AccountWithMetadata { - account: state.get_account_by_address(recipient_address), - is_authorized: false, - }; + let sender_pre = + AccountWithMetadata::new(sender_private_account.clone(), true, &sender_keys.npk()); + let recipient_pre = AccountWithMetadata::new( + state.get_account_by_address(recipient_address), + false, + recipient_address, + ); let esk = [3; 32]; let shared_secret = SharedSecretKey::new(&esk, &sender_keys.ivk()); @@ -943,7 +954,7 @@ pub mod tests { let sender_keys = test_public_account_keys_1(); let recipient_keys = test_private_account_keys_1(); - let mut state = V01State::new_with_genesis_accounts(&[(sender_keys.address(), 200)]); + let mut state = V01State::new_with_genesis_accounts(&[(sender_keys.address(), 200)], &[]); let balance_to_move = 37; @@ -989,7 +1000,7 @@ pub mod tests { }; let recipient_keys = test_private_account_keys_2(); - let mut state = V01State::new_with_genesis_accounts(&[]) + let mut state = V01State::new_with_genesis_accounts(&[], &[]) .with_private_account(&sender_keys, &sender_private_account); let balance_to_move = 37; @@ -1054,10 +1065,10 @@ pub mod tests { }; let recipient_keys = test_public_account_keys_1(); let recipient_initial_balance = 400; - let mut state = V01State::new_with_genesis_accounts(&[( - recipient_keys.address(), - recipient_initial_balance, - )]) + let mut state = V01State::new_with_genesis_accounts( + &[(recipient_keys.address(), recipient_initial_balance)], + &[], + ) .with_private_account(&sender_keys, &sender_private_account); let balance_to_move = 37; @@ -1114,14 +1125,15 @@ pub mod tests { #[test] fn test_burner_program_should_fail_in_privacy_preserving_circuit() { let program = Program::burner(); - let public_account = AccountWithMetadata { - account: Account { + let public_account = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; + true, + AccountId::new([0; 32]), + ); let result = execute_and_prove( &[public_account], @@ -1139,14 +1151,15 @@ pub mod tests { #[test] fn test_minter_program_should_fail_in_privacy_preserving_circuit() { let program = Program::minter(); - let public_account = AccountWithMetadata { - account: Account { + let public_account = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 0, ..Account::default() }, - is_authorized: true, - }; + true, + AccountId::new([0; 32]), + ); let result = execute_and_prove( &[public_account], @@ -1164,14 +1177,15 @@ pub mod tests { #[test] fn test_nonce_changer_program_should_fail_in_privacy_preserving_circuit() { let program = Program::nonce_changer_program(); - let public_account = AccountWithMetadata { - account: Account { + let public_account = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 0, ..Account::default() }, - is_authorized: true, - }; + true, + AccountId::new([0; 32]), + ); let result = execute_and_prove( &[public_account], @@ -1189,14 +1203,15 @@ pub mod tests { #[test] fn test_data_changer_program_should_fail_for_non_owned_account_in_privacy_preserving_circuit() { let program = Program::data_changer(); - let public_account = AccountWithMetadata { - account: Account { + let public_account = AccountWithMetadata::new( + Account { program_owner: [0, 1, 2, 3, 4, 5, 6, 7], balance: 0, ..Account::default() }, - is_authorized: true, - }; + true, + AccountId::new([0; 32]), + ); let result = execute_and_prove( &[public_account], @@ -1214,14 +1229,15 @@ pub mod tests { #[test] fn test_extra_output_program_should_fail_in_privacy_preserving_circuit() { let program = Program::extra_output_program(); - let public_account = AccountWithMetadata { - account: Account { + let public_account = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 0, ..Account::default() }, - is_authorized: true, - }; + true, + AccountId::new([0; 32]), + ); let result = execute_and_prove( &[public_account], @@ -1239,22 +1255,24 @@ pub mod tests { #[test] fn test_missing_output_program_should_fail_in_privacy_preserving_circuit() { let program = Program::missing_output_program(); - let public_account_1 = AccountWithMetadata { - account: Account { + let public_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 0, ..Account::default() }, - is_authorized: true, - }; - let public_account_2 = AccountWithMetadata { - account: Account { + true, + AccountId::new([0; 32]), + ); + let public_account_2 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 0, ..Account::default() }, - is_authorized: true, - }; + true, + AccountId::new([1; 32]), + ); let result = execute_and_prove( &[public_account_1, public_account_2], @@ -1272,14 +1290,15 @@ pub mod tests { #[test] fn test_program_owner_changer_should_fail_in_privacy_preserving_circuit() { let program = Program::program_owner_changer(); - let public_account = AccountWithMetadata { - account: Account { + let public_account = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 0, ..Account::default() }, - is_authorized: true, - }; + true, + AccountId::new([0; 32]), + ); let result = execute_and_prove( &[public_account], @@ -1297,22 +1316,24 @@ pub mod tests { #[test] fn test_transfer_from_non_owned_account_should_fail_in_privacy_preserving_circuit() { let program = Program::simple_balance_transfer(); - let public_account_1 = AccountWithMetadata { - account: Account { + let public_account_1 = AccountWithMetadata::new( + Account { program_owner: [0, 1, 2, 3, 4, 5, 6, 7], balance: 100, ..Account::default() }, - is_authorized: true, - }; - let public_account_2 = AccountWithMetadata { - account: Account { + true, + AccountId::new([0; 32]), + ); + let public_account_2 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 0, ..Account::default() }, - is_authorized: true, - }; + true, + AccountId::new([1; 32]), + ); let result = execute_and_prove( &[public_account_1, public_account_2], @@ -1330,22 +1351,24 @@ pub mod tests { #[test] fn test_circuit_fails_if_visibility_masks_have_incorrect_lenght() { let program = Program::simple_balance_transfer(); - let public_account_1 = AccountWithMetadata { - account: Account { + let public_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let public_account_2 = AccountWithMetadata { - account: Account { + true, + AccountId::new([0; 32]), + ); + let public_account_2 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 0, ..Account::default() }, - is_authorized: true, - }; + true, + AccountId::new([1; 32]), + ); // Setting only one visibility mask for a circuit execution with two pre_state accounts. let visibility_mask = [0]; @@ -1367,18 +1390,17 @@ pub mod tests { 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 { - account: Account { + let private_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let private_account_2 = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; + true, + AccountId::new([0; 32]), + ); + let private_account_2 = + AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); // Setting only one nonce for an execution with two private accounts. let private_account_nonces = [0xdeadbeef1]; @@ -1408,18 +1430,17 @@ pub mod tests { fn test_circuit_fails_if_insufficient_keys_are_provided() { let program = Program::simple_balance_transfer(); let sender_keys = test_private_account_keys_1(); - let private_account_1 = AccountWithMetadata { - account: Account { + let private_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let private_account_2 = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; + true, + AccountId::new([0; 32]), + ); + let private_account_2 = + AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); // Setting only one key for an execution with two private accounts. let private_account_keys = [( @@ -1444,18 +1465,17 @@ pub mod tests { 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 { - account: Account { + let private_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let private_account_2 = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; + true, + AccountId::new([0; 32]), + ); + let private_account_2 = + AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); // Setting no auth key for an execution with one non default private accounts. let private_account_auth = []; @@ -1486,18 +1506,17 @@ pub mod tests { 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 { - account: Account { + let private_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let private_account_2 = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; + true, + AccountId::new([0; 32]), + ); + let private_account_2 = + AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); let private_account_keys = [ // First private account is the sender @@ -1535,22 +1554,24 @@ pub mod tests { 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 { - account: Account { + let private_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let private_account_2 = AccountWithMetadata { - account: Account { + true, + AccountId::new([0; 32]), + ); + let private_account_2 = AccountWithMetadata::new( + Account { // Non default balance balance: 1, ..Account::default() }, - is_authorized: false, - }; + false, + AccountId::new([1; 32]), + ); let result = execute_and_prove( &[private_account_1, private_account_2], @@ -1580,22 +1601,24 @@ pub mod tests { 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 { - account: Account { + let private_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let private_account_2 = AccountWithMetadata { - account: Account { + true, + AccountId::new([0; 32]), + ); + let private_account_2 = AccountWithMetadata::new( + Account { // Non default program_owner program_owner: [0, 1, 2, 3, 4, 5, 6, 7], ..Account::default() }, - is_authorized: false, - }; + false, + AccountId::new([1; 32]), + ); let result = execute_and_prove( &[private_account_1, private_account_2], @@ -1624,22 +1647,24 @@ pub mod tests { 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 { - account: Account { + let private_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let private_account_2 = AccountWithMetadata { - account: Account { + true, + AccountId::new([0; 32]), + ); + let private_account_2 = AccountWithMetadata::new( + Account { // Non default data data: b"hola mundo".to_vec(), ..Account::default() }, - is_authorized: false, - }; + false, + AccountId::new([1; 32]), + ); let result = execute_and_prove( &[private_account_1, private_account_2], @@ -1668,22 +1693,24 @@ pub mod tests { 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 { - account: Account { + let private_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let private_account_2 = AccountWithMetadata { - account: Account { + true, + AccountId::new([0; 32]), + ); + let private_account_2 = AccountWithMetadata::new( + Account { // Non default nonce nonce: 0xdeadbeef, ..Account::default() }, - is_authorized: false, - }; + false, + AccountId::new([1; 32]), + ); let result = execute_and_prove( &[private_account_1, private_account_2], @@ -1713,19 +1740,21 @@ pub mod tests { 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 { - account: Account { + let private_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let private_account_2 = AccountWithMetadata { - account: Account::default(), + true, + AccountId::new([0; 32]), + ); + let private_account_2 = AccountWithMetadata::new( + Account::default(), // This should be set to false in normal circumstances - is_authorized: true, - }; + true, + AccountId::new([1; 32]), + ); let result = execute_and_prove( &[private_account_1, private_account_2], @@ -1752,18 +1781,17 @@ pub mod tests { #[test] fn test_circuit_should_fail_with_invalid_visibility_mask_value() { let program = Program::simple_balance_transfer(); - let public_account_1 = AccountWithMetadata { - account: Account { + let public_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let public_account_2 = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; + true, + AccountId::new([0; 32]), + ); + let public_account_2 = + AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); let visibility_mask = [0, 3]; let result = execute_and_prove( @@ -1784,18 +1812,17 @@ pub mod tests { 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 { - account: Account { + let private_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let private_account_2 = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; + true, + AccountId::new([0; 32]), + ); + let private_account_2 = + AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); // Setting three new private account nonces for a circuit execution with only two private // accounts. @@ -1827,18 +1854,17 @@ pub mod tests { 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 { - account: Account { + let private_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let private_account_2 = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; + true, + AccountId::new([0; 32]), + ); + let private_account_2 = + AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); // Setting three private account keys for a circuit execution with only two private // accounts. @@ -1874,18 +1900,17 @@ pub mod tests { 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 { - account: Account { + let private_account_1 = AccountWithMetadata::new( + Account { program_owner: program.id(), balance: 100, ..Account::default() }, - is_authorized: true, - }; - let private_account_2 = AccountWithMetadata { - account: Account::default(), - is_authorized: false, - }; + true, + AccountId::new([0; 32]), + ); + let private_account_2 = + AccountWithMetadata::new(Account::default(), false, AccountId::new([1; 32])); // 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). diff --git a/nssa/program_methods/guest/Cargo.lock b/nssa/test_program_methods/guest/Cargo.lock similarity index 91% rename from nssa/program_methods/guest/Cargo.lock rename to nssa/test_program_methods/guest/Cargo.lock index 18285e9..8cb2bec 100644 --- a/nssa/program_methods/guest/Cargo.lock +++ b/nssa/test_program_methods/guest/Cargo.lock @@ -44,12 +44,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -61,9 +55,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" [[package]] name = "ark-bn254" @@ -107,7 +101,7 @@ checksum = "e7e89fe77d1f0f4fe5b96dfc940923d88d17b6a773808124f21e764dfb063c6a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -123,7 +117,7 @@ dependencies = [ "ark-std", "educe", "fnv", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "itertools", "num-bigint", "num-integer", @@ -158,7 +152,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62945a2f7e6de02a31fe400aa489f0e0f5b2502e69f95f853adb82a96c7a6b60" dependencies = [ "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -171,7 +165,7 @@ dependencies = [ "num-traits", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -201,7 +195,7 @@ dependencies = [ "ark-std", "educe", "fnv", - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -254,7 +248,7 @@ checksum = "213888f660fddcca0d257e88e54ac05bca01885f258ccdf695bafd77031bb69d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -291,6 +285,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.5.0" @@ -347,9 +347,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "34efbcccd345379ca2868b2b2c9d3782e9cc58ba87bc7d79d5b53d9c9ae6f25d" [[package]] name = "blake2" @@ -408,7 +408,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -434,7 +434,7 @@ checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -454,9 +454,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.10" +version = "1.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" +checksum = "dd0b03af37dad7a14518b7691d81acb0f8222604ad3d1b02f6b4bed5188c0cd5" dependencies = [ "serde", ] @@ -486,18 +486,19 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.31" +version = "1.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2" +checksum = "65193589c6404eb80b450d618eaf9a2cafaaafd57ecce47370519ef674a7bd44" dependencies = [ + "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -518,15 +519,14 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" dependencies = [ - "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-link", + "windows-link 0.2.0", ] [[package]] @@ -621,7 +621,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -632,7 +632,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -685,7 +685,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -695,7 +695,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -715,7 +715,7 @@ checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "unicode-xid", ] @@ -749,7 +749,7 @@ dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -760,7 +760,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -801,7 +801,7 @@ dependencies = [ "enum-ordinalize", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -854,7 +854,7 @@ checksum = "0d28318a75d4aead5c4db25382e8ef717932d0346600cacae6357eb5941bc5ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -865,12 +865,12 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -879,6 +879,12 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +[[package]] +name = "find-msvc-tools" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd99930f64d146689264c637b5af2f0233a933bef0d8570e2526bf9e083192d" + [[package]] name = "fnv" version = "1.0.7" @@ -909,7 +915,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -920,9 +926,9 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -957,7 +963,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1040,9 +1046,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "foldhash", @@ -1054,7 +1060,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.15.4", + "hashbrown 0.15.5", ] [[package]] @@ -1117,18 +1123,20 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +checksum = "eb3aa54a13a0dfe7fbe3a59e0c76093041720fdc77b110cc0fc260fafb4dc51e" dependencies = [ + "atomic-waker", "bytes", "futures-channel", - "futures-util", + "futures-core", "http", "http-body", "httparse", "itoa", "pin-project-lite", + "pin-utils", "smallvec", "tokio", "want", @@ -1153,9 +1161,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d9b05277c7e8da2c93a568989bb6207bef0112e8d17df7a6eda4a3cf143bc5e" +checksum = "3c6995591a8f1380fcb4ba966a252a4b29188d51d2b89e3a252f5305be65aea8" dependencies = [ "base64", "bytes", @@ -1169,7 +1177,7 @@ dependencies = [ "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.0", + "socket2", "tokio", "tower-service", "tracing", @@ -1177,9 +1185,9 @@ dependencies = [ [[package]] name = "iana-time-zone" -version = "0.1.63" +version = "0.1.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -1293,9 +1301,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -1331,12 +1339,12 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" dependencies = [ "equivalent", - "hashbrown 0.15.4", + "hashbrown 0.15.5", "serde", ] @@ -1351,11 +1359,11 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93587f37623a1a17d94ef2bc9ada592f5465fe7732084ab7beefabe5c77c0c4" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "cfg-if", "libc", ] @@ -1393,9 +1401,9 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "6247da8b8658ad4e73a186e747fcc5fc2a29f979d6fe6269127fdb5fd08298d0" dependencies = [ "once_cell", "wasm-bindgen", @@ -1430,7 +1438,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1444,9 +1452,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.174" +version = "0.2.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "6a82ae493e598baaea5209805c49bbf2ea7de956d50d7da0da1164f9c6d28543" [[package]] name = "libm" @@ -1456,19 +1464,19 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "391290121bad3d37fbddad76d8f5d1c1c314cfc646d143d7e07a3086ddff0ce3" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "libc", ] [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -1505,7 +1513,7 @@ checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1532,7 +1540,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "block", "core-graphics-types", "foreign-types", @@ -1657,7 +1665,7 @@ checksum = "77e878c846a8abae00dd069496dbe8751b16ac1c3d6bd2a7283a938e8228f90d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -1707,9 +1715,9 @@ dependencies = [ [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pin-project-lite" @@ -1758,9 +1766,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -1791,9 +1799,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -1806,7 +1814,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "version_check", "yansi", ] @@ -1816,7 +1824,6 @@ name = "programs" version = "0.1.0" dependencies = [ "nssa-core", - "risc0-zkvm", ] [[package]] @@ -1825,7 +1832,7 @@ version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fcdab19deb5195a31cf7726a210015ff1496ba1464fd42cb4f537b8b01b471f" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "num-traits", "rand 0.9.2", "rand_chacha 0.9.0", @@ -1853,14 +1860,14 @@ dependencies = [ "itertools", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "quinn" -version = "0.11.8" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", "cfg_aliases", @@ -1869,7 +1876,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2", "thiserror", "tokio", "tracing", @@ -1878,9 +1885,9 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.12" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" dependencies = [ "bytes", "getrandom 0.3.3", @@ -1899,16 +1906,16 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.13" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcebb1209ee276352ef14ff8732e24cc2b02bbac986cd74a4c81bcb2f9881970" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ "cfg_aliases", "libc", "once_cell", - "socket2 0.5.10", + "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] @@ -2021,14 +2028,14 @@ checksum = "1165225c21bff1f3bbce98f5a1f889949bc902d3575308cc7b0de30b4f6d27c7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" dependencies = [ "aho-corasick", "memchr", @@ -2038,9 +2045,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.9" +version = "0.4.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" dependencies = [ "aho-corasick", "memchr", @@ -2049,15 +2056,15 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "reqwest" -version = "0.12.22" +version = "0.12.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +checksum = "d429f34c8092b2d42c7c93cec323bb4adeb7c67698f70839adec842ec10c7ceb" dependencies = [ "base64", "bytes", @@ -2389,15 +2396,15 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustix" -version = "1.0.8" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "errno", "libc", "linux-raw-sys", - "windows-sys 0.60.2", + "windows-sys 0.61.0", ] [[package]] @@ -2426,9 +2433,9 @@ dependencies = [ [[package]] name = "rustls-webpki" -version = "0.103.4" +version = "0.103.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a17884ae0c1b773f1ccd2bd4a8c72f16da897310a98b0e84bf349ad5ead92fc" +checksum = "8572f3c2cb9934231157b45499fc41e1f58c589fdfb81a844ba873265e80f8eb" dependencies = [ "ring", "rustls-pki-types", @@ -2437,9 +2444,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" @@ -2516,14 +2523,14 @@ checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "serde_json" -version = "1.0.142" +version = "1.0.143" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7" +checksum = "d401abef1d108fbd9cbaebc3e46611f4b1021f714a0597a71f41ee463f5f4a5a" dependencies = [ "itoa", "memchr", @@ -2562,7 +2569,7 @@ dependencies = [ "chrono", "hex", "indexmap 1.9.3", - "indexmap 2.10.0", + "indexmap 2.11.0", "schemars 0.9.0", "schemars 1.0.4", "serde", @@ -2581,7 +2588,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2613,9 +2620,9 @@ dependencies = [ [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "smallvec" @@ -2623,16 +2630,6 @@ version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" -[[package]] -name = "socket2" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" -dependencies = [ - "libc", - "windows-sys 0.52.0", -] - [[package]] name = "socket2" version = "0.6.0" @@ -2666,7 +2663,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d904e7009df136af5297832a3ace3370cd14ff1546a232f4f185036c2736fcac" dependencies = [ "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2699,7 +2696,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2721,9 +2718,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -2747,47 +2744,47 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "tempfile" -version = "3.20.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +checksum = "84fa4d11fadde498443cca10fd3ac23c951f0dc59e080e9f4b93d4df4e4eea53" dependencies = [ "fastrand", "getrandom 0.3.3", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys 0.61.0", ] [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] name = "time" -version = "0.3.42" +version = "0.3.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca967379f9d8eb8058d86ed467d81d03e81acd45757e4ca341c24affbe8e8e3" +checksum = "83bde6f1ec10e72d583d91623c939f623002284ef622b87de38cfd546cbf2031" dependencies = [ "deranged", "num-conv", @@ -2799,15 +2796,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9108bb380861b07264b950ded55a44a14a4adc68b9f5efd85aafc3aa4d40a68" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" [[package]] name = "time-macros" -version = "0.2.23" +version = "0.2.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7182799245a7264ce590b349d90338f1c1affad93d2639aed5f8f69c090b334c" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" dependencies = [ "num-conv", "time-core", @@ -2825,9 +2822,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ "tinyvec_macros", ] @@ -2851,7 +2848,7 @@ dependencies = [ "mio", "pin-project-lite", "slab", - "socket2 0.6.0", + "socket2", "windows-sys 0.59.0", ] @@ -2905,7 +2902,7 @@ version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.10.0", + "indexmap 2.11.0", "serde", "serde_spanned", "toml_datetime", @@ -2940,7 +2937,7 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", "bytes", "futures-util", "http", @@ -2984,7 +2981,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3044,13 +3041,14 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", "percent-encoding", + "serde", ] [[package]] @@ -3097,35 +3095,36 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "4ad224d2776649cfb4f4471124f8176e54c1cca67a88108e30a0cd98b90e7ad3" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "3a1364104bdcd3c03f22b16a3b1c9620891469f5e9f09bc38b2db121e593e732" dependencies = [ "bumpalo", "log", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "9c0a08ecf5d99d5604a6666a70b3cde6ab7cc6142f5e641a8ef48fc744ce8854" dependencies = [ "cfg-if", "js-sys", @@ -3136,9 +3135,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "0d7ab4ca3e367bb1ed84ddbd83cc6e41e115f8337ed047239578210214e36c76" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3146,22 +3145,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "4a518014843a19e2dbbd0ed5dfb6b99b23fb886b14e6192a00803a3e14c552b0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "255eb0aa4cc2eea3662a00c2bbd66e93911b7361d5e0fcd62385acfd7e15dcee" dependencies = [ "unicode-ident", ] @@ -3181,9 +3180,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "50462a022f46851b81d5441d1a6f5bac0b21a1d72d64bd4906fbdd4bf7230ec7" dependencies = [ "js-sys", "wasm-bindgen", @@ -3210,13 +3209,13 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.61.2" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +checksum = "57fe7168f7de578d2d8a05b07fd61870d2e73b4020e9f49aa00da8471723497c" dependencies = [ "windows-implement", "windows-interface", - "windows-link", + "windows-link 0.2.0", "windows-result", "windows-strings", ] @@ -3229,7 +3228,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3240,7 +3239,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3250,21 +3249,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" [[package]] -name = "windows-result" -version = "0.3.4" +name = "windows-link" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + +[[package]] +name = "windows-result" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7084dcc306f89883455a206237404d3eaf961e5bd7e0f312f7c91f57eb44167f" dependencies = [ - "windows-link", + "windows-link 0.2.0", ] [[package]] name = "windows-strings" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +checksum = "7218c655a553b0bed4426cf54b20d7ba363ef543b52d515b3e48d7fd55318dda" dependencies = [ - "windows-link", + "windows-link 0.2.0", ] [[package]] @@ -3294,6 +3299,15 @@ dependencies = [ "windows-targets 0.53.3", ] +[[package]] +name = "windows-sys" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e201184e40b2ede64bc2ea34968b28e33622acdbbf37104f0e4a33f7abe657aa" +dependencies = [ + "windows-link 0.2.0", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -3316,7 +3330,7 @@ version = "0.53.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" dependencies = [ - "windows-link", + "windows-link 0.1.3", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -3425,9 +3439,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" [[package]] name = "winnow" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -3438,7 +3452,7 @@ version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.3", ] [[package]] @@ -3449,9 +3463,9 @@ checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" [[package]] name = "yaml-rust2" -version = "0.10.3" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ce2a4ff45552406d02501cea6c18d8a7e50228e7736a872951fe2fe75c91be7" +checksum = "2462ea039c445496d8793d052e13787f2b90e750b833afee748e601c17621ed9" dependencies = [ "arraydeque", "encoding_rs", @@ -3484,7 +3498,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -3505,7 +3519,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3525,7 +3539,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -3546,7 +3560,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3579,5 +3593,5 @@ checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] diff --git a/sequencer_core/Cargo.toml b/sequencer_core/Cargo.toml index bfed0cb..72a8cc4 100644 --- a/sequencer_core/Cargo.toml +++ b/sequencer_core/Cargo.toml @@ -11,6 +11,7 @@ rand.workspace = true tempfile.workspace = true chrono.workspace = true log.workspace = true +nssa-core = { path = "../nssa/core", features = ["host"] } [dependencies.storage] path = "../storage" diff --git a/sequencer_core/src/config.rs b/sequencer_core/src/config.rs index cdf79a3..2f57f72 100644 --- a/sequencer_core/src/config.rs +++ b/sequencer_core/src/config.rs @@ -9,6 +9,13 @@ pub struct AccountInitialData { pub balance: u128, } +#[derive(Debug, Serialize, Deserialize, Clone)] +///Helperstruct to initialize commitments +pub struct CommitmentsInitialData { + pub npk: nssa_core::NullifierPublicKey, + pub account: nssa_core::account::Account, +} + #[derive(Clone, Serialize, Deserialize)] pub struct SequencerConfig { ///Home dir of sequencer storage @@ -27,6 +34,8 @@ pub struct SequencerConfig { pub port: u16, ///List of initial accounts data pub initial_accounts: Vec, + ///List of initial commitments + pub initial_commitments: Vec, ///Sequencer own signing key pub signing_key: [u8; 32], } diff --git a/sequencer_core/src/lib.rs b/sequencer_core/src/lib.rs index 59b787e..6c54a50 100644 --- a/sequencer_core/src/lib.rs +++ b/sequencer_core/src/lib.rs @@ -49,12 +49,27 @@ impl std::error::Error for TransactionMalformationErrorKind {} impl SequencerCore { pub fn start_from_config(config: SequencerConfig) -> Self { + let mut initial_commitments = vec![]; + + for init_comm_data in config.initial_commitments.clone() { + let npk = init_comm_data.npk; + + let mut acc = init_comm_data.account; + + acc.program_owner = nssa::program::Program::authenticated_transfer_program().id(); + + let comm = nssa_core::Commitment::new(&npk, &acc); + + initial_commitments.push(comm); + } + Self { store: SequecerChainStore::new_with_genesis( &config.home, config.genesis_id, config.is_genesis_random, &config.initial_accounts, + &initial_commitments, nssa::PrivateKey::try_new(config.signing_key).unwrap(), ), mempool: MemPool::default(), @@ -209,6 +224,7 @@ mod tests { block_create_timeout_millis: 1000, port: 8080, initial_accounts, + initial_commitments: vec![], signing_key: *sequencer_sign_key_for_testing().value(), } } diff --git a/sequencer_core/src/sequencer_store/mod.rs b/sequencer_core/src/sequencer_store/mod.rs index 38085c7..ad2939a 100644 --- a/sequencer_core/src/sequencer_store/mod.rs +++ b/sequencer_core/src/sequencer_store/mod.rs @@ -20,6 +20,7 @@ impl SequecerChainStore { genesis_id: u64, is_genesis_random: bool, initial_accounts: &[AccountInitialData], + initial_commitments: &[nssa_core::Commitment], signing_key: nssa::PrivateKey, ) -> Self { let init_accs: Vec<(Address, u128)> = initial_accounts @@ -28,11 +29,12 @@ impl SequecerChainStore { .collect(); #[cfg(not(feature = "testnet"))] - let state = nssa::V01State::new_with_genesis_accounts(&init_accs); + let state = nssa::V01State::new_with_genesis_accounts(&init_accs, initial_commitments); #[cfg(feature = "testnet")] let state = { - let mut this = nssa::V01State::new_with_genesis_accounts(&init_accs); + let mut this = + nssa::V01State::new_with_genesis_accounts(&init_accs, initial_commitments); this.add_pinata_program("cafe".repeat(16).parse().unwrap()); this }; diff --git a/sequencer_rpc/Cargo.toml b/sequencer_rpc/Cargo.toml index af7e011..433eee1 100644 --- a/sequencer_rpc/Cargo.toml +++ b/sequencer_rpc/Cargo.toml @@ -12,6 +12,7 @@ actix-cors.workspace = true futures.workspace = true hex.workspace = true tempfile.workspace = true +nssa-core = { path = "../nssa/core", features = ["host"] } base64.workspace = true actix-web.workspace = true diff --git a/sequencer_rpc/src/process.rs b/sequencer_rpc/src/process.rs index db3b4c4..5b456fe 100644 --- a/sequencer_rpc/src/process.rs +++ b/sequencer_rpc/src/process.rs @@ -14,7 +14,8 @@ use common::{ requests::{ GetAccountBalanceRequest, GetAccountBalanceResponse, GetAccountRequest, GetAccountResponse, GetAccountsNoncesRequest, GetAccountsNoncesResponse, - GetInitialTestnetAccountsRequest, GetTransactionByHashRequest, + GetInitialTestnetAccountsRequest, GetProofByCommitmentRequest, + GetProofByCommitmentResponse, GetTransactionByHashRequest, GetTransactionByHashResponse, }, }, @@ -38,6 +39,7 @@ pub const GET_ACCOUNT_BALANCE: &str = "get_account_balance"; pub const GET_TRANSACTION_BY_HASH: &str = "get_transaction_by_hash"; pub const GET_ACCOUNTS_NONCES: &str = "get_accounts_nonces"; pub const GET_ACCOUNT: &str = "get_account"; +pub const GET_PROOF_FOR_COMMITMENT: &str = "get_proof_for_commitment"; pub const HELLO_FROM_SEQUENCER: &str = "HELLO_FROM_SEQUENCER"; @@ -250,6 +252,21 @@ impl JsonHandler { respond(helperstruct) } + /// Returns the commitment proof, corresponding to commitment + async fn process_get_proof_by_commitment(&self, request: Request) -> Result { + let get_proof_req = GetProofByCommitmentRequest::parse(Some(request.params))?; + + let membership_proof = { + let state = self.sequencer_state.lock().await; + state + .store + .state + .get_proof_for_commitment(&get_proof_req.commitment) + }; + let helperstruct = GetProofByCommitmentResponse { membership_proof }; + respond(helperstruct) + } + pub async fn process_request_internal(&self, request: Request) -> Result { match request.method.as_ref() { HELLO => self.process_temp_hello(request).await, @@ -262,6 +279,7 @@ impl JsonHandler { GET_ACCOUNTS_NONCES => self.process_get_accounts_nonces(request).await, GET_ACCOUNT => self.process_get_account(request).await, GET_TRANSACTION_BY_HASH => self.process_get_transaction_by_hash(request).await, + GET_PROOF_FOR_COMMITMENT => self.process_get_proof_by_commitment(request).await, _ => Err(RpcErr(RpcError::method_not_found(request.method))), } } @@ -320,6 +338,7 @@ mod tests { block_create_timeout_millis: 1000, port: 8080, initial_accounts, + initial_commitments: vec![], signing_key: *sequencer_sign_key_for_testing().value(), } } diff --git a/wallet/Cargo.toml b/wallet/Cargo.toml index af669ad..76dda9e 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -14,8 +14,10 @@ tempfile.workspace = true clap.workspace = true nssa-core = { path = "../nssa/core" } base64.workspace = true +k256 = { version = "0.13.3" } bytemuck = "1.23.2" borsh.workspace = true +hex.workspace = true [dependencies.key_protocol] path = "../key_protocol" diff --git a/wallet/src/chain_storage/mod.rs b/wallet/src/chain_storage/mod.rs index d3b07b3..e2923fe 100644 --- a/wallet/src/chain_storage/mod.rs +++ b/wallet/src/chain_storage/mod.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; use anyhow::Result; use key_protocol::key_protocol_core::NSSAUserData; -use crate::config::{PersistentAccountData, WalletConfig}; +use crate::config::{InitialAccountData, PersistentAccountData, WalletConfig}; pub struct WalletChainStore { pub user_data: NSSAUserData, @@ -12,23 +12,50 @@ pub struct WalletChainStore { impl WalletChainStore { pub fn new(config: WalletConfig) -> Result { - let accounts_keys: HashMap = config - .initial_accounts - .clone() - .into_iter() - .map(|init_acc_data| (init_acc_data.address, init_acc_data.pub_sign_key)) - .collect(); + let mut public_init_acc_map = HashMap::new(); + let mut private_init_acc_map = HashMap::new(); + + for init_acc_data in config.initial_accounts.clone() { + match init_acc_data { + InitialAccountData::Public(data) => { + public_init_acc_map.insert(data.address, data.pub_sign_key); + } + InitialAccountData::Private(data) => { + private_init_acc_map.insert(data.address, (data.key_chain, data.account)); + } + } + } Ok(Self { - user_data: NSSAUserData::new_with_accounts(accounts_keys, HashMap::new())?, + user_data: NSSAUserData::new_with_accounts(public_init_acc_map, private_init_acc_map)?, wallet_config: config, }) } - pub(crate) fn insert_account_data(&mut self, acc_data: PersistentAccountData) { + pub fn insert_private_account_data( + &mut self, + addr: nssa::Address, + account: nssa_core::account::Account, + ) { self.user_data - .pub_account_signing_keys - .insert(acc_data.address, acc_data.pub_sign_key); + .user_private_accounts + .entry(addr) + .and_modify(|(_, acc)| *acc = account); + } + + pub(crate) fn insert_account_data(&mut self, acc_data: PersistentAccountData) { + match acc_data { + PersistentAccountData::Public(acc_data) => { + self.user_data + .pub_account_signing_keys + .insert(acc_data.address, acc_data.pub_sign_key); + } + PersistentAccountData::Private(acc_data) => { + self.user_data + .user_private_accounts + .insert(acc_data.address, (acc_data.key_chain, acc_data.account)); + } + } } } @@ -42,24 +69,16 @@ mod tests { fn create_initial_accounts() -> Vec { let initial_acc1 = serde_json::from_str(r#"{ - "address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", - "pub_sign_key": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], - "account": { - "program_owner": [0,0,0,0,0,0,0,0], - "balance": 100, - "nonce": 0, - "data": [] + "Public": { + "address": "1b84c5567b126440995d3ed5aaba0565d71e1834604819ff9c17f5e9d5dd078f", + "pub_sign_key": [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] } }"#).unwrap(); let initial_acc2 = serde_json::from_str(r#"{ - "address": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", - "pub_sign_key": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2], - "account": { - "program_owner": [0,0,0,0,0,0,0,0], - "balance": 100, - "nonce": 0, - "data": [] + "Public": { + "address": "4d4b6cd1361032ca9bd2aeb9d900aa4d45d9ead80ac9423374c451a7254d0766", + "pub_sign_key": [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] } }"#).unwrap(); diff --git a/wallet/src/config.rs b/wallet/src/config.rs index b6355ce..b5d861c 100644 --- a/wallet/src/config.rs +++ b/wallet/src/config.rs @@ -1,19 +1,93 @@ +use key_protocol::key_management::KeyChain; use serde::{Deserialize, Serialize}; use std::path::PathBuf; #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct InitialAccountData { +pub struct InitialAccountDataPublic { pub address: nssa::Address, - pub account: nssa_core::account::Account, pub pub_sign_key: nssa::PrivateKey, } #[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PersistentAccountData { +pub struct PersistentAccountDataPublic { pub address: nssa::Address, pub pub_sign_key: nssa::PrivateKey, } +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct InitialAccountDataPrivate { + pub address: nssa::Address, + pub account: nssa_core::account::Account, + pub key_chain: KeyChain, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PersistentAccountDataPrivate { + pub address: nssa::Address, + pub account: nssa_core::account::Account, + pub key_chain: KeyChain, +} + +//Big difference in enum variants sizes +//however it is improbable, that we will have that much accounts, that it will substantialy affect memory +#[allow(clippy::large_enum_variant)] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum InitialAccountData { + Public(InitialAccountDataPublic), + Private(InitialAccountDataPrivate), +} + +//Big difference in enum variants sizes +//however it is improbable, that we will have that much accounts, that it will substantialy affect memory +#[allow(clippy::large_enum_variant)] +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum PersistentAccountData { + Public(PersistentAccountDataPublic), + Private(PersistentAccountDataPrivate), +} + +impl InitialAccountData { + pub fn address(&self) -> nssa::Address { + match &self { + Self::Public(acc) => acc.address, + Self::Private(acc) => acc.address, + } + } +} + +impl PersistentAccountData { + pub fn address(&self) -> nssa::Address { + match &self { + Self::Public(acc) => acc.address, + Self::Private(acc) => acc.address, + } + } +} + +impl From for InitialAccountData { + fn from(value: InitialAccountDataPublic) -> Self { + Self::Public(value) + } +} + +impl From for InitialAccountData { + fn from(value: InitialAccountDataPrivate) -> Self { + Self::Private(value) + } +} + +impl From for PersistentAccountData { + fn from(value: PersistentAccountDataPublic) -> Self { + Self::Public(value) + } +} + +impl From for PersistentAccountData { + fn from(value: PersistentAccountDataPrivate) -> Self { + Self::Private(value) + } +} + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct GasConfig { /// Gas spent per deploying one byte of data diff --git a/wallet/src/helperfunctions.rs b/wallet/src/helperfunctions.rs index e3e6119..b46fe19 100644 --- a/wallet/src/helperfunctions.rs +++ b/wallet/src/helperfunctions.rs @@ -8,7 +8,10 @@ use serde::Serialize; use crate::{ HOME_DIR_ENV_VAR, - config::{PersistentAccountData, WalletConfig}, + config::{ + PersistentAccountData, PersistentAccountDataPrivate, PersistentAccountDataPublic, + WalletConfig, + }, }; /// Get home dir for wallet. Env var `NSSA_WALLET_HOME_DIR` must be set before execution to succeed. @@ -56,10 +59,24 @@ pub fn produce_data_for_storage(user_data: &NSSAUserData) -> Vec Address { + pub fn create_new_account_public(&mut self) -> Address { self.storage .user_data .generate_new_public_transaction_private_key() } - pub fn search_for_initial_account(&self, acc_addr: Address) -> Option { - for initial_acc in &self.storage.wallet_config.initial_accounts { - if initial_acc.address == acc_addr { - return Some(initial_acc.account.clone()); - } - } - None + pub fn create_new_account_private(&mut self) -> Address { + self.storage + .user_data + .generate_new_privacy_preserving_transaction_key_chain() } pub async fn claim_pinata( @@ -105,48 +104,63 @@ impl WalletCore { Ok(self.sequencer_client.send_tx_public(tx).await?) } - pub async fn send_public_native_token_transfer( + pub async fn send_new_token_definition( &self, - from: Address, - to: Address, - balance_to_move: u128, + definition_address: Address, + supply_address: Address, + name: [u8; 6], + total_supply: u128, ) -> Result { - let Ok(balance) = self.get_account_balance(from).await else { - return Err(ExecutionFailureKind::SequencerError); - }; + let addresses = vec![definition_address, supply_address]; + let program_id = nssa::program::Program::token().id(); + // Instruction must be: [0x00 || total_supply (little-endian 16 bytes) || name (6 bytes)] + let mut instruction = [0; 23]; + instruction[1..17].copy_from_slice(&total_supply.to_le_bytes()); + instruction[17..].copy_from_slice(&name); + let message = + nssa::public_transaction::Message::try_new(program_id, addresses, vec![], instruction) + .unwrap(); - if balance >= balance_to_move { - let Ok(nonces) = self.get_accounts_nonces(vec![from]).await else { - return Err(ExecutionFailureKind::SequencerError); - }; + let witness_set = nssa::public_transaction::WitnessSet::for_message(&message, &[]); - let addresses = vec![from, to]; - let program_id = nssa::program::Program::authenticated_transfer_program().id(); - let message = nssa::public_transaction::Message::try_new( - program_id, - addresses, - nonces, - balance_to_move, - ) - .unwrap(); + let tx = nssa::PublicTransaction::new(message, witness_set); - let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); - - let Some(signing_key) = signing_key else { - return Err(ExecutionFailureKind::KeyNotFoundError); - }; - - let witness_set = - nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); - - let tx = nssa::PublicTransaction::new(message, witness_set); - - Ok(self.sequencer_client.send_tx_public(tx).await?) - } else { - Err(ExecutionFailureKind::InsufficientFundsError) - } + Ok(self.sequencer_client.send_tx_public(tx).await?) } + pub async fn send_transfer_token_transaction( + &self, + sender_address: Address, + recipient_address: Address, + amount: u128, + ) -> Result { + let addresses = vec![sender_address, recipient_address]; + let program_id = nssa::program::Program::token().id(); + // Instruction must be: [0x01 || amount (little-endian 16 bytes) || 0x00 || 0x00 || 0x00 || 0x00 || 0x00 || 0x00]. + let mut instruction = [0; 23]; + instruction[0] = 0x01; + instruction[1..17].copy_from_slice(&amount.to_le_bytes()); + let Ok(nonces) = self.get_accounts_nonces(vec![sender_address]).await else { + return Err(ExecutionFailureKind::SequencerError); + }; + let message = + nssa::public_transaction::Message::try_new(program_id, addresses, nonces, instruction) + .unwrap(); + + let Some(signing_key) = self + .storage + .user_data + .get_pub_account_signing_key(&sender_address) + else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + let witness_set = + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + + let tx = nssa::PublicTransaction::new(message, witness_set); + + Ok(self.sequencer_client.send_tx_public(tx).await?) + } ///Get account balance pub async fn get_account_balance(&self, acc: Address) -> Result { Ok(self @@ -172,7 +186,7 @@ impl WalletCore { } ///Poll transactions - pub async fn poll_public_native_token_transfer(&self, hash: String) -> Result { + pub async fn poll_native_token_transfer(&self, hash: String) -> Result { let transaction_encoded = self.poller.poll_tx(hash).await?; let tx_base64_decode = BASE64.decode(transaction_encoded)?; let pub_tx = borsh::from_slice::(&tx_base64_decode).unwrap(); @@ -186,7 +200,9 @@ impl WalletCore { #[clap(about)] pub enum Command { ///Send native token transfer from `from` to `to` for `amount` - SendNativeTokenTransfer { + /// + /// Public operation + SendNativeTokenTransferPublic { ///from - valid 32 byte hex string #[arg(long)] from: String, @@ -197,8 +213,98 @@ pub enum Command { #[arg(long)] amount: u128, }, - ///Register new account - RegisterAccount {}, + ///Send native token transfer from `from` to `to` for `amount` + /// + /// Private operation + SendNativeTokenTransferPrivate { + ///from - valid 32 byte hex string + #[arg(long)] + from: String, + ///to - valid 32 byte hex string + #[arg(long)] + to: String, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, + ///Send native token transfer from `from` to `to` for `amount` + /// + /// Private operation + SendNativeTokenTransferPrivateForeignAccount { + ///from - valid 32 byte hex string + #[arg(long)] + from: String, + ///to_npk - valid 32 byte hex string + #[arg(long)] + to_npk: String, + ///to_ipk - valid 33 byte hex string + #[arg(long)] + to_ipk: String, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, + ///Send native token transfer from `from` to `to` for `amount` + /// + /// Deshielded operation + SendNativeTokenTransferDeshielded { + ///from - valid 32 byte hex string + #[arg(long)] + from: String, + ///to - valid 32 byte hex string + #[arg(long)] + to: String, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, + ///Send native token transfer from `from` to `to` for `amount` + /// + /// Shielded operation + SendNativeTokenTransferShielded { + ///from - valid 32 byte hex string + #[arg(long)] + from: String, + ///to - valid 32 byte hex string + #[arg(long)] + to: String, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, + ///Send native token transfer from `from` to `to` for `amount` + /// + /// Shielded operation + SendNativeTokenTransferShieldedForeignAccount { + ///from - valid 32 byte hex string + #[arg(long)] + from: String, + ///to_npk - valid 32 byte hex string + #[arg(long)] + to_npk: String, + ///to_ipk - valid 33 byte hex string + #[arg(long)] + to_ipk: String, + ///amount - amount of balance to move + #[arg(long)] + amount: u128, + }, + ///Claim account `acc_addr` generated in transaction `tx_hash`, using secret `sh_secret` at ciphertext id `ciph_id` + ClaimPrivateAccount { + ///tx_hash - valid 32 byte hex string + #[arg(long)] + tx_hash: String, + ///acc_addr - valid 32 byte hex string + #[arg(long)] + acc_addr: String, + ///ciph_id - id of cipher in transaction + #[arg(long)] + ciph_id: usize, + }, + ///Register new public account + RegisterAccountPublic {}, + ///Register new private account + RegisterAccountPrivate {}, ///Fetch transaction by `hash` FetchTx { #[arg(short, long)] @@ -219,6 +325,26 @@ pub enum Command { #[arg(short, long)] addr: String, }, + //Create a new token using the token program + CreateNewToken { + #[arg(short, long)] + definition_addr: String, + #[arg(short, long)] + supply_addr: String, + #[arg(short, long)] + name: String, + #[arg(short, long)] + total_supply: u128, + }, + //Transfer tokens using the token program + TransferToken { + #[arg(short, long)] + sender_addr: String, + #[arg(short, long)] + recipient_addr: String, + #[arg(short, long)] + balance_to_move: u128, + }, // TODO: Testnet only. Refactor to prevent compilation on mainnet. // Claim piƱata prize ClaimPinata { @@ -243,12 +369,19 @@ pub struct Args { pub command: Command, } -pub async fn execute_subcommand(command: Command) -> Result<()> { +#[derive(Debug, Clone)] +pub enum SubcommandReturnValue { + PrivacyPreservingTransfer { tx_hash: String }, + RegisterAccount { addr: nssa::Address }, + Empty, +} + +pub async fn execute_subcommand(command: Command) -> Result { let wallet_config = fetch_config()?; let mut wallet_core = WalletCore::start_from_config_update_chain(wallet_config)?; - match command { - Command::SendNativeTokenTransfer { from, to, amount } => { + let subcommand_ret = match command { + Command::SendNativeTokenTransferPublic { from, to, amount } => { let from = produce_account_addr_from_hex(from)?; let to = produce_account_addr_from_hex(to)?; @@ -258,17 +391,336 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { println!("Results of tx send is {res:#?}"); - let transfer_tx = wallet_core - .poll_public_native_token_transfer(res.tx_hash) - .await?; + let transfer_tx = wallet_core.poll_native_token_transfer(res.tx_hash).await?; println!("Transaction data is {transfer_tx:?}"); + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::Empty } - Command::RegisterAccount {} => { - let addr = wallet_core.create_new_account(); - wallet_core.store_persistent_accounts()?; + Command::SendNativeTokenTransferPrivate { from, to, amount } => { + let from = produce_account_addr_from_hex(from)?; + let to = produce_account_addr_from_hex(to)?; + + let (res, secret) = wallet_core + .send_private_native_token_transfer(from, to, amount) + .await?; + + println!("Results of tx send is {res:#?}"); + + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let from_ebc = tx.message.encrypted_private_post_states[0].clone(); + let from_comm = tx.message.new_commitments[0].clone(); + + let to_ebc = tx.message.encrypted_private_post_states[1].clone(); + let to_comm = tx.message.new_commitments[1].clone(); + + let res_acc_from = nssa_core::EncryptionScheme::decrypt( + &from_ebc.ciphertext, + &secret, + &from_comm, + 0, + ) + .unwrap(); + + let res_acc_to = + nssa_core::EncryptionScheme::decrypt(&to_ebc.ciphertext, &secret, &to_comm, 1) + .unwrap(); + + println!("Received new from acc {res_acc_from:#?}"); + println!("Received new to acc {res_acc_to:#?}"); + + println!("Transaction data is {:?}", tx.message); + + wallet_core + .storage + .insert_private_account_data(from, res_acc_from); + wallet_core + .storage + .insert_private_account_data(to, res_acc_to); + } + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } + } + Command::SendNativeTokenTransferPrivateForeignAccount { + from, + to_npk, + to_ipk, + amount, + } => { + let from = produce_account_addr_from_hex(from)?; + let to_npk_res = hex::decode(to_npk)?; + let mut to_npk = [0; 32]; + to_npk.copy_from_slice(&to_npk_res); + let to_npk = nssa_core::NullifierPublicKey(to_npk); + + let to_ipk_res = hex::decode(to_ipk)?; + let mut to_ipk = [0u8; 33]; + to_ipk.copy_from_slice(&to_ipk_res); + let to_ipk = + nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); + + let (res, secret) = wallet_core + .send_private_native_token_transfer_outer_account(from, to_npk, to_ipk, amount) + .await?; + + println!("Results of tx send is {res:#?}"); + + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let from_ebc = tx.message.encrypted_private_post_states[0].clone(); + let from_comm = tx.message.new_commitments[0].clone(); + + let to_ebc = tx.message.encrypted_private_post_states[1].clone(); + let to_comm = tx.message.new_commitments[1].clone(); + + let res_acc_from = nssa_core::EncryptionScheme::decrypt( + &from_ebc.ciphertext, + &secret, + &from_comm, + 0, + ) + .unwrap(); + + let res_acc_to = + nssa_core::EncryptionScheme::decrypt(&to_ebc.ciphertext, &secret, &to_comm, 1) + .unwrap(); + + println!("RES acc {res_acc_from:#?}"); + println!("RES acc to {res_acc_to:#?}"); + + println!("Transaction data is {:?}", tx.message); + + wallet_core + .storage + .insert_private_account_data(from, res_acc_from); + } + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } + } + Command::SendNativeTokenTransferDeshielded { from, to, amount } => { + let from = produce_account_addr_from_hex(from)?; + let to = produce_account_addr_from_hex(to)?; + + let (res, secret) = wallet_core + .send_deshielded_native_token_transfer(from, to, amount) + .await?; + + println!("Results of tx send is {res:#?}"); + + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let from_ebc = tx.message.encrypted_private_post_states[0].clone(); + let from_comm = tx.message.new_commitments[0].clone(); + + let res_acc_from = nssa_core::EncryptionScheme::decrypt( + &from_ebc.ciphertext, + &secret, + &from_comm, + 0, + ) + .unwrap(); + + println!("RES acc {res_acc_from:#?}"); + + println!("Transaction data is {:?}", tx.message); + + wallet_core + .storage + .insert_private_account_data(from, res_acc_from); + } + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } + } + Command::SendNativeTokenTransferShielded { from, to, amount } => { + let from = produce_account_addr_from_hex(from)?; + let to = produce_account_addr_from_hex(to)?; + + let (res, secret) = wallet_core + .send_shiedled_native_token_transfer(from, to, amount) + .await?; + + println!("Results of tx send is {res:#?}"); + + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let to_ebc = tx.message.encrypted_private_post_states[0].clone(); + let to_comm = tx.message.new_commitments[0].clone(); + + let res_acc_to = + nssa_core::EncryptionScheme::decrypt(&to_ebc.ciphertext, &secret, &to_comm, 0) + .unwrap(); + + println!("RES acc to {res_acc_to:#?}"); + + println!("Transaction data is {:?}", tx.message); + } + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } + } + Command::SendNativeTokenTransferShieldedForeignAccount { + from, + to_npk, + to_ipk, + amount, + } => { + let from = produce_account_addr_from_hex(from)?; + + let to_npk_res = hex::decode(to_npk)?; + let mut to_npk = [0; 32]; + to_npk.copy_from_slice(&to_npk_res); + let to_npk = nssa_core::NullifierPublicKey(to_npk); + + let to_ipk_res = hex::decode(to_ipk)?; + let mut to_ipk = [0u8; 33]; + to_ipk.copy_from_slice(&to_ipk_res); + let to_ipk = + nssa_core::encryption::shared_key_derivation::Secp256k1Point(to_ipk.to_vec()); + + let (res, secret) = wallet_core + .send_shielded_native_token_transfer_maybe_outer_account( + from, to_npk, to_ipk, amount, + ) + .await?; + + println!("Results of tx send is {res:#?}"); + + let tx_hash = res.tx_hash; + let transfer_tx = wallet_core + .poll_native_token_transfer(tx_hash.clone()) + .await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let to_ebc = tx.message.encrypted_private_post_states[0].clone(); + let to_comm = tx.message.new_commitments[0].clone(); + + let res_acc_to = + nssa_core::EncryptionScheme::decrypt(&to_ebc.ciphertext, &secret, &to_comm, 0) + .unwrap(); + + println!("RES acc to {res_acc_to:#?}"); + + println!("Transaction data is {:?}", tx.message); + } + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::PrivacyPreservingTransfer { tx_hash } + } + Command::ClaimPrivateAccount { + tx_hash, + acc_addr, + ciph_id, + } => { + let acc_addr = produce_account_addr_from_hex(acc_addr)?; + + let account_key_chain = wallet_core + .storage + .user_data + .user_private_accounts + .get(&acc_addr); + + let Some((account_key_chain, _)) = account_key_chain else { + anyhow::bail!("Account not found"); + }; + + let transfer_tx = wallet_core.poll_native_token_transfer(tx_hash).await?; + + if let NSSATransaction::PrivacyPreserving(tx) = transfer_tx { + let to_ebc = tx.message.encrypted_private_post_states[ciph_id].clone(); + let to_comm = tx.message.new_commitments[ciph_id].clone(); + let shared_secret = account_key_chain.calculate_shared_secret_receiver(to_ebc.epk); + + let res_acc_to = nssa_core::EncryptionScheme::decrypt( + &to_ebc.ciphertext, + &shared_secret, + &to_comm, + ciph_id as u32, + ) + .unwrap(); + + println!("RES acc to {res_acc_to:#?}"); + + println!("Transaction data is {:?}", tx.message); + + wallet_core + .storage + .insert_private_account_data(acc_addr, res_acc_to); + } + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::Empty + } + Command::RegisterAccountPublic {} => { + let addr = wallet_core.create_new_account_public(); println!("Generated new account with addr {addr}"); + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::RegisterAccount { addr } + } + Command::RegisterAccountPrivate {} => { + let addr = wallet_core.create_new_account_private(); + + let (key, account) = wallet_core + .storage + .user_data + .get_private_account(&addr) + .unwrap(); + + println!("Generated new account with addr {addr:#?}"); + println!("With key {key:#?}"); + println!("With account {account:#?}"); + + let path = wallet_core.store_persistent_accounts()?; + + println!("Stored persistent accounts at {path:#?}"); + + SubcommandReturnValue::RegisterAccount { addr } } Command::FetchTx { tx_hash } => { let tx_obj = wallet_core @@ -277,23 +729,68 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { .await?; println!("Transaction object {tx_obj:#?}"); + + SubcommandReturnValue::Empty } Command::GetAccountBalance { addr } => { let addr = Address::from_str(&addr)?; let balance = wallet_core.get_account_balance(addr).await?; println!("Accounts {addr} balance is {balance}"); + + SubcommandReturnValue::Empty } Command::GetAccountNonce { addr } => { let addr = Address::from_str(&addr)?; let nonce = wallet_core.get_accounts_nonces(vec![addr]).await?[0]; println!("Accounts {addr} nonce is {nonce}"); + + SubcommandReturnValue::Empty } Command::GetAccount { addr } => { let addr: Address = addr.parse()?; let account: HumanReadableAccount = wallet_core.get_account(addr).await?.into(); println!("{}", serde_json::to_string(&account).unwrap()); + + SubcommandReturnValue::Empty + } + Command::CreateNewToken { + definition_addr, + supply_addr, + name, + total_supply, + } => { + let name = name.as_bytes(); + if name.len() > 6 { + // TODO: return error + panic!(); + } + let mut name_bytes = [0; 6]; + name_bytes[..name.len()].copy_from_slice(name); + wallet_core + .send_new_token_definition( + definition_addr.parse().unwrap(), + supply_addr.parse().unwrap(), + name_bytes, + total_supply, + ) + .await?; + SubcommandReturnValue::Empty + } + Command::TransferToken { + sender_addr, + recipient_addr, + balance_to_move, + } => { + wallet_core + .send_transfer_token_transaction( + sender_addr.parse().unwrap(), + recipient_addr.parse().unwrap(), + balance_to_move, + ) + .await?; + SubcommandReturnValue::Empty } Command::ClaimPinata { pinata_addr, @@ -308,8 +805,10 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { ) .await?; info!("Results of tx send is {res:#?}"); - } - } - Ok(()) + SubcommandReturnValue::Empty + } + }; + + Ok(subcommand_ret) } diff --git a/wallet/src/token_transfers/deshielded.rs b/wallet/src/token_transfers/deshielded.rs new file mode 100644 index 0000000..adf0614 --- /dev/null +++ b/wallet/src/token_transfers/deshielded.rs @@ -0,0 +1,102 @@ +use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse}; +use k256::elliptic_curve::rand_core::{OsRng, RngCore}; +use nssa::Address; +use nssa_core::{SharedSecretKey, encryption::EphemeralPublicKey}; + +use crate::WalletCore; + +impl WalletCore { + pub async fn send_deshielded_native_token_transfer( + &self, + from: Address, + to: Address, + balance_to_move: u128, + ) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { + let from_data = self.storage.user_data.get_private_account(&from).cloned(); + let to_data = self.get_account(to).await; + + let Some((from_keys, mut from_acc)) = from_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let Ok(to_acc) = to_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + if from_acc.balance >= balance_to_move { + let program = nssa::program::Program::authenticated_transfer_program(); + + from_acc.program_owner = program.id(); + + let sender_commitment = + nssa_core::Commitment::new(&from_keys.nullifer_public_key, &from_acc); + + let sender_pre = nssa_core::account::AccountWithMetadata { + account: from_acc.clone(), + is_authorized: true, + account_id: (&from_keys.nullifer_public_key).into(), + }; + let recipient_pre = nssa_core::account::AccountWithMetadata { + account: to_acc.clone(), + is_authorized: false, + account_id: (&to).into(), + }; + + //Move into different function + let mut esk = [0; 32]; + OsRng.fill_bytes(&mut esk); + let shared_secret = SharedSecretKey::new(&esk, &from_keys.incoming_viewing_public_key); + let epk = EphemeralPublicKey::from_scalar(esk); + + let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), + &[1, 0], + &[from_acc.nonce + 1], + &[(from_keys.nullifer_public_key.clone(), shared_secret.clone())], + &[( + from_keys.private_key_holder.nullifier_secret_key, + self.sequencer_client + .get_proof_for_commitment(sender_commitment) + .await + .unwrap() + .unwrap(), + )], + &program, + ) + .unwrap(); + + let message = + nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( + vec![to], + vec![], + vec![( + from_keys.nullifer_public_key.clone(), + from_keys.incoming_viewing_public_key.clone(), + epk, + )], + output, + ) + .unwrap(); + + let witness_set = + nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( + &message, + proof, + &[], + ); + + let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new( + message, + witness_set, + ); + + Ok(( + self.sequencer_client.send_tx_private(tx).await?, + shared_secret, + )) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } +} diff --git a/wallet/src/token_transfers/mod.rs b/wallet/src/token_transfers/mod.rs new file mode 100644 index 0000000..edfbe3e --- /dev/null +++ b/wallet/src/token_transfers/mod.rs @@ -0,0 +1,4 @@ +pub mod deshielded; +pub mod private; +pub mod public; +pub mod shielded; diff --git a/wallet/src/token_transfers/private.rs b/wallet/src/token_transfers/private.rs new file mode 100644 index 0000000..11ae6dc --- /dev/null +++ b/wallet/src/token_transfers/private.rs @@ -0,0 +1,234 @@ +use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse}; +use key_protocol::key_management::ephemeral_key_holder::EphemeralKeyHolder; +use nssa::Address; + +use crate::WalletCore; + +impl WalletCore { + pub async fn send_private_native_token_transfer_outer_account( + &self, + from: Address, + to_npk: nssa_core::NullifierPublicKey, + to_ipk: nssa_core::encryption::IncomingViewingPublicKey, + balance_to_move: u128, + ) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { + let from_data = self.storage.user_data.get_private_account(&from).cloned(); + + let Some((from_keys, mut from_acc)) = from_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let to_acc = nssa_core::account::Account::default(); + + if from_acc.balance >= balance_to_move { + let program = nssa::program::Program::authenticated_transfer_program(); + + from_acc.program_owner = program.id(); + + let sender_commitment = + nssa_core::Commitment::new(&from_keys.nullifer_public_key, &from_acc); + + let sender_pre = nssa_core::account::AccountWithMetadata { + account: from_acc.clone(), + is_authorized: true, + account_id: (&from_keys.nullifer_public_key).into(), + }; + + let recipient_pre = nssa_core::account::AccountWithMetadata { + account: to_acc.clone(), + is_authorized: false, + account_id: (&to_npk).into(), + }; + + let eph_holder = EphemeralKeyHolder::new( + to_npk.clone(), + from_keys.private_key_holder.outgoing_viewing_secret_key, + from_acc.nonce.try_into().unwrap(), + ); + + let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); + + let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), + &[1, 2], + &[from_acc.nonce + 1, to_acc.nonce + 1], + &[ + (from_keys.nullifer_public_key.clone(), shared_secret.clone()), + (to_npk.clone(), shared_secret.clone()), + ], + &[( + from_keys.private_key_holder.nullifier_secret_key, + self.sequencer_client + .get_proof_for_commitment(sender_commitment) + .await + .unwrap() + .unwrap(), + )], + &program, + ) + .unwrap(); + + let message = + nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( + vec![], + vec![], + vec![ + ( + from_keys.nullifer_public_key.clone(), + from_keys.incoming_viewing_public_key.clone(), + eph_holder.generate_ephemeral_public_key(), + ), + ( + to_npk.clone(), + to_ipk.clone(), + eph_holder.generate_ephemeral_public_key(), + ), + ], + output, + ) + .unwrap(); + + let witness_set = + nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( + &message, + proof, + &[], + ); + + let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new( + message, + witness_set, + ); + + Ok(( + self.sequencer_client.send_tx_private(tx).await?, + shared_secret, + )) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } + + pub async fn send_private_native_token_transfer( + &self, + from: Address, + to: Address, + balance_to_move: u128, + ) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { + let from_data = self.storage.user_data.get_private_account(&from).cloned(); + let to_data = self.storage.user_data.get_private_account(&to).cloned(); + + let Some((from_keys, mut from_acc)) = from_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let Some((to_keys, mut to_acc)) = to_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let to_npk = to_keys.nullifer_public_key.clone(); + let to_ipk = to_keys.incoming_viewing_public_key.clone(); + + if from_acc.balance >= balance_to_move { + let program = nssa::program::Program::authenticated_transfer_program(); + + from_acc.program_owner = program.id(); + to_acc.program_owner = program.id(); + + let sender_commitment = + nssa_core::Commitment::new(&from_keys.nullifer_public_key, &from_acc); + let receiver_commitment = + nssa_core::Commitment::new(&to_keys.nullifer_public_key, &to_acc); + + let sender_pre = nssa_core::account::AccountWithMetadata { + account: from_acc.clone(), + is_authorized: true, + account_id: (&from_keys.nullifer_public_key).into(), + }; + let recipient_pre = nssa_core::account::AccountWithMetadata { + account: to_acc.clone(), + is_authorized: true, + account_id: (&to_npk).into(), + }; + + let eph_holder = EphemeralKeyHolder::new( + to_npk.clone(), + from_keys.private_key_holder.outgoing_viewing_secret_key, + from_acc.nonce.try_into().unwrap(), + ); + + let shared_secret = eph_holder.calculate_shared_secret_sender(to_ipk.clone()); + + let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), + &[1, 1], + &[from_acc.nonce + 1, to_acc.nonce + 1], + &[ + (from_keys.nullifer_public_key.clone(), shared_secret.clone()), + (to_npk.clone(), shared_secret.clone()), + ], + &[ + ( + from_keys.private_key_holder.nullifier_secret_key, + self.sequencer_client + .get_proof_for_commitment(sender_commitment) + .await + .unwrap() + .unwrap(), + ), + ( + to_keys.private_key_holder.nullifier_secret_key, + self.sequencer_client + .get_proof_for_commitment(receiver_commitment) + .await + .unwrap() + .unwrap(), + ), + ], + &program, + ) + .unwrap(); + + let message = + nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( + vec![], + vec![], + vec![ + ( + from_keys.nullifer_public_key.clone(), + from_keys.incoming_viewing_public_key.clone(), + eph_holder.generate_ephemeral_public_key(), + ), + ( + to_npk.clone(), + to_ipk.clone(), + eph_holder.generate_ephemeral_public_key(), + ), + ], + output, + ) + .unwrap(); + + let witness_set = + nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( + &message, + proof, + &[], + ); + + let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new( + message, + witness_set, + ); + + Ok(( + self.sequencer_client.send_tx_private(tx).await?, + shared_secret, + )) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } +} diff --git a/wallet/src/token_transfers/public.rs b/wallet/src/token_transfers/public.rs new file mode 100644 index 0000000..7a5a78e --- /dev/null +++ b/wallet/src/token_transfers/public.rs @@ -0,0 +1,48 @@ +use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse}; +use nssa::Address; + +use crate::WalletCore; + +impl WalletCore { + pub async fn send_public_native_token_transfer( + &self, + from: Address, + to: Address, + balance_to_move: u128, + ) -> Result { + let Ok(balance) = self.get_account_balance(from).await else { + return Err(ExecutionFailureKind::SequencerError); + }; + + if balance >= balance_to_move { + let Ok(nonces) = self.get_accounts_nonces(vec![from]).await else { + return Err(ExecutionFailureKind::SequencerError); + }; + + let addresses = vec![from, to]; + let program_id = nssa::program::Program::authenticated_transfer_program().id(); + let message = nssa::public_transaction::Message::try_new( + program_id, + addresses, + nonces, + balance_to_move, + ) + .unwrap(); + + let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); + + let Some(signing_key) = signing_key else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let witness_set = + nssa::public_transaction::WitnessSet::for_message(&message, &[signing_key]); + + let tx = nssa::PublicTransaction::new(message, witness_set); + + Ok(self.sequencer_client.send_tx_public(tx).await?) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } +} diff --git a/wallet/src/token_transfers/shielded.rs b/wallet/src/token_transfers/shielded.rs new file mode 100644 index 0000000..859c465 --- /dev/null +++ b/wallet/src/token_transfers/shielded.rs @@ -0,0 +1,181 @@ +use common::{ExecutionFailureKind, sequencer_client::json::SendTxResponse}; +use key_protocol::key_management::ephemeral_key_holder::produce_one_sided_shared_secret_receiver; +use nssa::Address; + +use crate::WalletCore; + +impl WalletCore { + pub async fn send_shiedled_native_token_transfer( + &self, + from: Address, + to: Address, + balance_to_move: u128, + ) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { + let from_data = self.get_account(from).await; + let to_data = self.storage.user_data.get_private_account(&to).cloned(); + + let Ok(from_acc) = from_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let Some((to_keys, mut to_acc)) = to_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let to_npk = to_keys.nullifer_public_key.clone(); + let to_ipk = to_keys.incoming_viewing_public_key.clone(); + + if from_acc.balance >= balance_to_move { + let program = nssa::program::Program::authenticated_transfer_program(); + + to_acc.program_owner = program.id(); + + let receiver_commitment = + nssa_core::Commitment::new(&to_keys.nullifer_public_key, &to_acc); + + let sender_pre = nssa_core::account::AccountWithMetadata { + account: from_acc.clone(), + is_authorized: true, + account_id: (&from).into(), + }; + let recipient_pre = nssa_core::account::AccountWithMetadata { + account: to_acc.clone(), + is_authorized: true, + account_id: (&to_npk).into(), + }; + + let (shared_secret, epk) = produce_one_sided_shared_secret_receiver(&to_ipk); + + let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), + &[0, 1], + &[to_acc.nonce + 1], + &[(to_npk.clone(), shared_secret.clone())], + &[( + to_keys.private_key_holder.nullifier_secret_key, + self.sequencer_client + .get_proof_for_commitment(receiver_commitment) + .await + .unwrap() + .unwrap(), + )], + &program, + ) + .unwrap(); + + let message = + nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( + vec![from], + vec![from_acc.nonce], + vec![(to_npk.clone(), to_ipk.clone(), epk)], + output, + ) + .unwrap(); + + let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); + + let Some(signing_key) = signing_key else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let witness_set = + nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( + &message, + proof, + &[signing_key], + ); + + let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new( + message, + witness_set, + ); + + Ok(( + self.sequencer_client.send_tx_private(tx).await?, + shared_secret, + )) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } + + pub async fn send_shielded_native_token_transfer_maybe_outer_account( + &self, + from: Address, + to_npk: nssa_core::NullifierPublicKey, + to_ipk: nssa_core::encryption::IncomingViewingPublicKey, + balance_to_move: u128, + ) -> Result<(SendTxResponse, nssa_core::SharedSecretKey), ExecutionFailureKind> { + let from_data = self.get_account(from).await; + + let Ok(from_acc) = from_data else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let to_acc = nssa_core::account::Account::default(); + + if from_acc.balance >= balance_to_move { + let program = nssa::program::Program::authenticated_transfer_program(); + + let sender_pre = nssa_core::account::AccountWithMetadata { + account: from_acc.clone(), + is_authorized: true, + account_id: (&from).into(), + }; + + let recipient_pre = nssa_core::account::AccountWithMetadata { + account: to_acc.clone(), + is_authorized: false, + account_id: (&to_npk).into(), + }; + + let (shared_secret, epk) = produce_one_sided_shared_secret_receiver(&to_ipk); + + let (output, proof) = nssa::privacy_preserving_transaction::circuit::execute_and_prove( + &[sender_pre, recipient_pre], + &nssa::program::Program::serialize_instruction(balance_to_move).unwrap(), + &[0, 2], + &[to_acc.nonce + 1], + &[(to_npk.clone(), shared_secret.clone())], + &[], + &program, + ) + .unwrap(); + + let message = + nssa::privacy_preserving_transaction::message::Message::try_from_circuit_output( + vec![from], + vec![from_acc.nonce], + vec![(to_npk.clone(), to_ipk.clone(), epk)], + output, + ) + .unwrap(); + + let signing_key = self.storage.user_data.get_pub_account_signing_key(&from); + + let Some(signing_key) = signing_key else { + return Err(ExecutionFailureKind::KeyNotFoundError); + }; + + let witness_set = + nssa::privacy_preserving_transaction::witness_set::WitnessSet::for_message( + &message, + proof, + &[signing_key], + ); + + let tx = nssa::privacy_preserving_transaction::PrivacyPreservingTransaction::new( + message, + witness_set, + ); + + Ok(( + self.sequencer_client.send_tx_private(tx).await?, + shared_secret, + )) + } else { + Err(ExecutionFailureKind::InsufficientFundsError) + } + } +}