From e02c7b905b75fb30c81818a4f0aa306fa81e7d2d Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Tue, 8 Jul 2025 14:56:09 +0200 Subject: [PATCH 1/7] Update to v4.0.0 --- Cargo.toml | 6 +++--- block-parser/src/decoders.rs | 4 ++-- client/Cargo.toml | 1 + execute-service/src/process_variant.rs | 4 ++-- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 1bbd76b..fa662d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,9 +11,9 @@ members = [ [workspace.dependencies] [workspace.dependencies.snarkvm] -#git = "https://github.com/ProvableHQ/snarkVM.git" -#rev = "57bfe32" -version = "=3.8.0" +git = "https://github.com/ProvableHQ/snarkVM.git" +rev = "c1ea0705c" +#version = "=3.8.0" #snarkvm = { path = "../snarkVM" } [profile.release] diff --git a/block-parser/src/decoders.rs b/block-parser/src/decoders.rs index ae8b14b..82662ae 100644 --- a/block-parser/src/decoders.rs +++ b/block-parser/src/decoders.rs @@ -146,7 +146,7 @@ pub fn decode_block_unchecked(string: &str) -> Result<(Vec *U64::::from_str(&v)?, + Some(v) => *U64::::from_str(v)?, None => bail!("Invalid JSON object"), }; // Add the `bond_public` operation to the credits transactions. @@ -187,7 +187,7 @@ pub fn decode_block_unchecked(string: &str) -> Result<(Vec *U64::::from_str(&v)?, + Some(v) => *U64::::from_str(v)?, None => bail!("Invalid JSON object"), }; // Add the `unbond_public` operation to the credits transactions. diff --git a/client/Cargo.toml b/client/Cargo.toml index e2d4456..0e07705 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -24,6 +24,7 @@ features = [ "full" ] [dependencies.reqwest] version = "0.11.22" +features = [ "json" ] [dependencies.warp] version = "0.3.6" diff --git a/execute-service/src/process_variant.rs b/execute-service/src/process_variant.rs index 810d83e..d1a175c 100644 --- a/execute-service/src/process_variant.rs +++ b/execute-service/src/process_variant.rs @@ -74,7 +74,7 @@ impl ProcessVariant { let (_, mut trace) = process.execute::(function_authorization, rng)?; // Prepare the trace. - trace.prepare(query.clone())?; + trace.prepare(&query.clone())?; // Compute the proof and construct the execution. let execution = trace.prove_execution::(&locator, VarunaVersion::V2, rng)?; @@ -83,7 +83,7 @@ impl ProcessVariant { let (_, mut trace) = process.execute::(fee_authorization, rng)?; // Prepare the trace. - trace.prepare(query)?; + trace.prepare(&query)?; // Compute the proof and construct the fee. let fee = trace.prove_fee::(VarunaVersion::V2, rng)?; From 754290bde80f76d101326fee8265d3677450aa04 Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Tue, 8 Jul 2025 14:58:24 +0200 Subject: [PATCH 2/7] Update snarkVM rev --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index fa662d3..1590b69 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ members = [ [workspace.dependencies.snarkvm] git = "https://github.com/ProvableHQ/snarkVM.git" -rev = "c1ea0705c" +rev = "668b72b" #version = "=3.8.0" #snarkvm = { path = "../snarkVM" } From 82c656c5b91e1d217534f8b853522320429c424b Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Tue, 8 Jul 2025 15:20:30 +0200 Subject: [PATCH 3/7] Use snarkVM rev with authorize_request support --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 1590b69..fa662d3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,7 @@ members = [ [workspace.dependencies.snarkvm] git = "https://github.com/ProvableHQ/snarkVM.git" -rev = "668b72b" +rev = "c1ea0705c" #version = "=3.8.0" #snarkvm = { path = "../snarkVM" } From 2a66f6ca14bb6325f85dee2dae3b5b8c37b65eee Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Tue, 8 Jul 2025 15:20:40 +0200 Subject: [PATCH 4/7] Fix clippy --- block-parser/src/block_json/input.rs | 5 +---- block-parser/src/lib.rs | 8 ++++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/block-parser/src/block_json/input.rs b/block-parser/src/block_json/input.rs index 4dc03eb..9ab6fc0 100644 --- a/block-parser/src/block_json/input.rs +++ b/block-parser/src/block_json/input.rs @@ -29,10 +29,7 @@ impl InputJSON { None => bail!("Invalid input ID"), }; // Get the value of the input. - let value = match json.get("value").and_then(|v| v.as_str()) { - Some(value) => Some(value.to_string()), - None => None, - }; + let value = json.get("value").and_then(|v| v.as_str()).map(|v|v.to_string()); Ok(Self { type_, id, value }) } diff --git a/block-parser/src/lib.rs b/block-parser/src/lib.rs index f754f4c..92e9744 100644 --- a/block-parser/src/lib.rs +++ b/block-parser/src/lib.rs @@ -130,12 +130,12 @@ pub fn process_block_transactions( #[cfg(test)] mod tests { - use super::*; - use snarkvm::prelude::{CanaryV0, TestnetV0}; + // use super::*; + // use snarkvm::prelude::{CanaryV0}; - use std::fs::File; + // use std::fs::File; - type CurrentNetwork = CanaryV0; + // type CurrentNetwork = CanaryV0; // #[test] // fn test_dont_add_bonded_map() { From 4f6987b229227d59cf66c47fcac3e84b222d04b5 Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Tue, 8 Jul 2025 15:43:24 +0200 Subject: [PATCH 5/7] Add support for authorizing signed requests --- authorize-service/src/authorize.rs | 25 +++ authorize-service/src/process_variant.rs | 41 +++++ authorize-service/src/request.rs | 6 + authorize-service/src/response.rs | 6 + authorize-service/src/routes.rs | 17 ++ client/Cargo.toml | 4 + client/src/main.rs | 213 ++++++++++++++++++++++- 7 files changed, 308 insertions(+), 4 deletions(-) diff --git a/authorize-service/src/authorize.rs b/authorize-service/src/authorize.rs index 5473e37..9765927 100644 --- a/authorize-service/src/authorize.rs +++ b/authorize-service/src/authorize.rs @@ -45,3 +45,28 @@ pub fn authorize(bytes: Bytes) -> Result { process.borrow().as_ref().unwrap().authorize(&bytes) }) } + +pub fn authorize_signed(bytes: Bytes) -> Result { + PROCESS.with(|process| { + // Initialize the process if it is not already initialized. + if process.borrow().is_none() { + *process.borrow_mut() = match N::ID { + MainnetV0::ID => { + println!("Loading mainnet process..."); + Some(ProcessVariant::MainnetV0(Process::load()?)) + } + TestnetV0::ID => { + println!("Loading testnet process..."); + Some(ProcessVariant::TestnetV0(Process::load()?)) + } + CanaryV0::ID => { + println!("Loading canary process..."); + Some(ProcessVariant::CanaryV0(Process::load()?)) + } + _ => panic!("Invalid network"), + }; + }; + // Compute the `Authorization`. + process.borrow().as_ref().unwrap().authorize_request(&bytes) + }) +} diff --git a/authorize-service/src/process_variant.rs b/authorize-service/src/process_variant.rs index be4c3d0..5c52a8d 100644 --- a/authorize-service/src/process_variant.rs +++ b/authorize-service/src/process_variant.rs @@ -78,3 +78,44 @@ impl ProcessVariant { Ok(serde_json::to_value(response)?) } } + +impl ProcessVariant { + pub fn authorize_request(&self, bytes: &[u8]) -> Result { + match self { + ProcessVariant::MainnetV0(process) => { + Self::handle_authorize_request::(process, bytes) + } + ProcessVariant::TestnetV0(process) => { + Self::handle_authorize_request::(process, bytes) + } + ProcessVariant::CanaryV0(process) => { + Self::handle_authorize_request::(process, bytes) + } + } + } + + fn handle_authorize_request, N: Network>( + process: &Process, + bytes: &[u8], + ) -> Result { + // Deserialize the request. + let request = serde_json::from_slice::>(bytes)?; + + // Initialize the RNG. + let rng = &mut rand_chacha::ChaCha20Rng::from_entropy(); + + // Authorize the function. + let authorization = process.authorize_request::( + request.request, + rng, + )?; + + // Construct the response. + let response = AuthorizeSignedResponse:: { + authorization, + }; + + // Return the response as JSON. + Ok(serde_json::to_value(response)?) + } +} diff --git a/authorize-service/src/request.rs b/authorize-service/src/request.rs index f6ec532..24207d8 100644 --- a/authorize-service/src/request.rs +++ b/authorize-service/src/request.rs @@ -34,6 +34,12 @@ pub struct AuthorizeRequest { pub priority_fee_in_microcredits: U64, } +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AuthorizeSignedRequest { + #[serde(bound(deserialize = ""))] + pub request: snarkvm::prelude::Request, +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct SignRequest { #[serde(bound(deserialize = ""))] diff --git a/authorize-service/src/response.rs b/authorize-service/src/response.rs index 04f622e..defd82a 100644 --- a/authorize-service/src/response.rs +++ b/authorize-service/src/response.rs @@ -30,6 +30,12 @@ pub struct AuthorizeResponse { pub fee_authorization: Authorization, } +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct AuthorizeSignedResponse { + #[serde(bound(deserialize = ""))] + pub authorization: Authorization, +} + #[derive(Clone, Debug, Serialize, Deserialize)] pub struct SignResponse { pub signed_message: Vec, diff --git a/authorize-service/src/routes.rs b/authorize-service/src/routes.rs index e7835b9..a54ca6a 100644 --- a/authorize-service/src/routes.rs +++ b/authorize-service/src/routes.rs @@ -50,6 +50,23 @@ pub fn authorize_route() -> impl Filter() -> impl Filter + Clone +{ + warp::post() + .and(warp::path("authorize_signed")) + .and(warp::path::end()) + .and(warp::body::content_length_limit(32 * 1024)) // 32 KiB + .and(warp::body::bytes()) + .and_then(|bytes: Bytes| async move { + let response = match tokio_rayon::spawn_fifo(|| authorize_signed::(bytes)).await { + Ok(response) => response, + Err(_) => return Err(warp::reject()), + }; + Ok(warp::reply::json(&response)) + }) +} + // POST /sign pub fn sign_route() -> impl Filter + Clone { warp::post() diff --git a/client/Cargo.toml b/client/Cargo.toml index 0e07705..dbfd7f5 100644 --- a/client/Cargo.toml +++ b/client/Cargo.toml @@ -12,6 +12,10 @@ path = "../execute-service" [dependencies.anyhow] version = "1.0.75" +[dependencies.clap] +version = "4.3" +features = ["derive"] + [dependencies.rand_chacha] version = "0.3.1" diff --git a/client/src/main.rs b/client/src/main.rs index d735135..2fe9924 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -16,19 +16,21 @@ use snarkvm::ledger::block::Transaction; use snarkvm::prelude::{ - Field, FromBytes, Identifier, Network, PrivateKey, ProgramID, ToBytes, Uniform, Value, U64, + Field, FromBytes, Identifier, Network, PrivateKey, ProgramID, ToBytes, Uniform, Value, U64, Request, ValueType, Literal, }; use authorize_service::*; use execute_service::*; use anyhow::{bail, Result}; +use clap::Parser; use rand_chacha::rand_core::SeedableRng; use reqwest::Client; use std::str::FromStr; const KEYGEN_URL: &str = "http://localhost:8080/keygen"; const AUTHORIZE_URL: &str = "http://localhost:8080/authorize"; +const AUTHORIZE_SIGNED_URL: &str = "http://localhost:8080/authorize_signed"; const EXECUTE_URL: &str = "http://localhost:8081/execute"; const BROADCAST_URL: &str = "http://localhost:3033/canary/transaction/broadcast"; @@ -38,8 +40,19 @@ const DEVNET_PRIVATE_KEY: &str = "APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKU type CurrentNetwork = snarkvm::prelude::CanaryV0; +/// Command-line arguments parser +#[derive(Parser)] +struct Args { + /// Determine whether or not to let the client generate the request. + #[arg(long, default_value = "false")] + generate_requests: bool, +} + #[tokio::main] async fn main() -> Result<()> { + // Parse command-line arguments + let args = Args::parse(); + // Create a `Client` instance. let client = Client::new(); @@ -79,12 +92,49 @@ async fn main() -> Result<()> { // Construct the priority fee. let priority_fee_in_microcredits = U64::new(10); + // Determine whether the authorization service will sign the request. + if !args.generate_requests { + sign_authorize_execute( + &client, + private_key, + recipient, + amount_in_microcredits, + base_fee_in_microcredits, + priority_fee_in_microcredits, + ).await + } else { + authorize_execute( + &client, + private_key, + recipient, + amount_in_microcredits, + base_fee_in_microcredits, + priority_fee_in_microcredits, + rng, + ).await + } +} + +/// Sends an `AuthorizeRequest` to the authorization service and executes the transaction. +async fn sign_authorize_execute( + client: &Client, + private_key: PrivateKey, + recipient: Value, + amount_in_microcredits: Value, + base_fee_in_microcredits: U64, + priority_fee_in_microcredits: U64, +) -> Result<()> { + // Construct the inputs for the request. + let program_id = ProgramID::from_str("credits.aleo")?; + let function_name = Identifier::from_str("transfer_public")?; + let inputs = vec![recipient, amount_in_microcredits]; + // Construct an `AuthorizeRequest`. let authorize_request = AuthorizeRequest:: { private_key, - program_id: ProgramID::from_str("credits.aleo")?, - function_name: Identifier::from_str("transfer_public")?, - inputs: vec![recipient, amount_in_microcredits], + program_id, + function_name, + inputs, base_fee_in_microcredits, priority_fee_in_microcredits, }; @@ -174,3 +224,158 @@ async fn main() -> Result<()> { Ok(()) } + +/// Sends an `AuthorizeSignedRequest` to the authorization service and executes the transaction. +async fn authorize_execute( + client: &Client, + private_key: PrivateKey, + recipient: Value, + amount_in_microcredits: Value, + base_fee_in_microcredits: U64, + priority_fee_in_microcredits: U64, + rng: &mut rand_chacha::ChaCha20Rng, +) -> Result<()> { + // Construct the inputs for the request. + let program_id = ProgramID::from_str("credits.aleo")?; + let function_name = Identifier::from_str("transfer_public")?; + let is_root = true; + let root_tvk = None; + let inputs = vec![ + recipient, + amount_in_microcredits, + ]; + let input_types = [ValueType::from_str("address.public").unwrap(), ValueType::from_str("u64.public").unwrap()]; + // Compute the request. + let request = Request::sign(&private_key, program_id, function_name, inputs.into_iter(), &input_types, root_tvk, is_root, rng)?; + + // Construct an `AuthorizeSignRequest`. + let authorize_request = AuthorizeSignedRequest:: { + request, + }; + + // Send the request. + let response = client + .post(AUTHORIZE_SIGNED_URL) + .json(&authorize_request) + .send() + .await?; + + // If the request was successful, deserialize the response as an `AuthorizeSignedResponse`. + let authorize_response = match response.status().is_success() { + true => response.json::>().await?, + false => bail!( + "Authorization request failed with status: {}", + response.status() + ), + }; + + let execution_id = authorize_response.authorization.to_execution_id()?; + + // Construct the inputs for the fee request. + let program_id = ProgramID::from_str("credits.aleo")?; + let function_name = Identifier::from_str("fee_public")?; + let is_root = true; + let root_tvk = None; + let inputs = vec![ + Value::from(Literal::U64(base_fee_in_microcredits)), + Value::from(Literal::U64(priority_fee_in_microcredits)), + Value::from(Literal::Field(execution_id)), + ]; + let input_types = [ + ValueType::from_str("u64.public").unwrap(), + ValueType::from_str("u64.public").unwrap(), + ValueType::from_str("field.public").unwrap(), + ]; + // Compute the request. + let request = Request::sign(&private_key, program_id, function_name, inputs.into_iter(), &input_types, root_tvk, is_root, rng)?; + + // Construct an `AuthorizeSignRequest`. + let authorize_request = AuthorizeSignedRequest:: { + request, + }; + + // Send the request. + let response = client + .post(AUTHORIZE_SIGNED_URL) + .json(&authorize_request) + .send() + .await?; + + // If the request was successful, deserialize the response as an `AuthorizeSignedResponse`. + let authorize_fee_response = match response.status().is_success() { + true => response.json::>().await?, + false => bail!( + "Authorization request failed with status: {}", + response.status() + ), + }; + + // Get the latest state root. + let response = client.get(STATE_ROOT_URL).send().await?; + + // If the request was successful, deserialize the response JSON as a `StateRoot`. + let state_root = match response.status().is_success() { + true => { + response + .json::<::StateRoot>() + .await? + } + false => bail!( + "State root request failed with status: {}", + response.status() + ), + }; + + println!("Using state root: {}", state_root); + + // Construct an `ExecuteRequest`. + let execute_request = ExecuteRequest:: { + function_authorization: authorize_response.authorization, + fee_authorization: authorize_fee_response.authorization, + state_root: Some(state_root), + state_path: None, + }; + + // Send the request. + let response = client + .post(EXECUTE_URL) + .body(execute_request.to_bytes_le()?) + .header("Content-Type", "application/octet-stream") + .send() + .await?; + + // If the request was successful, deserialize the response archive as a `Transaction`. + let transaction = match response.status().is_success() { + true => { + let bytes = response.bytes().await?; + Transaction::::from_bytes_le(&bytes)? + } + false => bail!( + "Execution request failed with status: {}", + response.status() + ), + }; + + // Send the transaction as a broadcast request as JSON. + let response = client.post(BROADCAST_URL).json(&transaction).send().await?; + + // If the request was successful, print the response and the response body. + match response.status().is_success() { + true => { + println!( + "Broadcast request succeeded with status: {}", + response.status() + ); + println!( + "Broadcast request response body: {}", + response.text().await? + ); + } + false => bail!( + "Broadcast request failed with status: {}", + response.status() + ), + } + + Ok(()) +} From 82681dc8ecfc37018c81c1644a8186e26263d6d8 Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Tue, 8 Jul 2025 16:32:10 +0200 Subject: [PATCH 6/7] Add support for authorizing request signing --- README.md | 18 ++++++++ authorize-service/src/authorize.rs | 7 +-- authorize-service/src/main.rs | 1 + authorize-service/src/process_variant.rs | 9 +--- authorize-service/src/routes.rs | 4 +- block-parser/src/block_json/input.rs | 5 +- client/src/main.rs | 59 +++++++++++++++++------- 7 files changed, 73 insertions(+), 30 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..f3df936 --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# Service + +## Testing + +``` +git clone git@github.com:ProvableHQ/snarkOS.git +cd snarkOS +./devnet.sh (use network ID 2) +``` + +``` +git clone git@github.com:ProvableHQ/service.git +cargo build --release +./target/release/authorize-service --network canary +./target/release/execute-service --network canary +./target/release/transfer_client --generate-requests +``` + diff --git a/authorize-service/src/authorize.rs b/authorize-service/src/authorize.rs index 9765927..b798c51 100644 --- a/authorize-service/src/authorize.rs +++ b/authorize-service/src/authorize.rs @@ -18,11 +18,12 @@ use super::*; // Initialize a thread-local `ProcessVariant`. thread_local! { - pub static PROCESS: RefCell> = const { RefCell::new(None) }; + pub static PROCESS1: RefCell> = const { RefCell::new(None) }; + pub static PROCESS2: RefCell> = const { RefCell::new(None) }; } pub fn authorize(bytes: Bytes) -> Result { - PROCESS.with(|process| { + PROCESS1.with(|process| { // Initialize the process if it is not already initialized. if process.borrow().is_none() { *process.borrow_mut() = match N::ID { @@ -47,7 +48,7 @@ pub fn authorize(bytes: Bytes) -> Result { } pub fn authorize_signed(bytes: Bytes) -> Result { - PROCESS.with(|process| { + PROCESS2.with(|process| { // Initialize the process if it is not already initialized. if process.borrow().is_none() { *process.borrow_mut() = match N::ID { diff --git a/authorize-service/src/main.rs b/authorize-service/src/main.rs index 7bfae53..6fa3cbc 100644 --- a/authorize-service/src/main.rs +++ b/authorize-service/src/main.rs @@ -33,6 +33,7 @@ async fn run(port: u16) { let routes = keygen_route::() .or(authorize_route::()) + .or(authorize_signed_route::()) .or(sign_route::()) .or(verify_route::()) .with(warp::trace( diff --git a/authorize-service/src/process_variant.rs b/authorize-service/src/process_variant.rs index 5c52a8d..25e8030 100644 --- a/authorize-service/src/process_variant.rs +++ b/authorize-service/src/process_variant.rs @@ -105,15 +105,10 @@ impl ProcessVariant { let rng = &mut rand_chacha::ChaCha20Rng::from_entropy(); // Authorize the function. - let authorization = process.authorize_request::( - request.request, - rng, - )?; + let authorization = process.authorize_request::(request.request, rng)?; // Construct the response. - let response = AuthorizeSignedResponse:: { - authorization, - }; + let response = AuthorizeSignedResponse:: { authorization }; // Return the response as JSON. Ok(serde_json::to_value(response)?) diff --git a/authorize-service/src/routes.rs b/authorize-service/src/routes.rs index a54ca6a..cf22a06 100644 --- a/authorize-service/src/routes.rs +++ b/authorize-service/src/routes.rs @@ -51,8 +51,8 @@ pub fn authorize_route() -> impl Filter() -> impl Filter + Clone -{ +pub fn authorize_signed_route( +) -> impl Filter + Clone { warp::post() .and(warp::path("authorize_signed")) .and(warp::path::end()) diff --git a/block-parser/src/block_json/input.rs b/block-parser/src/block_json/input.rs index 9ab6fc0..1a3aa78 100644 --- a/block-parser/src/block_json/input.rs +++ b/block-parser/src/block_json/input.rs @@ -29,7 +29,10 @@ impl InputJSON { None => bail!("Invalid input ID"), }; // Get the value of the input. - let value = json.get("value").and_then(|v| v.as_str()).map(|v|v.to_string()); + let value = json + .get("value") + .and_then(|v| v.as_str()) + .map(|v| v.to_string()); Ok(Self { type_, id, value }) } diff --git a/client/src/main.rs b/client/src/main.rs index 2fe9924..4a88a60 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -16,7 +16,8 @@ use snarkvm::ledger::block::Transaction; use snarkvm::prelude::{ - Field, FromBytes, Identifier, Network, PrivateKey, ProgramID, ToBytes, Uniform, Value, U64, Request, ValueType, Literal, + Field, FromBytes, Identifier, Literal, Network, PrivateKey, ProgramID, Request, ToBytes, + Uniform, Value, ValueType, U64, }; use authorize_service::*; @@ -101,7 +102,8 @@ async fn main() -> Result<()> { amount_in_microcredits, base_fee_in_microcredits, priority_fee_in_microcredits, - ).await + ) + .await } else { authorize_execute( &client, @@ -111,7 +113,8 @@ async fn main() -> Result<()> { base_fee_in_microcredits, priority_fee_in_microcredits, rng, - ).await + ) + .await } } @@ -240,18 +243,25 @@ async fn authorize_execute( let function_name = Identifier::from_str("transfer_public")?; let is_root = true; let root_tvk = None; - let inputs = vec![ - recipient, - amount_in_microcredits, + let inputs = vec![recipient, amount_in_microcredits]; + let input_types = [ + ValueType::from_str("address.public").unwrap(), + ValueType::from_str("u64.public").unwrap(), ]; - let input_types = [ValueType::from_str("address.public").unwrap(), ValueType::from_str("u64.public").unwrap()]; // Compute the request. - let request = Request::sign(&private_key, program_id, function_name, inputs.into_iter(), &input_types, root_tvk, is_root, rng)?; + let request = Request::sign( + &private_key, + program_id, + function_name, + inputs.into_iter(), + &input_types, + root_tvk, + is_root, + rng, + )?; // Construct an `AuthorizeSignRequest`. - let authorize_request = AuthorizeSignedRequest:: { - request, - }; + let authorize_request = AuthorizeSignedRequest:: { request }; // Send the request. let response = client @@ -262,7 +272,11 @@ async fn authorize_execute( // If the request was successful, deserialize the response as an `AuthorizeSignedResponse`. let authorize_response = match response.status().is_success() { - true => response.json::>().await?, + true => { + response + .json::>() + .await? + } false => bail!( "Authorization request failed with status: {}", response.status() @@ -287,12 +301,19 @@ async fn authorize_execute( ValueType::from_str("field.public").unwrap(), ]; // Compute the request. - let request = Request::sign(&private_key, program_id, function_name, inputs.into_iter(), &input_types, root_tvk, is_root, rng)?; + let request = Request::sign( + &private_key, + program_id, + function_name, + inputs.into_iter(), + &input_types, + root_tvk, + is_root, + rng, + )?; // Construct an `AuthorizeSignRequest`. - let authorize_request = AuthorizeSignedRequest:: { - request, - }; + let authorize_request = AuthorizeSignedRequest:: { request }; // Send the request. let response = client @@ -303,7 +324,11 @@ async fn authorize_execute( // If the request was successful, deserialize the response as an `AuthorizeSignedResponse`. let authorize_fee_response = match response.status().is_success() { - true => response.json::>().await?, + true => { + response + .json::>() + .await? + } false => bail!( "Authorization request failed with status: {}", response.status() From 3bcbbf79f3d314ec4b64ccecfbad507ef31ff1f0 Mon Sep 17 00:00:00 2001 From: Victor Sint Nicolaas Date: Fri, 29 Aug 2025 15:03:07 +0200 Subject: [PATCH 7/7] Add security note --- client/src/main.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/client/src/main.rs b/client/src/main.rs index 4a88a60..ea8951a 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -283,6 +283,9 @@ async fn authorize_execute( ), }; + // NOTE: in this example, it is critical for security reasons that the + // client computes the execution id, and checks that the tcm/scm matches the + // request. let execution_id = authorize_response.authorization.to_execution_id()?; // Construct the inputs for the fee request.