From 3f120936b48e7265293001b3bf4a2dacbe707302 Mon Sep 17 00:00:00 2001 From: chalda Date: Fri, 11 Oct 2024 10:58:05 +0200 Subject: [PATCH] [client-rs] tx builder with option to not check signers (#8) * [client-rs] tx builder with option to not check signers * [review] fix for wrong sig_verify --- libs/dynsigner/src/lib.rs | 2 +- .../src/marinade/builder.rs | 2 +- .../src/transactions/prepared_transaction.rs | 9 +++- .../src/transactions/signature_builder.rs | 32 ++++++++++-- .../src/transactions/transaction_builder.rs | 50 +++++++++++++++---- .../src/transactions/transaction_executors.rs | 26 +++++++--- libs/marinade-common-cli/src/matchers.rs | 2 +- 7 files changed, 96 insertions(+), 27 deletions(-) diff --git a/libs/dynsigner/src/lib.rs b/libs/dynsigner/src/lib.rs index 4e781e4..31a30dd 100644 --- a/libs/dynsigner/src/lib.rs +++ b/libs/dynsigner/src/lib.rs @@ -1,7 +1,7 @@ use solana_sdk::pubkey::Pubkey; +use solana_sdk::signature::Keypair; use solana_sdk::signer::Signer; use std::sync::Arc; -use solana_sdk::signature::Keypair; /// Auxiliary data structure to align the types of the solana-clap-utils with anchor-client. pub struct DynSigner(pub Arc); diff --git a/libs/marinade-client-rs/src/marinade/builder.rs b/libs/marinade-client-rs/src/marinade/builder.rs index 69649f6..bfa874f 100644 --- a/libs/marinade-client-rs/src/marinade/builder.rs +++ b/libs/marinade-client-rs/src/marinade/builder.rs @@ -15,10 +15,10 @@ use dynsigner::PubkeyOrKeypair; use marinade_finance::instructions::{ChangeAuthorityData, ConfigMarinadeParams}; use marinade_finance::state::Fee; use solana_sdk::pubkey::Pubkey; +use solana_sdk::signature::Keypair; use solana_sdk::signer::Signer; use std::ops::Deref; use std::sync::Arc; -use solana_sdk::signature::Keypair; pub trait MarinadeRequestBuilder<'a, C> { fn add_validator( diff --git a/libs/marinade-client-rs/src/transactions/prepared_transaction.rs b/libs/marinade-client-rs/src/transactions/prepared_transaction.rs index 3daddd7..aa4bf6c 100644 --- a/libs/marinade-client-rs/src/transactions/prepared_transaction.rs +++ b/libs/marinade-client-rs/src/transactions/prepared_transaction.rs @@ -1,10 +1,10 @@ use crate::transactions::signature_builder::SignatureBuilder; use solana_sdk::hash::Hash; use solana_sdk::pubkey::Pubkey; +use solana_sdk::signer::keypair::Keypair; use solana_sdk::signer::SignerError; use solana_sdk::transaction::Transaction; use std::sync::Arc; -use solana_sdk::signer::keypair::Keypair; pub struct PreparedTransaction { pub transaction: Transaction, @@ -23,6 +23,13 @@ impl PreparedTransaction { }) } + pub fn new_no_signers(transaction: Transaction) -> Self { + Self { + transaction, + signers: vec![], + } + } + pub fn sign(&mut self, recent_blockhash: Hash) -> Result<&Transaction, SignerError> { self.transaction.try_sign( &self diff --git a/libs/marinade-client-rs/src/transactions/signature_builder.rs b/libs/marinade-client-rs/src/transactions/signature_builder.rs index 21be12e..417403a 100644 --- a/libs/marinade-client-rs/src/transactions/signature_builder.rs +++ b/libs/marinade-client-rs/src/transactions/signature_builder.rs @@ -1,4 +1,4 @@ -use log::error; +use log::{debug, error}; use solana_sdk::{ pubkey::Pubkey, signature::{Keypair, Signature, Signer, SignerError}, @@ -7,12 +7,27 @@ use solana_sdk::{ }; use std::{collections::HashMap, sync::Arc}; -#[derive(Debug, Default)] +#[derive(Debug)] pub struct SignatureBuilder { pub signers: HashMap>, + pub is_check_signers: bool, } impl SignatureBuilder { + pub fn new() -> Self { + Self { + is_check_signers: true, + signers: HashMap::new(), + } + } + + pub fn new_without_check() -> Self { + Self { + is_check_signers: false, + ..SignatureBuilder::new() + } + } + pub fn add_signer(&mut self, signer: Arc) -> Pubkey { let pubkey = signer.pubkey(); self.signers.insert(pubkey, signer); @@ -47,9 +62,16 @@ impl SignatureBuilder { if let Some(keypair) = self.signers.get(&key) { transaction.signatures[pos] = keypair.try_sign_message(&message)?; } else { - error!("sign_transaction: not enough signers, expected key: {}, available keys in builder: {:?}", - key, self.signers.keys().collect::>()); - return Err(SignerError::NotEnoughSigners); + let error_msg = format!( + "sign_transaction: not enough signers, expected key: {}, available keys in builder: {:?}", + key, self.signers.keys().collect::>() + ); + if self.is_check_signers { + error!("{}", error_msg); + return Err(SignerError::NotEnoughSigners); + } else { + debug!("{}", error_msg); + } } } Ok(()) diff --git a/libs/marinade-client-rs/src/transactions/transaction_builder.rs b/libs/marinade-client-rs/src/transactions/transaction_builder.rs index 9ba2d08..6a7ec00 100644 --- a/libs/marinade-client-rs/src/transactions/transaction_builder.rs +++ b/libs/marinade-client-rs/src/transactions/transaction_builder.rs @@ -5,7 +5,10 @@ use anyhow::anyhow; use log::error; use once_cell::sync::OnceCell; use solana_sdk::{ - instruction::Instruction, packet::PACKET_DATA_SIZE, pubkey::Pubkey, signature::{Signer, Keypair}, + instruction::Instruction, + packet::PACKET_DATA_SIZE, + pubkey::Pubkey, + signature::{Keypair, Signer}, transaction::Transaction, }; use std::ops::Deref; @@ -27,22 +30,31 @@ pub struct TransactionBuilder { instruction_packs: Vec>, current_instruction_pack: OnceCell>, max_transaction_size: usize, + is_check_signers: bool, } impl TransactionBuilder { pub fn new(fee_payer: Arc, max_transaction_size: usize) -> Self { - let mut signature_builder = SignatureBuilder::default(); + let mut signature_builder = SignatureBuilder::new(); + let fee_payer = signature_builder.add_signer(fee_payer); let builder = Self { - fee_payer: signature_builder.add_signer(fee_payer), + fee_payer, signature_builder, instruction_packs: Vec::new(), current_instruction_pack: OnceCell::new(), max_transaction_size, + is_check_signers: true, }; builder.current_instruction_pack.set(Vec::new()).unwrap(); builder } + pub fn with_no_signers_check(mut self) -> Self { + self.is_check_signers = false; + self.signature_builder = SignatureBuilder::new_without_check(); + self + } + pub fn fee_payer(&self) -> Pubkey { self.fee_payer } @@ -80,6 +92,10 @@ impl TransactionBuilder { } fn check_signers(&self, instruction: &Instruction) -> Result<(), TransactionBuildError> { + if !self.is_check_signers { + return Ok(()); + } + for account in &instruction.accounts { if account.is_signer && !self.signature_builder.contains_key(&account.pubkey) { error!( @@ -97,6 +113,10 @@ impl TransactionBuilder { Ok(()) } + pub fn is_check_signers(&self) -> bool { + self.is_check_signers + } + #[inline] pub fn finish_instruction_pack(&mut self) { self.instruction_packs.push( @@ -187,10 +207,14 @@ impl TransactionBuilder { let instructions: Vec = self.instruction_packs.remove(0).into_iter().collect(); let transaction = Transaction::new_with_payer(&instructions, Some(&self.fee_payer)); - Some( - PreparedTransaction::new(transaction, &self.signature_builder) - .expect("Signature keys must be checked when instruction added"), - ) + if self.is_check_signers() { + Some( + PreparedTransaction::new(transaction, &self.signature_builder) + .expect("Signature keys must be checked when instruction added"), + ) + } else { + Some(PreparedTransaction::new_no_signers(transaction)) + } } else { None } @@ -244,10 +268,14 @@ impl TransactionBuilder { } transaction }; - Some( - PreparedTransaction::new(transaction, &self.signature_builder) - .expect("Signature keys must be checked when instruction added"), - ) + if self.is_check_signers() { + Some( + PreparedTransaction::new(transaction, &self.signature_builder) + .expect("Signature keys must be checked when instruction added"), + ) + } else { + Some(PreparedTransaction::new_no_signers(transaction)) + } } pub fn build_single_combined(&mut self) -> Option { diff --git a/libs/marinade-client-rs/src/transactions/transaction_executors.rs b/libs/marinade-client-rs/src/transactions/transaction_executors.rs index a8cea9d..d637243 100644 --- a/libs/marinade-client-rs/src/transactions/transaction_executors.rs +++ b/libs/marinade-client-rs/src/transactions/transaction_executors.rs @@ -51,11 +51,19 @@ pub fn log_execution( } pub trait TransactionSimulator { - fn simulate(&self, rpc_client: &RpcClient, sig_verify: bool) -> RpcResult; + fn simulate( + &self, + rpc_client: &RpcClient, + sig_verify: bool, + ) -> RpcResult; } impl<'a, C: Deref + Clone> TransactionSimulator for RequestBuilder<'a, C> { - fn simulate(&self, rpc_client: &RpcClient, sig_verify: bool) -> RpcResult { + fn simulate( + &self, + rpc_client: &RpcClient, + sig_verify: bool, + ) -> RpcResult { let tx = self.signed_transaction().map_err(|err| { error!( "RequestBuilder#simulate: cannot build transactions from builder: {:?}", @@ -63,10 +71,13 @@ impl<'a, C: Deref + Clone> TransactionSimulator for Reques ); ForUser(format!("Building transaction error: {}", err)) })?; - rpc_client.simulate_transaction_with_config(&tx, RpcSimulateTransactionConfig { - sig_verify, - ..RpcSimulateTransactionConfig::default() - }) + rpc_client.simulate_transaction_with_config( + &tx, + RpcSimulateTransactionConfig { + sig_verify, + ..RpcSimulateTransactionConfig::default() + }, + ) } } @@ -220,6 +231,7 @@ pub fn execute_transaction_builder( // expecting the instructions are dependent one to each other // the result of the first can be used in the next one, for that simulation is run only for the fist bunch let mut number_of_transactions = 0_u32; + let is_checked_signers = transaction_builder.is_check_signers(); for mut prepared_transaction in transaction_builder.sequence_combined() { number_of_transactions += 1; if number_of_transactions > 1 { @@ -239,7 +251,7 @@ pub fn execute_transaction_builder( &mut prepared_transaction, rpc_client, RpcSimulateTransactionConfig { - sig_verify: !print, + sig_verify: !print && is_checked_signers, commitment: simulation_commitment, encoding: preflight_config.encoding, min_context_slot: preflight_config.min_context_slot, diff --git a/libs/marinade-common-cli/src/matchers.rs b/libs/marinade-common-cli/src/matchers.rs index 88656be..ea53541 100644 --- a/libs/marinade-common-cli/src/matchers.rs +++ b/libs/marinade-common-cli/src/matchers.rs @@ -6,9 +6,9 @@ use solana_clap_utils::input_parsers::pubkey_of_signer; use solana_clap_utils::keypair::{keypair_from_path, signer_from_path}; use solana_remote_wallet::remote_wallet::RemoteWalletManager; use solana_sdk::pubkey::Pubkey; +use solana_sdk::signature::Keypair; use solana_sdk::signer::Signer; use std::{str::FromStr, sync::Arc}; -use solana_sdk::signature::Keypair; // Getting keypair from the matched name as the keypair path argument, or returns the default signer pub fn keypair_from_path_or_default(