diff --git a/Cargo.lock b/Cargo.lock index 712e553d39798b..5af797f90a457a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3853,6 +3853,7 @@ dependencies = [ "num-traits 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", "pretty-hex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "reqwest 0.10.4 (registry+https://github.com/rust-lang/crates.io-index)", +<<<<<<< HEAD "serde 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.105 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3872,6 +3873,28 @@ dependencies = [ "solana-storage-program 1.1.4", "solana-vote-program 1.1.4", "solana-vote-signer 1.1.4", +======= + "serde 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.106 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)", + "solana-budget-program 1.2.0", + "solana-clap-utils 1.2.0", + "solana-cli-config 1.2.0", + "solana-client 1.2.0", + "solana-config-program 1.2.0", + "solana-core 1.2.0", + "solana-faucet 1.2.0", + "solana-logger 1.2.0", + "solana-net-utils 1.2.0", + "solana-remote-wallet 1.2.0", + "solana-runtime 1.2.0", + "solana-sdk 1.2.0", + "solana-stake-program 1.2.0", + "solana-storage-program 1.2.0", + "solana-transaction-status 1.2.0", + "solana-vote-program 1.2.0", + "solana-vote-signer 1.2.0", +>>>>>>> 7e7cbec8a... Passing -v/--verbose to `solana confirm` now displays the full transaction "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.13 (registry+https://github.com/rust-lang/crates.io-index)", "url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -4246,6 +4269,7 @@ dependencies = [ "histogram 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.48 (registry+https://github.com/rust-lang/crates.io-index)", "serde_yaml 0.8.11 (registry+https://github.com/rust-lang/crates.io-index)", +<<<<<<< HEAD "solana-clap-utils 1.1.4", "solana-ledger 1.1.4", "solana-logger 1.1.4", @@ -4253,6 +4277,17 @@ dependencies = [ "solana-sdk 1.1.4", "solana-stake-program 1.1.4", "solana-vote-program 1.1.4", +======= + "solana-clap-utils 1.2.0", + "solana-cli 1.2.0", + "solana-ledger 1.2.0", + "solana-logger 1.2.0", + "solana-runtime 1.2.0", + "solana-sdk 1.2.0", + "solana-stake-program 1.2.0", + "solana-transaction-status 1.2.0", + "solana-vote-program 1.2.0", +>>>>>>> 7e7cbec8a... Passing -v/--verbose to `solana confirm` now displays the full transaction "tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 8e52ae51d32f41..0606406ec5a586 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -26,6 +26,7 @@ pretty-hex = "0.1.1" reqwest = { version = "0.10.4", default-features = false, features = ["blocking", "rustls-tls", "json"] } serde = "1.0.105" serde_derive = "1.0.103" +<<<<<<< HEAD serde_json = "1.0.48" solana-budget-program = { path = "../programs/budget", version = "1.1.4" } solana-clap-utils = { path = "../clap-utils", version = "1.1.4" } @@ -43,6 +44,26 @@ solana-storage-program = { path = "../programs/storage", version = "1.1.4" } solana-vote-program = { path = "../programs/vote", version = "1.1.4" } solana-vote-signer = { path = "../vote-signer", version = "1.1.4" } thiserror = "1.0.13" +======= +serde_json = "1.0.51" +solana-budget-program = { path = "../programs/budget", version = "1.2.0" } +solana-clap-utils = { path = "../clap-utils", version = "1.2.0" } +solana-cli-config = { path = "../cli-config", version = "1.2.0" } +solana-client = { path = "../client", version = "1.2.0" } +solana-config-program = { path = "../programs/config", version = "1.2.0" } +solana-faucet = { path = "../faucet", version = "1.2.0" } +solana-logger = { path = "../logger", version = "1.2.0" } +solana-net-utils = { path = "../net-utils", version = "1.2.0" } +solana-remote-wallet = { path = "../remote-wallet", version = "1.2.0" } +solana-runtime = { path = "../runtime", version = "1.2.0" } +solana-sdk = { path = "../sdk", version = "1.2.0" } +solana-stake-program = { path = "../programs/stake", version = "1.2.0" } +solana-storage-program = { path = "../programs/storage", version = "1.2.0" } +solana-transaction-status = { path = "../transaction-status", version = "1.2.0" } +solana-vote-program = { path = "../programs/vote", version = "1.2.0" } +solana-vote-signer = { path = "../vote-signer", version = "1.2.0" } +thiserror = "1.0.15" +>>>>>>> 7e7cbec8a... Passing -v/--verbose to `solana confirm` now displays the full transaction url = "2.1.1" [dev-dependencies] diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 93365225488aab..9b3bbc1da30663 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -1165,12 +1165,48 @@ fn process_balance( } } -fn process_confirm(rpc_client: &RpcClient, signature: &Signature) -> ProcessResult { - match rpc_client.get_signature_status(&signature) { +fn process_confirm( + rpc_client: &RpcClient, + config: &CliConfig, + signature: &Signature, +) -> ProcessResult { + match rpc_client.get_signature_status_with_commitment_and_history( + &signature, + CommitmentConfig::max(), + true, + ) { Ok(status) => { if let Some(result) = status { match result { - Ok(_) => Ok("Confirmed".to_string()), + Ok(_) => { + if config.verbose { + match rpc_client.get_confirmed_transaction( + signature, + solana_transaction_status::TransactionEncoding::Binary, + ) { + Ok(confirmed_transaction) => { + println!("\nTransaction:"); + crate::display::println_transaction( + &confirmed_transaction + .transaction + .transaction + .decode() + .expect("Successful decode"), + &confirmed_transaction.transaction.meta, + " ", + ); + println!(); + Ok(format!("Confirmed in slot {}", confirmed_transaction.slot)) + } + Err(err) => Ok(format!( + "Confirmed. Unable to get confirmed transaction details: {}", + err + )), + } + } else { + Ok("Confirmed".to_string()) + } + } Err(err) => Ok(format!("Transaction failed with error: {}", err)), } } else { @@ -2063,7 +2099,7 @@ pub fn process_command(config: &CliConfig) -> ProcessResult { // Cancel a contract by contract Pubkey CliCommand::Cancel(pubkey) => process_cancel(&rpc_client, config, &pubkey), // Confirm the last client transaction by signature - CliCommand::Confirm(signature) => process_confirm(&rpc_client, signature), + CliCommand::Confirm(signature) => process_confirm(&rpc_client, config, signature), // If client has positive balance, pay lamports to another address CliCommand::Pay(PayCommand { lamports, diff --git a/cli/src/display.rs b/cli/src/display.rs index 2fcc24ac7e03a5..a5b386e2953014 100644 --- a/cli/src/display.rs +++ b/cli/src/display.rs @@ -1,6 +1,10 @@ use crate::cli::SettingType; use console::style; -use solana_sdk::hash::Hash; +use solana_sdk::{ + hash::Hash, native_token::lamports_to_sol, program_utils::limited_deserialize, + transaction::Transaction, +}; +use solana_transaction_status::RpcTransactionStatusMeta; use std::fmt; // Pretty print a "name value" @@ -59,3 +63,106 @@ pub fn println_signers( } println!(); } + +pub fn println_transaction( + transaction: &Transaction, + transaction_status: &Option, + prefix: &str, +) { + let message = &transaction.message; + println!("{}Recent Blockhash: {:?}", prefix, message.recent_blockhash); + for (signature_index, signature) in transaction.signatures.iter().enumerate() { + println!("{}Signature {}: {:?}", prefix, signature_index, signature); + } + println!("{}{:?}", prefix, message.header); + for (account_index, account) in message.account_keys.iter().enumerate() { + println!("{}Account {}: {:?}", prefix, account_index, account); + } + for (instruction_index, instruction) in message.instructions.iter().enumerate() { + let program_pubkey = message.account_keys[instruction.program_id_index as usize]; + println!("{}Instruction {}", prefix, instruction_index); + println!( + "{} Program: {} ({})", + prefix, program_pubkey, instruction.program_id_index + ); + for (account_index, account) in instruction.accounts.iter().enumerate() { + let account_pubkey = message.account_keys[*account as usize]; + println!( + "{} Account {}: {} ({})", + prefix, account_index, account_pubkey, account + ); + } + + let mut raw = true; + if program_pubkey == solana_vote_program::id() { + if let Ok(vote_instruction) = limited_deserialize::< + solana_vote_program::vote_instruction::VoteInstruction, + >(&instruction.data) + { + println!("{} {:?}", prefix, vote_instruction); + raw = false; + } + } else if program_pubkey == solana_stake_program::id() { + if let Ok(stake_instruction) = limited_deserialize::< + solana_stake_program::stake_instruction::StakeInstruction, + >(&instruction.data) + { + println!("{} {:?}", prefix, stake_instruction); + raw = false; + } + } else if program_pubkey == solana_sdk::system_program::id() { + if let Ok(system_instruction) = limited_deserialize::< + solana_sdk::system_instruction::SystemInstruction, + >(&instruction.data) + { + println!("{} {:?}", prefix, system_instruction); + raw = false; + } + } + + if raw { + println!("{} Data: {:?}", prefix, instruction.data); + } + } + + if let Some(transaction_status) = transaction_status { + println!( + "{}Status: {}", + prefix, + match &transaction_status.status { + Ok(_) => "Ok".into(), + Err(err) => err.to_string(), + } + ); + println!("{} Fee: {}", prefix, transaction_status.fee); + assert_eq!( + transaction_status.pre_balances.len(), + transaction_status.post_balances.len() + ); + for (i, (pre, post)) in transaction_status + .pre_balances + .iter() + .zip(transaction_status.post_balances.iter()) + .enumerate() + { + if pre == post { + println!( + "{} Account {} balance: {} SOL", + prefix, + i, + lamports_to_sol(*pre) + ); + } else { + println!( + "{} Account {} balance: {} SOL -> {} SOL", + prefix, + i, + lamports_to_sol(*pre), + lamports_to_sol(*post) + ); + } + } + } else { + println!("{}Status: Unavailable", prefix); + } +} diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 6c0f34fec12260..2f3feef2ab873a 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -23,7 +23,9 @@ use solana_sdk::{ signers::Signers, transaction::{self, Transaction, TransactionError}, }; -use solana_transaction_status::{ConfirmedBlock, TransactionEncoding, TransactionStatus}; +use solana_transaction_status::{ + ConfirmedBlock, ConfirmedTransaction, TransactionEncoding, TransactionStatus, +}; use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY; use std::{ error, @@ -158,6 +160,28 @@ impl RpcClient { .map(|status_meta| status_meta.status)) } + pub fn get_signature_status_with_commitment_and_history( + &self, + signature: &Signature, + commitment_config: CommitmentConfig, + search_transaction_history: bool, + ) -> ClientResult>> { + let signature_status = self.client.send( + &RpcRequest::GetSignatureStatuses, + json!([[signature.to_string()], { + "searchTransactionHistory": search_transaction_history + }]), + 5, + )?; + let result: Response>> = + serde_json::from_value(signature_status) + .map_err(|err| ClientError::new_with_command(err.into(), "GetSignatureStatuses"))?; + Ok(result.value[0] + .clone() + .filter(|result| result.satisfies_commitment(commitment_config)) + .map(|status_meta| status_meta.status)) + } + pub fn get_slot(&self) -> ClientResult { self.get_slot_with_commitment(CommitmentConfig::default()) } @@ -255,6 +279,44 @@ impl RpcClient { .map_err(|err| ClientError::new_with_command(err.into(), "GetConfirmedBlocks")) } + pub fn get_confirmed_signatures_for_address( + &self, + address: &Pubkey, + start_slot: Slot, + end_slot: Slot, + ) -> ClientResult> { + let response = self + .client + .send( + &RpcRequest::GetConfirmedSignaturesForAddress, + json!([address, start_slot, end_slot]), + 0, + ) + .map_err(|err| err.into_with_command("GetConfirmedSignaturesForAddress"))?; + + serde_json::from_value(response).map_err(|err| { + ClientError::new_with_command(err.into(), "GetConfirmedSignaturesForAddress") + }) + } + + pub fn get_confirmed_transaction( + &self, + signature: &Signature, + encoding: TransactionEncoding, + ) -> ClientResult { + let response = self + .client + .send( + &RpcRequest::GetConfirmedTransaction, + json!([signature.to_string(), encoding]), + 0, + ) + .map_err(|err| err.into_with_command("GetConfirmedTransaction"))?; + + serde_json::from_value(response) + .map_err(|err| ClientError::new_with_command(err.into(), "GetConfirmedTransaction")) + } + pub fn get_block_time(&self, slot: Slot) -> ClientResult { let response = self .client diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index bb1e578b1fd2c1..c9e7e9b3545298 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -11,6 +11,8 @@ pub enum RpcRequest { GetClusterNodes, GetConfirmedBlock, GetConfirmedBlocks, + GetConfirmedSignaturesForAddress, + GetConfirmedTransaction, GetEpochInfo, GetEpochSchedule, GetGenesisHash, @@ -52,6 +54,8 @@ impl RpcRequest { RpcRequest::GetClusterNodes => "getClusterNodes", RpcRequest::GetConfirmedBlock => "getConfirmedBlock", RpcRequest::GetConfirmedBlocks => "getConfirmedBlocks", + RpcRequest::GetConfirmedSignaturesForAddress => "getConfirmedSignaturesForAddress", + RpcRequest::GetConfirmedTransaction => "getConfirmedTransaction", RpcRequest::GetEpochInfo => "getEpochInfo", RpcRequest::GetEpochSchedule => "getEpochSchedule", RpcRequest::GetGenesisHash => "getGenesisHash", diff --git a/ledger-tool/Cargo.toml b/ledger-tool/Cargo.toml index b133e23f07f913..10e510db826c3e 100644 --- a/ledger-tool/Cargo.toml +++ b/ledger-tool/Cargo.toml @@ -14,6 +14,7 @@ clap = "2.33.0" histogram = "*" serde_json = "1.0.48" serde_yaml = "0.8.11" +<<<<<<< HEAD solana-clap-utils = { path = "../clap-utils", version = "1.1.4" } solana-ledger = { path = "../ledger", version = "1.1.4" } solana-logger = { path = "../logger", version = "1.1.4" } @@ -21,6 +22,17 @@ solana-runtime = { path = "../runtime", version = "1.1.4" } solana-sdk = { path = "../sdk", version = "1.1.4" } solana-vote-program = { path = "../programs/vote", version = "1.1.4" } solana-stake-program = { path = "../programs/stake", version = "1.1.4" } +======= +solana-clap-utils = { path = "../clap-utils", version = "1.2.0" } +solana-cli = { path = "../cli", version = "1.2.0" } +solana-ledger = { path = "../ledger", version = "1.2.0" } +solana-logger = { path = "../logger", version = "1.2.0" } +solana-runtime = { path = "../runtime", version = "1.2.0" } +solana-sdk = { path = "../sdk", version = "1.2.0" } +solana-stake-program = { path = "../programs/stake", version = "1.2.0" } +solana-transaction-status = { path = "../transaction-status", version = "1.2.0" } +solana-vote-program = { path = "../programs/vote", version = "1.2.0" } +>>>>>>> 7e7cbec8a... Passing -v/--verbose to `solana confirm` now displays the full transaction tempfile = "3.1.0" [dev-dependencies] diff --git a/ledger-tool/src/main.rs b/ledger-tool/src/main.rs index 0e0a5a83d9f6ba..2d7a5996289f81 100644 --- a/ledger-tool/src/main.rs +++ b/ledger-tool/src/main.rs @@ -16,8 +16,8 @@ use solana_ledger::{ snapshot_utils, }; use solana_sdk::{ - clock::Slot, genesis_config::GenesisConfig, native_token::lamports_to_sol, - program_utils::limited_deserialize, pubkey::Pubkey, shred_version::compute_shred_version, + clock::Slot, genesis_config::GenesisConfig, native_token::lamports_to_sol, pubkey::Pubkey, + shred_version::compute_shred_version, }; use solana_vote_program::vote_state::VoteState; use std::{ @@ -84,112 +84,23 @@ fn output_slot( entry.transactions.len() ); for (transactions_index, transaction) in entry.transactions.iter().enumerate() { - let message = &transaction.message; println!(" Transaction {}", transactions_index); - println!(" Recent Blockhash: {:?}", message.recent_blockhash); - for (signature_index, signature) in transaction.signatures.iter().enumerate() { - println!(" Signature {}: {:?}", signature_index, signature); - } - println!(" {:?}", message.header); - for (account_index, account) in message.account_keys.iter().enumerate() { - println!(" Account {}: {:?}", account_index, account); - } - for (instruction_index, instruction) in message.instructions.iter().enumerate() - { - let program_pubkey = - message.account_keys[instruction.program_id_index as usize]; - println!(" Instruction {}", instruction_index); - println!( - " Program: {} ({})", - program_pubkey, instruction.program_id_index - ); - for (account_index, account) in instruction.accounts.iter().enumerate() { - let account_pubkey = message.account_keys[*account as usize]; - println!( - " Account {}: {} ({})", - account_index, account_pubkey, account + let transaction_status = blockstore + .read_transaction_status((transaction.signatures[0], slot)) + .unwrap_or_else(|err| { + eprintln!( + "Failed to read transaction status for {} at slot {}: {}", + transaction.signatures[0], slot, err ); - } - - let mut raw = true; - if program_pubkey == solana_vote_program::id() { - if let Ok(vote_instruction) = - limited_deserialize::< - solana_vote_program::vote_instruction::VoteInstruction, - >(&instruction.data) - { - println!(" {:?}", vote_instruction); - raw = false; - } - } else if program_pubkey == solana_stake_program::id() { - if let Ok(stake_instruction) = - limited_deserialize::< - solana_stake_program::stake_instruction::StakeInstruction, - >(&instruction.data) - { - println!(" {:?}", stake_instruction); - raw = false; - } - } else if program_pubkey == solana_sdk::system_program::id() { - if let Ok(system_instruction) = - limited_deserialize::< - solana_sdk::system_instruction::SystemInstruction, - >(&instruction.data) - { - println!(" {:?}", system_instruction); - raw = false; - } - } + None + }) + .map(|transaction_status| transaction_status.into()); - if raw { - println!(" Data: {:?}", instruction.data); - } - } - match blockstore.read_transaction_status((transaction.signatures[0], slot)) { - Ok(transaction_status) => { - if let Some(transaction_status) = transaction_status { - println!( - " Status: {}", - if transaction_status.status.is_ok() { - "Ok".into() - } else { - transaction_status.status.unwrap_err().to_string() - } - ); - println!(" Fee: {}", transaction_status.fee); - assert_eq!( - transaction_status.pre_balances.len(), - transaction_status.post_balances.len() - ); - for (i, (pre, post)) in transaction_status - .pre_balances - .iter() - .zip(transaction_status.post_balances.iter()) - .enumerate() - { - if pre == post { - println!( - " Account {} balance: {} SOL", - i, - lamports_to_sol(*pre) - ); - } else { - println!( - " Account {} balance: {} SOL -> {} SOL", - i, - lamports_to_sol(*pre), - lamports_to_sol(*post) - ); - } - } - } else { - println!(" Status: Unavailable"); - } - } - Err(err) => { - println!(" Status: {:?}", err); - } - } + solana_cli::display::println_transaction( + &transaction, + &transaction_status, + " ", + ); } } LedgerOutputMethod::Json => { diff --git a/transaction-status/src/lib.rs b/transaction-status/src/lib.rs index fd066141585a72..5ff0dde8670101 100644 --- a/transaction-status/src/lib.rs +++ b/transaction-status/src/lib.rs @@ -64,7 +64,7 @@ impl From for RpcTransactionStatusMeta { #[serde(rename_all = "camelCase")] pub struct TransactionStatus { pub slot: Slot, - pub confirmations: Option, + pub confirmations: Option, // None = rooted pub status: Result<()>, pub err: Option, }