From 1b44061d1c8a1f09b555973faf1034586674a442 Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Wed, 23 Aug 2023 15:30:16 +0100 Subject: [PATCH 1/2] Handle restorePreamble in cli prepare_and_send helper --- cmd/soroban-cli/src/rpc/mod.rs | 49 ++++++++++++++++++++++---- cmd/soroban-cli/src/rpc/transaction.rs | 36 ++++++++++++++++--- 2 files changed, 73 insertions(+), 12 deletions(-) diff --git a/cmd/soroban-cli/src/rpc/mod.rs b/cmd/soroban-cli/src/rpc/mod.rs index c0fdc6520..7c76f5e2e 100644 --- a/cmd/soroban-cli/src/rpc/mod.rs +++ b/cmd/soroban-cli/src/rpc/mod.rs @@ -10,9 +10,10 @@ use soroban_env_host::{ events::HostEvent, xdr::{ self, AccountEntry, AccountId, ContractDataEntry, DiagnosticEvent, Error as XdrError, - LedgerEntryData, LedgerFootprint, LedgerKey, LedgerKeyAccount, PublicKey, ReadXdr, - SorobanAuthorizationEntry, Transaction, TransactionEnvelope, TransactionMeta, - TransactionMetaV3, TransactionResult, TransactionV1Envelope, Uint256, VecM, WriteXdr, + LedgerEntryData, LedgerFootprint, LedgerKey, LedgerKeyAccount, Operation, OperationBody, + PublicKey, ReadXdr, SequenceNumber, SorobanAuthorizationEntry, Transaction, + TransactionEnvelope, TransactionMeta, TransactionMetaV3, TransactionResult, + TransactionV1Envelope, Uint256, VecM, WriteXdr, }, }; use soroban_sdk::token; @@ -28,7 +29,7 @@ use tokio::time::sleep; use crate::utils::{self, contract_spec}; mod transaction; -use transaction::{assemble, sign_soroban_authorizations}; +use transaction::{assemble, build_restore_txn, sign_soroban_authorizations}; const VERSION: Option<&str> = option_env!("CARGO_PKG_VERSION"); @@ -202,6 +203,17 @@ pub struct SimulateHostFunctionResult { pub xdr: String, } +#[derive(serde::Deserialize, serde::Serialize, Debug)] +pub struct SimulateTransactionResponseRestorePreamble { + #[serde(rename = "transactionData")] + pub transaction_data: String, + #[serde( + rename = "minResourceFee", + deserialize_with = "deserialize_number_from_string" + )] + pub min_resource_fee: u32, +} + #[derive(serde::Deserialize, serde::Serialize, Debug)] pub struct SimulateTransactionResponse { #[serde(skip_serializing_if = "Option::is_none", default)] @@ -223,6 +235,8 @@ pub struct SimulateTransactionResponse { deserialize_with = "deserialize_number_from_string" )] pub latest_ledger: u32, + #[serde(rename = "restorePreamble")] + restore_preamble: Option, } #[derive(serde::Deserialize, serde::Serialize, Debug)] @@ -602,7 +616,13 @@ soroban config identity fund {address} --helper-url "# &self, tx: &Transaction, log_events: Option, - ) -> Result { + ) -> Result< + ( + Transaction, + Option, + ), + Error, + > { tracing::trace!(?tx); let sim_response = self .simulate_transaction(&TransactionEnvelope::Tx(TransactionV1Envelope { @@ -610,7 +630,10 @@ soroban config identity fund {address} --helper-url "# signatures: VecM::default(), })) .await?; - assemble(tx, &sim_response, log_events) + Ok(( + assemble(tx, &sim_response, log_events)?, + sim_response.restore_preamble, + )) } pub async fn prepare_and_send_transaction( @@ -622,9 +645,20 @@ soroban config identity fund {address} --helper-url "# log_events: Option, ) -> Result<(TransactionResult, TransactionMeta, Vec), Error> { let GetLatestLedgerResponse { sequence, .. } = self.get_latest_ledger().await?; - let unsigned_tx = self + let (mut unsigned_tx, restore_preamble) = self .prepare_transaction(tx_without_preflight, log_events) .await?; + if let Some(restore) = restore_preamble { + // Build and submit the restore transaction + self.send_transaction(&utils::sign_transaction( + source_key, + &build_restore_txn(&unsigned_tx, &restore)?, + network_passphrase, + )?) + .await?; + // Increment the original txn's seq_num so it doesn't conflict + unsigned_tx.seq_num = SequenceNumber(unsigned_tx.seq_num.0 + 1); + } let (part_signed_tx, signed_auth_entries) = sign_soroban_authorizations( &unsigned_tx, source_key, @@ -638,6 +672,7 @@ soroban config identity fund {address} --helper-url "# // re-simulate to calculate the new fees self.prepare_transaction(&part_signed_tx, log_events) .await? + .0 }; let tx = utils::sign_transaction(source_key, &fee_ready_txn, network_passphrase)?; self.send_transaction(&tx).await diff --git a/cmd/soroban-cli/src/rpc/transaction.rs b/cmd/soroban-cli/src/rpc/transaction.rs index 341451452..501181e77 100644 --- a/cmd/soroban-cli/src/rpc/transaction.rs +++ b/cmd/soroban-cli/src/rpc/transaction.rs @@ -1,13 +1,16 @@ use ed25519_dalek::Signer; use sha2::{Digest, Sha256}; use soroban_env_host::xdr::{ - AccountId, DiagnosticEvent, Hash, HashIdPreimage, HashIdPreimageSorobanAuthorization, - OperationBody, PublicKey, ReadXdr, ScAddress, ScMap, ScSymbol, ScVal, - SorobanAddressCredentials, SorobanAuthorizationEntry, SorobanCredentials, - SorobanTransactionData, Transaction, TransactionExt, Uint256, VecM, WriteXdr, + AccountId, DiagnosticEvent, ExtensionPoint, Hash, HashIdPreimage, + HashIdPreimageSorobanAuthorization, Memo, Operation, OperationBody, Preconditions, PublicKey, + ReadXdr, RestoreFootprintOp, ScAddress, ScMap, ScSymbol, ScVal, SorobanAddressCredentials, + SorobanAuthorizationEntry, SorobanCredentials, SorobanTransactionData, Transaction, + TransactionExt, Uint256, VecM, WriteXdr, }; -use crate::rpc::{Error, LogEvents, SimulateTransactionResponse}; +use crate::rpc::{ + Error, LogEvents, SimulateTransactionResponse, SimulateTransactionResponseRestorePreamble, +}; // Apply the result of a simulateTransaction onto a transaction envelope, preparing it for // submission to the network. @@ -233,6 +236,29 @@ pub fn sign_soroban_authorization_entry( Ok(auth) } +pub fn build_restore_txn( + parent: &Transaction, + restore: &SimulateTransactionResponseRestorePreamble, +) -> Result { + let transaction_data = SorobanTransactionData::from_xdr_base64(restore.transaction_data)?; + Ok(Transaction { + source_account: parent.source_account.clone(), + fee: parent.fee + restore.min_resource_fee, + seq_num: parent.seq_num, + cond: Preconditions::None, + memo: Memo::None, + operations: vec![Operation { + source_account: None, + body: OperationBody::RestoreFootprint(RestoreFootprintOp { + ext: ExtensionPoint::V0, + }), + }] + .try_into() + .unwrap(), + ext: TransactionExt::V1(transaction_data), + }) +} + #[cfg(test)] mod tests { use super::*; From e110ece6a68ca5b1db63a45512212af253efc42a Mon Sep 17 00:00:00 2001 From: Paul Bellamy Date: Wed, 23 Aug 2023 15:39:40 +0100 Subject: [PATCH 2/2] clippy and fix tests --- cmd/soroban-cli/src/rpc/mod.rs | 8 ++++---- cmd/soroban-cli/src/rpc/transaction.rs | 8 ++++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/cmd/soroban-cli/src/rpc/mod.rs b/cmd/soroban-cli/src/rpc/mod.rs index 7c76f5e2e..faf07e4c7 100644 --- a/cmd/soroban-cli/src/rpc/mod.rs +++ b/cmd/soroban-cli/src/rpc/mod.rs @@ -10,10 +10,10 @@ use soroban_env_host::{ events::HostEvent, xdr::{ self, AccountEntry, AccountId, ContractDataEntry, DiagnosticEvent, Error as XdrError, - LedgerEntryData, LedgerFootprint, LedgerKey, LedgerKeyAccount, Operation, OperationBody, - PublicKey, ReadXdr, SequenceNumber, SorobanAuthorizationEntry, Transaction, - TransactionEnvelope, TransactionMeta, TransactionMetaV3, TransactionResult, - TransactionV1Envelope, Uint256, VecM, WriteXdr, + LedgerEntryData, LedgerFootprint, LedgerKey, LedgerKeyAccount, PublicKey, ReadXdr, + SequenceNumber, SorobanAuthorizationEntry, Transaction, TransactionEnvelope, + TransactionMeta, TransactionMetaV3, TransactionResult, TransactionV1Envelope, Uint256, + VecM, WriteXdr, }, }; use soroban_sdk::token; diff --git a/cmd/soroban-cli/src/rpc/transaction.rs b/cmd/soroban-cli/src/rpc/transaction.rs index 501181e77..08c834188 100644 --- a/cmd/soroban-cli/src/rpc/transaction.rs +++ b/cmd/soroban-cli/src/rpc/transaction.rs @@ -240,11 +240,12 @@ pub fn build_restore_txn( parent: &Transaction, restore: &SimulateTransactionResponseRestorePreamble, ) -> Result { - let transaction_data = SorobanTransactionData::from_xdr_base64(restore.transaction_data)?; + let transaction_data = + SorobanTransactionData::from_xdr_base64(restore.transaction_data.clone())?; Ok(Transaction { source_account: parent.source_account.clone(), fee: parent.fee + restore.min_resource_fee, - seq_num: parent.seq_num, + seq_num: parent.seq_num.clone(), cond: Preconditions::None, memo: Memo::None, operations: vec![Operation { @@ -327,6 +328,7 @@ mod tests { mem_bytes: "0".to_string(), }, latest_ledger: 3, + restore_preamble: None, } } @@ -444,6 +446,7 @@ mod tests { mem_bytes: "0".to_string(), }, latest_ledger: 3, + restore_preamble: None, }, None, ); @@ -471,6 +474,7 @@ mod tests { mem_bytes: "0".to_string(), }, latest_ledger: 3, + restore_preamble: None, }, None, );