From 77fd18901097ce8972dec8c2f5361aac17ba6226 Mon Sep 17 00:00:00 2001 From: Sergio Chouhy Date: Fri, 12 Dec 2025 14:20:58 -0300 Subject: [PATCH] refactor to use privacy tail calls from send_privacy_preserving_tx and add the example runner --- examples/program_deployment/README.md | 24 ++++++- .../src/bin/run_hello_world_private.rs | 2 +- ...n_hello_world_through_tail_call_private.rs | 71 +++++++++++++++++++ .../bin/run_hello_world_with_move_function.rs | 4 +- wallet/src/lib.rs | 11 +-- .../native_token_transfer/deshielded.rs | 2 +- .../native_token_transfer/private.rs | 6 +- .../native_token_transfer/shielded.rs | 4 +- wallet/src/program_facades/pinata.rs | 2 +- wallet/src/program_facades/token.rs | 16 ++--- 10 files changed, 119 insertions(+), 23 deletions(-) create mode 100644 examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs diff --git a/examples/program_deployment/README.md b/examples/program_deployment/README.md index 6325d2d..14a2bab 100644 --- a/examples/program_deployment/README.md +++ b/examples/program_deployment/README.md @@ -340,7 +340,7 @@ Luckily all that complexity is hidden behind the `wallet_core.send_privacy_prese .send_privacy_preserving_tx( accounts, &Program::serialize_instruction(greeting).unwrap(), - &program, + &program.into(), ) .await .unwrap(); @@ -568,6 +568,28 @@ Output: ``` Hola mundo!Hello from tail call ``` +## Private tail-calls +There's support for tail calls in privacy preserving executions too. The `run_hello_world_through_tail_call_private.rs` runner walks you through the process of invoking such an execution. +The only difference is that, since the execution is local, the runner will need both programs: the `simple_tail_call` and it's dependency `hello_world`. + +Let's use our existing private account with id `8vzkK7vsdrS2gdPhLk72La8X4FJkgJ5kJLUBRbEVkReU`. This one is already owned by the `hello_world` program. + +You can test the privacy tail calls with +```bash +cargo run --bin run_hello_world_through_tail_call_private \ + $EXAMPLE_PROGRAMS_BUILD_DIR/simple_tail_call.bin \ + $EXAMPLE_PROGRAMS_BUILD_DIR/hello_world.bin \ + 8vzkK7vsdrS2gdPhLk72La8X4FJkgJ5kJLUBRbEVkReU +``` + +>[!NOTE] +> The above command may take longer than the previous privacy executions because needs to generate proofs of execution of both the `simple_tail_call` and the `hello_world` programs. + +Once finished run the following to see the changes +```bash +wallet account sync-private +wallet account get --account-id Private/8vzkK7vsdrS2gdPhLk72La8X4FJkgJ5kJLUBRbEVkReU +``` # 13. Program derived accounts: authorizing accounts through tail calls diff --git a/examples/program_deployment/src/bin/run_hello_world_private.rs b/examples/program_deployment/src/bin/run_hello_world_private.rs index be4280b..dcbe59a 100644 --- a/examples/program_deployment/src/bin/run_hello_world_private.rs +++ b/examples/program_deployment/src/bin/run_hello_world_private.rs @@ -54,7 +54,7 @@ async fn main() { .send_privacy_preserving_tx( accounts, &Program::serialize_instruction(greeting).unwrap(), - &program, + &program.into(), ) .await .unwrap(); diff --git a/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs new file mode 100644 index 0000000..e62cfef --- /dev/null +++ b/examples/program_deployment/src/bin/run_hello_world_through_tail_call_private.rs @@ -0,0 +1,71 @@ +use std::collections::HashMap; + +use nssa::{ + AccountId, ProgramId, PublicTransaction, + privacy_preserving_transaction::circuit::ProgramWithDependencies, + program::Program, + public_transaction::{Message, WitnessSet}, +}; +use wallet::{PrivacyPreservingAccount, WalletCore, helperfunctions::fetch_config}; + +// Before running this example, compile the `simple_tail_call.rs` guest program with: +// +// cargo risczero build --manifest-path examples/program_deployment/methods/guest/Cargo.toml +// +// Note: you must run the above command from the root of the `lssa` repository. +// Note: The compiled binary file is stored in +// methods/guest/target/riscv32im-risc0-zkvm-elf/docker/simple_tail_call.bin +// +// +// Usage: +// cargo run --bin run_hello_world_through_tail_call_private /path/to/guest/binary +// +// Example: +// cargo run --bin run_hello_world_through_tail_call \ +// methods/guest/target/riscv32im-risc0-zkvm-elf/docker/simple_tail_call.bin \ +// Ds8q5PjLcKwwV97Zi7duhRVF9uwA2PuYMoLL7FwCzsXE + +#[tokio::main] +async fn main() { + // Load wallet config and storage + let wallet_config = fetch_config().await.unwrap(); + let wallet_core = WalletCore::start_from_config_update_chain(wallet_config) + .await + .unwrap(); + + // Parse arguments + // First argument is the path to the simple_tail_call program binary + let simple_tail_call_path = std::env::args_os().nth(1).unwrap().into_string().unwrap(); + // Second argument is the path to the hello_world program binary + let hello_world_path = std::env::args_os().nth(2).unwrap().into_string().unwrap(); + // Third argument is the account_id + let account_id: AccountId = std::env::args_os() + .nth(3) + .unwrap() + .into_string() + .unwrap() + .parse() + .unwrap(); + + // Load the program and its dependencies (the hellow world program) + let simple_tail_call_bytecode: Vec = std::fs::read(simple_tail_call_path).unwrap(); + let simple_tail_call = Program::new(simple_tail_call_bytecode).unwrap(); + let hello_world_bytecode: Vec = std::fs::read(hello_world_path).unwrap(); + let hello_world = Program::new(hello_world_bytecode).unwrap(); + let dependencies: HashMap = + [(hello_world.id(), hello_world)].into_iter().collect(); + let program_with_dependencies = ProgramWithDependencies::new(simple_tail_call, dependencies); + + let accounts = vec![PrivacyPreservingAccount::PrivateOwned(account_id)]; + + // Construct and submit the privacy-preserving transaction + let instruction = (); + wallet_core + .send_privacy_preserving_tx( + accounts, + &Program::serialize_instruction(instruction).unwrap(), + &program_with_dependencies, + ) + .await + .unwrap(); +} diff --git a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs index 77c2597..4307315 100644 --- a/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs +++ b/examples/program_deployment/src/bin/run_hello_world_with_move_function.rs @@ -105,7 +105,7 @@ async fn main() { .send_privacy_preserving_tx( accounts, &Program::serialize_instruction(instruction).unwrap(), - &program, + &program.into(), ) .await .unwrap(); @@ -146,7 +146,7 @@ async fn main() { .send_privacy_preserving_tx( accounts, &Program::serialize_instruction(instruction).unwrap(), - &program, + &program.into(), ) .await .unwrap(); diff --git a/wallet/src/lib.rs b/wallet/src/lib.rs index bc28311..25c098f 100644 --- a/wallet/src/lib.rs +++ b/wallet/src/lib.rs @@ -14,7 +14,10 @@ use key_protocol::key_management::key_tree::{chain_index::ChainIndex, traits::Ke use log::info; use nssa::{ Account, AccountId, PrivacyPreservingTransaction, - privacy_preserving_transaction::message::EncryptedAccountData, program::Program, + privacy_preserving_transaction::{ + circuit::ProgramWithDependencies, message::EncryptedAccountData, + }, + program::Program, }; use nssa_core::{Commitment, MembershipProof, SharedSecretKey, program::InstructionData}; pub use privacy_preserving_tx::PrivacyPreservingAccount; @@ -247,7 +250,7 @@ impl WalletCore { &self, accounts: Vec, instruction_data: &InstructionData, - program: &Program, + program: &ProgramWithDependencies, ) -> Result<(SendTxResponse, Vec), ExecutionFailureKind> { self.send_privacy_preserving_tx_with_pre_check(accounts, instruction_data, program, |_| { Ok(()) @@ -259,7 +262,7 @@ impl WalletCore { &self, accounts: Vec, instruction_data: &InstructionData, - program: &Program, + program: &ProgramWithDependencies, tx_pre_check: impl FnOnce(&[&Account]) -> Result<(), ExecutionFailureKind>, ) -> Result<(SendTxResponse, Vec), ExecutionFailureKind> { let acc_manager = privacy_preserving_tx::AccountManager::new(self, accounts).await?; @@ -283,7 +286,7 @@ impl WalletCore { .map(|keys| (keys.npk.clone(), keys.ssk.clone())) .collect::>(), &acc_manager.private_account_auth(), - &program.to_owned().into(), + program, ) .unwrap(); diff --git a/wallet/src/program_facades/native_token_transfer/deshielded.rs b/wallet/src/program_facades/native_token_transfer/deshielded.rs index 35a13ba..df604c6 100644 --- a/wallet/src/program_facades/native_token_transfer/deshielded.rs +++ b/wallet/src/program_facades/native_token_transfer/deshielded.rs @@ -20,7 +20,7 @@ impl NativeTokenTransfer<'_> { PrivacyPreservingAccount::Public(to), ], &instruction_data, - &program, + &program.into(), tx_pre_check, ) .await diff --git a/wallet/src/program_facades/native_token_transfer/private.rs b/wallet/src/program_facades/native_token_transfer/private.rs index 320027b..0baeeac 100644 --- a/wallet/src/program_facades/native_token_transfer/private.rs +++ b/wallet/src/program_facades/native_token_transfer/private.rs @@ -18,7 +18,7 @@ impl NativeTokenTransfer<'_> { .send_privacy_preserving_tx_with_pre_check( vec![PrivacyPreservingAccount::PrivateOwned(from)], &Program::serialize_instruction(instruction).unwrap(), - &Program::authenticated_transfer_program(), + &Program::authenticated_transfer_program().into(), |_| Ok(()), ) .await @@ -48,7 +48,7 @@ impl NativeTokenTransfer<'_> { }, ], &instruction_data, - &program, + &program.into(), tx_pre_check, ) .await @@ -75,7 +75,7 @@ impl NativeTokenTransfer<'_> { PrivacyPreservingAccount::PrivateOwned(to), ], &instruction_data, - &program, + &program.into(), tx_pre_check, ) .await diff --git a/wallet/src/program_facades/native_token_transfer/shielded.rs b/wallet/src/program_facades/native_token_transfer/shielded.rs index 0802d6e..6abd2d2 100644 --- a/wallet/src/program_facades/native_token_transfer/shielded.rs +++ b/wallet/src/program_facades/native_token_transfer/shielded.rs @@ -21,7 +21,7 @@ impl NativeTokenTransfer<'_> { PrivacyPreservingAccount::PrivateOwned(to), ], &instruction_data, - &program, + &program.into(), tx_pre_check, ) .await @@ -53,7 +53,7 @@ impl NativeTokenTransfer<'_> { }, ], &instruction_data, - &program, + &program.into(), tx_pre_check, ) .await diff --git a/wallet/src/program_facades/pinata.rs b/wallet/src/program_facades/pinata.rs index 41e7510..fdd5d70 100644 --- a/wallet/src/program_facades/pinata.rs +++ b/wallet/src/program_facades/pinata.rs @@ -38,7 +38,7 @@ impl Pinata<'_> { PrivacyPreservingAccount::PrivateOwned(winner_account_id), ], &nssa::program::Program::serialize_instruction(solution).unwrap(), - &nssa::program::Program::pinata(), + &nssa::program::Program::pinata().into(), ) .await .map(|(resp, secrets)| { diff --git a/wallet/src/program_facades/token.rs b/wallet/src/program_facades/token.rs index 4ec9c12..e7bdca9 100644 --- a/wallet/src/program_facades/token.rs +++ b/wallet/src/program_facades/token.rs @@ -54,7 +54,7 @@ impl Token<'_> { PrivacyPreservingAccount::PrivateOwned(supply_account_id), ], &instruction_data, - &program, + &program.into(), ) .await .map(|(resp, secrets)| { @@ -82,7 +82,7 @@ impl Token<'_> { PrivacyPreservingAccount::Public(supply_account_id), ], &instruction_data, - &program, + &program.into(), ) .await .map(|(resp, secrets)| { @@ -110,7 +110,7 @@ impl Token<'_> { PrivacyPreservingAccount::PrivateOwned(supply_account_id), ], &instruction_data, - &program, + &program.into(), ) .await .map(|(resp, secrets)| { @@ -176,7 +176,7 @@ impl Token<'_> { PrivacyPreservingAccount::PrivateOwned(recipient_account_id), ], &instruction_data, - &program, + &program.into(), ) .await .map(|(resp, secrets)| { @@ -206,7 +206,7 @@ impl Token<'_> { }, ], &instruction_data, - &program, + &program.into(), ) .await .map(|(resp, secrets)| { @@ -232,7 +232,7 @@ impl Token<'_> { PrivacyPreservingAccount::Public(recipient_account_id), ], &instruction_data, - &program, + &program.into(), ) .await .map(|(resp, secrets)| { @@ -259,7 +259,7 @@ impl Token<'_> { PrivacyPreservingAccount::PrivateOwned(recipient_account_id), ], &instruction_data, - &program, + &program.into(), ) .await .map(|(resp, secrets)| { @@ -290,7 +290,7 @@ impl Token<'_> { }, ], &instruction_data, - &program, + &program.into(), ) .await .map(|(resp, secrets)| {