Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Passing -v/--verbose to solana confirm now displays the full transaction (bp #9520) #9530

Merged
merged 1 commit into from
Apr 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ solana-runtime = { path = "../runtime", version = "1.1.4" }
solana-sdk = { path = "../sdk", version = "1.1.4" }
solana-stake-program = { path = "../programs/stake", version = "1.1.4" }
solana-storage-program = { path = "../programs/storage", version = "1.1.4" }
solana-transaction-status = { path = "../transaction-status", 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"
Expand Down
44 changes: 40 additions & 4 deletions cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down
109 changes: 108 additions & 1 deletion cli/src/display.rs
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -59,3 +63,106 @@ pub fn println_signers(
}
println!();
}

pub fn println_transaction(
transaction: &Transaction,
transaction_status: &Option<RpcTransactionStatusMeta>,
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);
}
}
64 changes: 63 additions & 1 deletion client/src/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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<Option<transaction::Result<()>>> {
let signature_status = self.client.send(
&RpcRequest::GetSignatureStatuses,
json!([[signature.to_string()], {
"searchTransactionHistory": search_transaction_history
}]),
5,
)?;
let result: Response<Vec<Option<TransactionStatus>>> =
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<Slot> {
self.get_slot_with_commitment(CommitmentConfig::default())
}
Expand Down Expand Up @@ -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<Vec<Signature>> {
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<ConfirmedTransaction> {
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<UnixTimestamp> {
let response = self
.client
Expand Down
4 changes: 4 additions & 0 deletions client/src/rpc_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ pub enum RpcRequest {
GetClusterNodes,
GetConfirmedBlock,
GetConfirmedBlocks,
GetConfirmedSignaturesForAddress,
GetConfirmedTransaction,
GetEpochInfo,
GetEpochSchedule,
GetGenesisHash,
Expand Down Expand Up @@ -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",
Expand Down
4 changes: 3 additions & 1 deletion ledger-tool/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ histogram = "*"
serde_json = "1.0.48"
serde_yaml = "0.8.11"
solana-clap-utils = { path = "../clap-utils", version = "1.1.4" }
solana-cli = { path = "../cli", version = "1.1.4" }
solana-ledger = { path = "../ledger", version = "1.1.4" }
solana-logger = { path = "../logger", version = "1.1.4" }
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-transaction-status = { path = "../transaction-status", version = "1.1.4" }
solana-vote-program = { path = "../programs/vote", version = "1.1.4" }
tempfile = "3.1.0"

[dev-dependencies]
Expand Down
Loading