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 82f6736..9467631 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,9 +41,9 @@ 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" rocksdb = { version = "0.21.0", default-features = false, features = [ "snappy", diff --git a/ci_scripts/lint-ubuntu.sh b/ci_scripts/lint-ubuntu.sh index 35a0e4a..8c60825 100755 --- a/ci_scripts/lint-ubuntu.sh +++ b/ci_scripts/lint-ubuntu.sh @@ -6,5 +6,4 @@ cargo install taplo-cli --locked cargo fmt -- --check taplo fmt --check -export RISC0_SKIP_BUILD=1 -cargo clippy --workspace --all-targets -- -D warnings +RISC0_SKIP_BUILD=1 cargo clippy --workspace --all-targets -- -D warnings diff --git a/ci_scripts/test-ubuntu.sh b/ci_scripts/test-ubuntu.sh index 0055dad..5c19b36 100755 --- a/ci_scripts/test-ubuntu.sh +++ b/ci_scripts/test-ubuntu.sh @@ -10,6 +10,10 @@ 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 dcb5f60..0bb6491 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"] } [dependencies.secp256k1-zkp] 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 92bcd62..88595b7 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 fc3da12..6d64f98 100644 --- a/integration_tests/Cargo.toml +++ b/integration_tests/Cargo.toml @@ -15,6 +15,8 @@ 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 dea293e..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(); @@ -298,15 +310,15 @@ pub async fn test_success_token_program() { let wallet_config = fetch_config().unwrap(); // Create new account for the token definition - wallet::execute_subcommand(Command::RegisterAccount {}) + wallet::execute_subcommand(Command::RegisterAccountPublic {}) .await .unwrap(); // Create new account for the token supply holder - wallet::execute_subcommand(Command::RegisterAccount {}) + wallet::execute_subcommand(Command::RegisterAccountPublic {}) .await .unwrap(); // Create new account for receiving a token transaction - wallet::execute_subcommand(Command::RegisterAccount {}) + wallet::execute_subcommand(Command::RegisterAccountPublic {}) .await .unwrap(); @@ -315,10 +327,15 @@ pub async fn test_success_token_program() { let mut new_persistent_accounts_addr = Vec::new(); for per_acc in persistent_accounts { - if (per_acc.address.to_string() != ACC_RECEIVER) - && (per_acc.address.to_string() != ACC_SENDER) - { - new_persistent_accounts_addr.push(per_acc.address); + 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, } } @@ -433,7 +450,483 @@ pub async fn test_success_token_program() { ); } +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; @@ -518,6 +1011,48 @@ pub async fn main_tests_runner() -> Result<()> { "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); } @@ -527,8 +1062,35 @@ pub async fn main_tests_runner() -> Result<()> { 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 b4835ff..63ae2b3 100644 --- a/key_protocol/src/key_management/ephemeral_key_holder.rs +++ b/key_protocol/src/key_management/ephemeral_key_holder.rs @@ -3,6 +3,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; @@ -13,6 +14,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 5f5f2aa..4154095 100644 --- a/key_protocol/src/key_management/mod.rs +++ b/key_protocol/src/key_management/mod.rs @@ -22,6 +22,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/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 0c06830..200b3a4 100644 --- a/nssa/core/src/nullifier.rs +++ b/nssa/core/src/nullifier.rs @@ -5,7 +5,7 @@ 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 { diff --git a/nssa/program_methods/guest/Cargo.toml b/nssa/program_methods/guest/Cargo.toml index 59dac61..9e5f543 100644 --- a/nssa/program_methods/guest/Cargo.toml +++ b/nssa/program_methods/guest/Cargo.toml @@ -6,6 +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/src/lib.rs b/nssa/src/lib.rs index 0e33f50..31591ad 100644 --- a/nssa/src/lib.rs +++ b/nssa/src/lib.rs @@ -2,7 +2,7 @@ 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; 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 0a8e124..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, } diff --git a/nssa/src/public_transaction/transaction.rs b/nssa/src/public_transaction/transaction.rs index e5c8b5a..8af9212 100644 --- a/nssa/src/public_transaction/transaction.rs +++ b/nssa/src/public_transaction/transaction.rs @@ -141,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 d978c8e..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,9 +82,12 @@ 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(), }; @@ -301,7 +307,7 @@ pub mod tests { 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); @@ -309,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)); @@ -324,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); @@ -335,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); @@ -345,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(); @@ -357,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()); @@ -377,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]); @@ -401,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; @@ -424,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; @@ -508,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 = @@ -524,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 = @@ -540,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 = @@ -556,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 @@ -578,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]); @@ -602,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]); @@ -626,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]); @@ -650,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; @@ -677,7 +688,7 @@ 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) + 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]); @@ -701,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(); @@ -718,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(); @@ -942,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; @@ -988,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; @@ -1053,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; 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 3330858..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,8 +1824,6 @@ name = "programs" version = "0.1.0" dependencies = [ "nssa-core", - "risc0-zkvm", - "serde", ] [[package]] @@ -1826,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", @@ -1854,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", @@ -1870,7 +1876,7 @@ dependencies = [ "quinn-udp", "rustc-hash", "rustls", - "socket2 0.5.10", + "socket2", "thiserror", "tokio", "tracing", @@ -1879,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", @@ -1900,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]] @@ -2022,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", @@ -2039,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", @@ -2050,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", @@ -2390,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]] @@ -2427,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", @@ -2438,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" @@ -2517,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", @@ -2563,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", @@ -2582,7 +2588,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2614,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" @@ -2624,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" @@ -2667,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]] @@ -2700,7 +2696,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -2722,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", @@ -2748,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", @@ -2800,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", @@ -2826,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", ] @@ -2852,7 +2848,7 @@ dependencies = [ "mio", "pin-project-lite", "slab", - "socket2 0.6.0", + "socket2", "windows-sys 0.59.0", ] @@ -2906,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", @@ -2941,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", @@ -2985,7 +2981,7 @@ checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3045,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]] @@ -3098,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", @@ -3137,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", @@ -3147,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", ] @@ -3182,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", @@ -3211,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", ] @@ -3230,7 +3228,7 @@ checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3241,7 +3239,7 @@ checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3251,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]] @@ -3295,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" @@ -3317,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", @@ -3426,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", ] @@ -3439,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]] @@ -3450,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", @@ -3485,7 +3498,7 @@ checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -3506,7 +3519,7 @@ checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3526,7 +3539,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", "synstructure", ] @@ -3547,7 +3560,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.104", + "syn 2.0.106", ] [[package]] @@ -3580,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 7972342..64c69af 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 00c5724..4ebb293 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 e37586b..5884ffa 100644 --- a/wallet/Cargo.toml +++ b/wallet/Cargo.toml @@ -14,7 +14,9 @@ tempfile.workspace = true clap.workspace = true nssa-core = { path = "../nssa/core" } base64.workspace = true +k256 = { version = "0.13.3" } bytemuck = "1.23.2" +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,6 @@ impl WalletCore { Ok(self.sequencer_client.send_tx_public(tx).await?) } - 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) - } - } - pub async fn send_new_token_definition( &self, definition_address: Address, @@ -229,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 = EncodedTransaction::from_bytes(tx_base64_decode); @@ -243,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, @@ -254,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)] @@ -320,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)?; @@ -335,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 @@ -354,23 +729,31 @@ 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, @@ -393,6 +776,7 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { total_supply, ) .await?; + SubcommandReturnValue::Empty } Command::TransferToken { sender_addr, @@ -406,6 +790,7 @@ pub async fn execute_subcommand(command: Command) -> Result<()> { balance_to_move, ) .await?; + SubcommandReturnValue::Empty } Command::ClaimPinata { pinata_addr, @@ -420,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) + } + } +}