transaction-status: Add return data to meta (#23688)

* transaction-status: Add return data to meta

* Add return data to simulation results

* Use pretty-hex for printing return data

* Update arg name, make TransactionRecord struct

* Rename TransactionRecord -> ExecutionRecord
This commit is contained in:
Jon Cinque 2022-03-22 23:17:05 +01:00 committed by GitHub
parent 359e2de090
commit 7af48465fa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 439 additions and 45 deletions

2
Cargo.lock generated
View File

@ -4424,6 +4424,7 @@ dependencies = [
"ed25519-dalek", "ed25519-dalek",
"humantime", "humantime",
"indicatif", "indicatif",
"pretty-hex",
"serde", "serde",
"serde_json", "serde_json",
"solana-account-decoder", "solana-account-decoder",
@ -5309,6 +5310,7 @@ dependencies = [
name = "solana-program-test" name = "solana-program-test"
version = "1.11.0" version = "1.11.0"
dependencies = [ dependencies = [
"assert_matches",
"async-trait", "async-trait",
"base64 0.13.0", "base64 0.13.0",
"bincode", "bincode",

View File

@ -1,5 +1,8 @@
use { use {
solana_sdk::{transaction::TransactionError, transport::TransportError}, solana_sdk::{
transaction::TransactionError, transaction_context::TransactionReturnData,
transport::TransportError,
},
std::io, std::io,
tarpc::client::RpcError, tarpc::client::RpcError,
thiserror::Error, thiserror::Error,
@ -25,6 +28,7 @@ pub enum BanksClientError {
err: TransactionError, err: TransactionError,
logs: Vec<String>, logs: Vec<String>,
units_consumed: u64, units_consumed: u64,
return_data: Option<TransactionReturnData>,
}, },
} }

View File

@ -247,6 +247,7 @@ impl BanksClient {
err, err,
logs: simulation_details.logs, logs: simulation_details.logs,
units_consumed: simulation_details.units_consumed, units_consumed: simulation_details.units_consumed,
return_data: simulation_details.return_data,
}), }),
BanksTransactionResultWithSimulation { BanksTransactionResultWithSimulation {
result: Some(result), result: Some(result),

View File

@ -12,6 +12,7 @@ use {
pubkey::Pubkey, pubkey::Pubkey,
signature::Signature, signature::Signature,
transaction::{self, Transaction, TransactionError}, transaction::{self, Transaction, TransactionError},
transaction_context::TransactionReturnData,
}, },
}; };
@ -35,6 +36,7 @@ pub struct TransactionStatus {
pub struct TransactionSimulationDetails { pub struct TransactionSimulationDetails {
pub logs: Vec<String>, pub logs: Vec<String>,
pub units_consumed: u64, pub units_consumed: u64,
pub return_data: Option<TransactionReturnData>,
} }
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]

View File

@ -266,6 +266,7 @@ impl Banks for BanksServer {
logs, logs,
post_simulation_accounts: _, post_simulation_accounts: _,
units_consumed, units_consumed,
return_data,
} = self } = self
.bank(commitment) .bank(commitment)
.simulate_transaction_unchecked(sanitized_transaction) .simulate_transaction_unchecked(sanitized_transaction)
@ -275,6 +276,7 @@ impl Banks for BanksServer {
simulation_details: Some(TransactionSimulationDetails { simulation_details: Some(TransactionSimulationDetails {
logs, logs,
units_consumed, units_consumed,
return_data,
}), }),
}; };
} }

View File

@ -17,6 +17,7 @@ clap = "2.33.0"
console = "0.15.0" console = "0.15.0"
humantime = "2.0.1" humantime = "2.0.1"
indicatif = "0.16.2" indicatif = "0.16.2"
pretty-hex = "0.2.1"
serde = "1.0.136" serde = "1.0.136"
serde_json = "1.0.79" serde_json = "1.0.79"
solana-account-decoder = { path = "../account-decoder", version = "=1.11.0" } solana-account-decoder = { path = "../account-decoder", version = "=1.11.0" }

View File

@ -14,6 +14,7 @@ use {
signature::Signature, signature::Signature,
stake, stake,
transaction::{TransactionError, TransactionVersion, VersionedTransaction}, transaction::{TransactionError, TransactionVersion, VersionedTransaction},
transaction_context::TransactionReturnData,
}, },
solana_transaction_status::{Rewards, UiTransactionStatusMeta}, solana_transaction_status::{Rewards, UiTransactionStatusMeta},
spl_memo::{id as spl_memo_id, v1::id as spl_memo_v1_id}, spl_memo::{id as spl_memo_id, v1::id as spl_memo_v1_id},
@ -246,6 +247,7 @@ fn write_transaction<W: io::Write>(
write_fees(w, transaction_status.fee, prefix)?; write_fees(w, transaction_status.fee, prefix)?;
write_balances(w, transaction_status, prefix)?; write_balances(w, transaction_status, prefix)?;
write_log_messages(w, transaction_status.log_messages.as_ref(), prefix)?; write_log_messages(w, transaction_status.log_messages.as_ref(), prefix)?;
write_return_data(w, transaction_status.return_data.as_ref(), prefix)?;
write_rewards(w, transaction_status.rewards.as_ref(), prefix)?; write_rewards(w, transaction_status.rewards.as_ref(), prefix)?;
} else { } else {
writeln!(w, "{}Status: Unavailable", prefix)?; writeln!(w, "{}Status: Unavailable", prefix)?;
@ -576,6 +578,25 @@ fn write_balances<W: io::Write>(
Ok(()) Ok(())
} }
fn write_return_data<W: io::Write>(
w: &mut W,
return_data: Option<&TransactionReturnData>,
prefix: &str,
) -> io::Result<()> {
if let Some(return_data) = return_data {
if !return_data.data.is_empty() {
use pretty_hex::*;
writeln!(
w,
"{}Return Data from Program {}:",
prefix, return_data.program_id
)?;
writeln!(w, "{} {:?}", prefix, return_data.data.hex_dump())?;
}
}
Ok(())
}
fn write_log_messages<W: io::Write>( fn write_log_messages<W: io::Write>(
w: &mut W, w: &mut W,
log_messages: Option<&Vec<String>>, log_messages: Option<&Vec<String>>,
@ -750,6 +771,10 @@ mod test {
commission: None, commission: None,
}]), }]),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: Some(TransactionReturnData {
program_id: Pubkey::new_from_array([2u8; 32]),
data: vec![1, 2, 3],
}),
}; };
let output = { let output = {
@ -786,6 +811,9 @@ Status: Ok
Account 1 balance: 0.00001 -> 0.0000099 Account 1 balance: 0.00001 -> 0.0000099
Log Messages: Log Messages:
Test message Test message
Return Data from Program 8qbHbw2BbbTHBW1sbeqakYXVKRQM8Ne7pLK7m6CVfeR:
Length: 3 (0x3) bytes
0000: 01 02 03 ...
Rewards: Rewards:
Address Type Amount New Balance \0 Address Type Amount New Balance \0
4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi rent -0.000000100 0.000009900 \0 4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi rent -0.000000100 0.000009900 \0
@ -820,6 +848,10 @@ Rewards:
commission: None, commission: None,
}]), }]),
loaded_addresses, loaded_addresses,
return_data: Some(TransactionReturnData {
program_id: Pubkey::new_from_array([2u8; 32]),
data: vec![1, 2, 3],
}),
}; };
let output = { let output = {
@ -865,6 +897,9 @@ Status: Ok
Account 3 balance: 0.00002 Account 3 balance: 0.00002
Log Messages: Log Messages:
Test message Test message
Return Data from Program 8qbHbw2BbbTHBW1sbeqakYXVKRQM8Ne7pLK7m6CVfeR:
Length: 3 (0x3) bytes
0000: 01 02 03 ...
Rewards: Rewards:
Address Type Amount New Balance \0 Address Type Amount New Balance \0
CktRuQ2mttgRGkXJtyksdKHjUdc2C4TgDzyB98oEzy8 rent -0.000000100 0.000014900 \0 CktRuQ2mttgRGkXJtyksdKHjUdc2C4TgDzyB98oEzy8 rent -0.000000100 0.000014900 \0

View File

@ -229,6 +229,7 @@ impl RpcSender for MockSender {
post_token_balances: None, post_token_balances: None,
rewards: None, rewards: None,
loaded_addresses: None, loaded_addresses: None,
return_data: None,
}), }),
}, },
block_time: Some(1628633791), block_time: Some(1628633791),
@ -340,6 +341,7 @@ impl RpcSender for MockSender {
logs: None, logs: None,
accounts: None, accounts: None,
units_consumed: None, units_consumed: None,
return_data: None,
}, },
})?, })?,
"getMinimumBalanceForRentExemption" => json![20], "getMinimumBalanceForRentExemption" => json![20],

View File

@ -7,6 +7,7 @@ use {
hash::Hash, hash::Hash,
inflation::Inflation, inflation::Inflation,
transaction::{Result, TransactionError}, transaction::{Result, TransactionError},
transaction_context::TransactionReturnData,
}, },
solana_transaction_status::{ solana_transaction_status::{
ConfirmedTransactionStatusWithSignature, TransactionConfirmationStatus, UiConfirmedBlock, ConfirmedTransactionStatusWithSignature, TransactionConfirmationStatus, UiConfirmedBlock,
@ -347,6 +348,7 @@ pub struct RpcSimulateTransactionResult {
pub logs: Option<Vec<String>>, pub logs: Option<Vec<String>>,
pub accounts: Option<Vec<Option<UiAccount>>>, pub accounts: Option<Vec<Option<UiAccount>>>,
pub units_consumed: Option<u64>, pub units_consumed: Option<u64>,
pub return_data: Option<TransactionReturnData>,
} }
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]

View File

@ -1180,6 +1180,7 @@ impl BankingStage {
MAX_PROCESSING_AGE, MAX_PROCESSING_AGE,
transaction_status_sender.is_some(), transaction_status_sender.is_some(),
transaction_status_sender.is_some(), transaction_status_sender.is_some(),
transaction_status_sender.is_some(),
&mut execute_and_commit_timings.execute_timings, &mut execute_and_commit_timings.execute_timings,
) )
}, },
@ -2149,6 +2150,7 @@ mod tests {
log_messages: None, log_messages: None,
inner_instructions: None, inner_instructions: None,
durable_nonce_fee: None, durable_nonce_fee: None,
return_data: None,
}) })
} }

View File

@ -1331,7 +1331,7 @@ fn load_blockstore(
blockstore.clone(), blockstore.clone(),
exit, exit,
enable_rpc_transaction_history, enable_rpc_transaction_history,
config.rpc_config.enable_cpi_and_log_storage, config.rpc_config.enable_extended_tx_metadata_storage,
transaction_notifier, transaction_notifier,
) )
} else { } else {
@ -1538,7 +1538,7 @@ fn initialize_rpc_transaction_history_services(
blockstore: Arc<Blockstore>, blockstore: Arc<Blockstore>,
exit: &Arc<AtomicBool>, exit: &Arc<AtomicBool>,
enable_rpc_transaction_history: bool, enable_rpc_transaction_history: bool,
enable_cpi_and_log_storage: bool, enable_extended_tx_metadata_storage: bool,
transaction_notifier: Option<TransactionNotifierLock>, transaction_notifier: Option<TransactionNotifierLock>,
) -> TransactionHistoryServices { ) -> TransactionHistoryServices {
let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root())); let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root()));
@ -1552,7 +1552,7 @@ fn initialize_rpc_transaction_history_services(
enable_rpc_transaction_history, enable_rpc_transaction_history,
transaction_notifier.clone(), transaction_notifier.clone(),
blockstore.clone(), blockstore.clone(),
enable_cpi_and_log_storage, enable_extended_tx_metadata_storage,
exit, exit,
)); ));

View File

@ -4697,6 +4697,7 @@ pub mod tests {
pubkey::Pubkey, pubkey::Pubkey,
signature::Signature, signature::Signature,
transaction::{Transaction, TransactionError}, transaction::{Transaction, TransactionError},
transaction_context::TransactionReturnData,
}, },
solana_storage_proto::convert::generated, solana_storage_proto::convert::generated,
solana_transaction_status::{InnerInstructions, Reward, Rewards, TransactionTokenBalance}, solana_transaction_status::{InnerInstructions, Reward, Rewards, TransactionTokenBalance},
@ -6858,6 +6859,7 @@ pub mod tests {
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: Some(TransactionReturnData::default()),
} }
.into(); .into();
blockstore blockstore
@ -6875,6 +6877,7 @@ pub mod tests {
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: Some(TransactionReturnData::default()),
} }
.into(); .into();
blockstore blockstore
@ -6892,6 +6895,7 @@ pub mod tests {
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: Some(TransactionReturnData::default()),
} }
.into(); .into();
blockstore blockstore
@ -6911,6 +6915,7 @@ pub mod tests {
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: Some(TransactionReturnData::default()),
}, },
} }
}) })
@ -7023,6 +7028,10 @@ pub mod tests {
writable: vec![Pubkey::new_unique()], writable: vec![Pubkey::new_unique()],
readonly: vec![Pubkey::new_unique()], readonly: vec![Pubkey::new_unique()],
}; };
let test_return_data = TransactionReturnData {
program_id: Pubkey::new_unique(),
data: vec![1, 2, 3],
};
// result not found // result not found
assert!(transaction_status_cf assert!(transaction_status_cf
@ -7042,6 +7051,7 @@ pub mod tests {
post_token_balances: Some(post_token_balances_vec.clone()), post_token_balances: Some(post_token_balances_vec.clone()),
rewards: Some(rewards_vec.clone()), rewards: Some(rewards_vec.clone()),
loaded_addresses: test_loaded_addresses.clone(), loaded_addresses: test_loaded_addresses.clone(),
return_data: Some(test_return_data.clone()),
} }
.into(); .into();
assert!(transaction_status_cf assert!(transaction_status_cf
@ -7060,6 +7070,7 @@ pub mod tests {
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses, loaded_addresses,
return_data,
} = transaction_status_cf } = transaction_status_cf
.get_protobuf_or_bincode::<StoredTransactionStatusMeta>((0, Signature::default(), 0)) .get_protobuf_or_bincode::<StoredTransactionStatusMeta>((0, Signature::default(), 0))
.unwrap() .unwrap()
@ -7076,6 +7087,7 @@ pub mod tests {
assert_eq!(post_token_balances.unwrap(), post_token_balances_vec); assert_eq!(post_token_balances.unwrap(), post_token_balances_vec);
assert_eq!(rewards.unwrap(), rewards_vec); assert_eq!(rewards.unwrap(), rewards_vec);
assert_eq!(loaded_addresses, test_loaded_addresses); assert_eq!(loaded_addresses, test_loaded_addresses);
assert_eq!(return_data.unwrap(), test_return_data);
// insert value // insert value
let status = TransactionStatusMeta { let status = TransactionStatusMeta {
@ -7089,6 +7101,7 @@ pub mod tests {
post_token_balances: Some(post_token_balances_vec.clone()), post_token_balances: Some(post_token_balances_vec.clone()),
rewards: Some(rewards_vec.clone()), rewards: Some(rewards_vec.clone()),
loaded_addresses: test_loaded_addresses.clone(), loaded_addresses: test_loaded_addresses.clone(),
return_data: Some(test_return_data.clone()),
} }
.into(); .into();
assert!(transaction_status_cf assert!(transaction_status_cf
@ -7107,6 +7120,7 @@ pub mod tests {
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses, loaded_addresses,
return_data,
} = transaction_status_cf } = transaction_status_cf
.get_protobuf_or_bincode::<StoredTransactionStatusMeta>(( .get_protobuf_or_bincode::<StoredTransactionStatusMeta>((
0, 0,
@ -7129,6 +7143,7 @@ pub mod tests {
assert_eq!(post_token_balances.unwrap(), post_token_balances_vec); assert_eq!(post_token_balances.unwrap(), post_token_balances_vec);
assert_eq!(rewards.unwrap(), rewards_vec); assert_eq!(rewards.unwrap(), rewards_vec);
assert_eq!(loaded_addresses, test_loaded_addresses); assert_eq!(loaded_addresses, test_loaded_addresses);
assert_eq!(return_data.unwrap(), test_return_data);
} }
#[test] #[test]
@ -7357,6 +7372,7 @@ pub mod tests {
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: Some(TransactionReturnData::default()),
} }
.into(); .into();
@ -7552,6 +7568,7 @@ pub mod tests {
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: Some(TransactionReturnData::default()),
} }
.into(); .into();
@ -7723,6 +7740,10 @@ pub mod tests {
let post_token_balances = Some(vec![]); let post_token_balances = Some(vec![]);
let rewards = Some(vec![]); let rewards = Some(vec![]);
let signature = transaction.signatures[0]; let signature = transaction.signatures[0];
let return_data = Some(TransactionReturnData {
program_id: Pubkey::new_unique(),
data: vec![1, 2, 3],
});
let status = TransactionStatusMeta { let status = TransactionStatusMeta {
status: Ok(()), status: Ok(()),
fee: 42, fee: 42,
@ -7734,6 +7755,7 @@ pub mod tests {
post_token_balances: post_token_balances.clone(), post_token_balances: post_token_balances.clone(),
rewards: rewards.clone(), rewards: rewards.clone(),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: return_data.clone(),
} }
.into(); .into();
blockstore blockstore
@ -7753,6 +7775,7 @@ pub mod tests {
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data,
}, },
} }
}) })
@ -7824,6 +7847,10 @@ pub mod tests {
let pre_token_balances = Some(vec![]); let pre_token_balances = Some(vec![]);
let post_token_balances = Some(vec![]); let post_token_balances = Some(vec![]);
let rewards = Some(vec![]); let rewards = Some(vec![]);
let return_data = Some(TransactionReturnData {
program_id: Pubkey::new_unique(),
data: vec![1, 2, 3],
});
let signature = transaction.signatures[0]; let signature = transaction.signatures[0];
let status = TransactionStatusMeta { let status = TransactionStatusMeta {
status: Ok(()), status: Ok(()),
@ -7836,6 +7863,7 @@ pub mod tests {
post_token_balances: post_token_balances.clone(), post_token_balances: post_token_balances.clone(),
rewards: rewards.clone(), rewards: rewards.clone(),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: return_data.clone(),
} }
.into(); .into();
blockstore blockstore
@ -7855,6 +7883,7 @@ pub mod tests {
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data,
}, },
} }
}) })
@ -8614,6 +8643,7 @@ pub mod tests {
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: Some(TransactionReturnData::default()),
} }
.into(); .into();
transaction_status_cf transaction_status_cf
@ -9171,6 +9201,10 @@ pub mod tests {
commission: None, commission: None,
}]), }]),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: Some(TransactionReturnData {
program_id: Pubkey::new_unique(),
data: vec![1, 2, 3],
}),
}; };
let deprecated_status: StoredTransactionStatusMeta = status.clone().try_into().unwrap(); let deprecated_status: StoredTransactionStatusMeta = status.clone().try_into().unwrap();
let protobuf_status: generated::TransactionStatusMeta = status.into(); let protobuf_status: generated::TransactionStatusMeta = status.into();

View File

@ -181,6 +181,7 @@ fn execute_batch(
transaction_status_sender.is_some(), transaction_status_sender.is_some(),
transaction_status_sender.is_some(), transaction_status_sender.is_some(),
transaction_status_sender.is_some(), transaction_status_sender.is_some(),
transaction_status_sender.is_some(),
timings, timings,
); );
@ -3510,6 +3511,7 @@ pub mod tests {
false, false,
false, false,
false, false,
false,
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),
); );
let (err, signature) = get_first_error(&batch, fee_collection_results).unwrap(); let (err, signature) = get_first_error(&batch, fee_collection_results).unwrap();

View File

@ -55,6 +55,9 @@ while [[ -n $1 ]]; do
elif [[ $1 = --enable-cpi-and-log-storage ]]; then elif [[ $1 = --enable-cpi-and-log-storage ]]; then
args+=("$1") args+=("$1")
shift shift
elif [[ $1 = --enable-extended-tx-metadata-storage ]]; then
args+=("$1")
shift
elif [[ $1 = --enable-rpc-bigtable-ledger-storage ]]; then elif [[ $1 = --enable-rpc-bigtable-ledger-storage ]]; then
args+=("$1") args+=("$1")
shift shift

View File

@ -141,6 +141,9 @@ while [[ -n $1 ]]; do
elif [[ $1 = --enable-cpi-and-log-storage ]]; then elif [[ $1 = --enable-cpi-and-log-storage ]]; then
args+=("$1") args+=("$1")
shift shift
elif [[ $1 = --enable-extended-tx-metadata-storage ]]; then
args+=("$1")
shift
elif [[ $1 = --skip-poh-verify ]]; then elif [[ $1 = --skip-poh-verify ]]; then
args+=("$1") args+=("$1")
shift shift

View File

@ -280,7 +280,7 @@ EOF
if $maybeFullRpc; then if $maybeFullRpc; then
args+=(--enable-rpc-transaction-history) args+=(--enable-rpc-transaction-history)
args+=(--enable-cpi-and-log-storage) args+=(--enable-extended-tx-metadata-storage)
fi fi
if [[ $airdropsEnabled = true ]]; then if [[ $airdropsEnabled = true ]]; then
@ -408,7 +408,7 @@ EOF
if $maybeFullRpc; then if $maybeFullRpc; then
args+=(--enable-rpc-transaction-history) args+=(--enable-rpc-transaction-history)
args+=(--enable-cpi-and-log-storage) args+=(--enable-extended-tx-metadata-storage)
fi fi
cat >> ~/solana/on-reboot <<EOF cat >> ~/solana/on-reboot <<EOF

View File

@ -8,6 +8,7 @@ repository = "https://github.com/solana-labs/solana"
version = "1.11.0" version = "1.11.0"
[dependencies] [dependencies]
assert_matches = "1.5.0"
async-trait = "0.1.52" async-trait = "0.1.52"
base64 = "0.13.0" base64 = "0.13.0"
bincode = "1.3.3" bincode = "1.3.3"

View File

@ -1,14 +1,19 @@
use { use {
assert_matches::assert_matches,
solana_banks_client::BanksClientError,
solana_program_test::{processor, ProgramTest}, solana_program_test::{processor, ProgramTest},
solana_sdk::{ solana_sdk::{
account_info::{next_account_info, AccountInfo}, account_info::{next_account_info, AccountInfo},
commitment_config::CommitmentLevel,
entrypoint::ProgramResult, entrypoint::ProgramResult,
instruction::{AccountMeta, Instruction}, instruction::{AccountMeta, Instruction},
msg, msg,
program::{get_return_data, invoke, set_return_data}, program::{get_return_data, invoke, set_return_data},
program_error::ProgramError,
pubkey::Pubkey, pubkey::Pubkey,
signature::Signer, signature::Signer,
transaction::Transaction, transaction::Transaction,
transaction_context::TransactionReturnData,
}, },
std::str::from_utf8, std::str::from_utf8,
}; };
@ -85,3 +90,55 @@ async fn return_data() {
.await .await
.unwrap(); .unwrap();
} }
// Process instruction to echo input back to another program
#[allow(clippy::unnecessary_wraps)]
fn error_set_return_data_process_instruction(
_program_id: &Pubkey,
_accounts: &[AccountInfo],
input: &[u8],
) -> ProgramResult {
set_return_data(input);
Err(ProgramError::InvalidInstructionData)
}
#[tokio::test]
async fn simulation_return_data() {
let error_set_return_data_program_id = Pubkey::new_unique();
let program_test = ProgramTest::new(
"error_set_return_data",
error_set_return_data_program_id,
processor!(error_set_return_data_process_instruction),
);
let mut context = program_test.start_with_context().await;
let expected_data = vec![240, 159, 166, 150];
let instructions = vec![Instruction {
program_id: error_set_return_data_program_id,
accounts: vec![],
data: expected_data.clone(),
}];
let transaction = Transaction::new_signed_with_payer(
&instructions,
Some(&context.payer.pubkey()),
&[&context.payer],
context.last_blockhash,
);
let error = context
.banks_client
.process_transaction_with_preflight_and_commitment(transaction, CommitmentLevel::Confirmed)
.await
.unwrap_err();
assert_matches!(
error,
BanksClientError::SimulationError {
return_data: Some(TransactionReturnData {
program_id,
data,
}),
..
} if program_id == error_set_return_data_program_id && data == expected_data
);
}

View File

@ -1862,6 +1862,12 @@ version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
[[package]]
name = "pretty-hex"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131"
[[package]] [[package]]
name = "proc-macro-crate" name = "proc-macro-crate"
version = "0.1.5" version = "0.1.5"
@ -3075,6 +3081,7 @@ dependencies = [
"console", "console",
"humantime", "humantime",
"indicatif", "indicatif",
"pretty-hex",
"serde", "serde",
"serde_json", "serde_json",
"solana-account-decoder", "solana-account-decoder",
@ -3419,6 +3426,7 @@ dependencies = [
name = "solana-program-test" name = "solana-program-test"
version = "1.11.0" version = "1.11.0"
dependencies = [ dependencies = [
"assert_matches",
"async-trait", "async-trait",
"base64 0.13.0", "base64 0.13.0",
"bincode", "bincode",

View File

@ -323,6 +323,7 @@ fn process_transaction_and_record_inner(
false, false,
true, true,
false, false,
false,
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),
) )
.0; .0;
@ -364,6 +365,7 @@ fn execute_transactions(
true, true,
true, true,
true, true,
true,
&mut timings, &mut timings,
); );
let tx_post_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals); let tx_post_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals);
@ -392,6 +394,7 @@ fn execute_transactions(
log_messages, log_messages,
inner_instructions, inner_instructions,
durable_nonce_fee, durable_nonce_fee,
return_data,
} = details; } = details;
let lamports_per_signature = match durable_nonce_fee { let lamports_per_signature = match durable_nonce_fee {
@ -432,6 +435,7 @@ fn execute_transactions(
log_messages, log_messages,
rewards: None, rewards: None,
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data,
}; };
Ok(ConfirmedTransactionWithStatusMeta { Ok(ConfirmedTransactionWithStatusMeta {

View File

@ -143,7 +143,7 @@ fn is_finalized(
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct JsonRpcConfig { pub struct JsonRpcConfig {
pub enable_rpc_transaction_history: bool, pub enable_rpc_transaction_history: bool,
pub enable_cpi_and_log_storage: bool, pub enable_extended_tx_metadata_storage: bool,
pub faucet_addr: Option<SocketAddr>, pub faucet_addr: Option<SocketAddr>,
pub health_check_slot_distance: u64, pub health_check_slot_distance: u64,
pub rpc_bigtable_config: Option<RpcBigtableConfig>, pub rpc_bigtable_config: Option<RpcBigtableConfig>,
@ -3551,6 +3551,7 @@ pub mod rpc_full {
logs, logs,
post_simulation_accounts: _, post_simulation_accounts: _,
units_consumed, units_consumed,
return_data,
} = preflight_bank.simulate_transaction(transaction) } = preflight_bank.simulate_transaction(transaction)
{ {
match err { match err {
@ -3568,6 +3569,7 @@ pub mod rpc_full {
logs: Some(logs), logs: Some(logs),
accounts: None, accounts: None,
units_consumed: Some(units_consumed), units_consumed: Some(units_consumed),
return_data,
}, },
} }
.into()); .into());
@ -3625,6 +3627,7 @@ pub mod rpc_full {
logs, logs,
post_simulation_accounts, post_simulation_accounts,
units_consumed, units_consumed,
return_data,
} = bank.simulate_transaction(transaction); } = bank.simulate_transaction(transaction);
let accounts = if let Some(config_accounts) = config.accounts { let accounts = if let Some(config_accounts) = config.accounts {
@ -3676,6 +3679,7 @@ pub mod rpc_full {
logs: Some(logs), logs: Some(logs),
accounts, accounts,
units_consumed: Some(units_consumed), units_consumed: Some(units_consumed),
return_data,
}, },
)) ))
} }
@ -5574,6 +5578,7 @@ pub mod tests {
"Program 11111111111111111111111111111111 invoke [1]", "Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success" "Program 11111111111111111111111111111111 success"
], ],
"returnData":null,
"unitsConsumed":0 "unitsConsumed":0
} }
}, },
@ -5660,6 +5665,7 @@ pub mod tests {
"Program 11111111111111111111111111111111 invoke [1]", "Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success" "Program 11111111111111111111111111111111 success"
], ],
"returnData":null,
"unitsConsumed":0 "unitsConsumed":0
} }
}, },
@ -5688,6 +5694,7 @@ pub mod tests {
"Program 11111111111111111111111111111111 invoke [1]", "Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success" "Program 11111111111111111111111111111111 success"
], ],
"returnData":null,
"unitsConsumed":0 "unitsConsumed":0
} }
}, },
@ -5737,6 +5744,7 @@ pub mod tests {
"err":"BlockhashNotFound", "err":"BlockhashNotFound",
"accounts":null, "accounts":null,
"logs":[], "logs":[],
"returnData":null,
"unitsConsumed":0 "unitsConsumed":0
} }
}, },
@ -5766,6 +5774,7 @@ pub mod tests {
"Program 11111111111111111111111111111111 invoke [1]", "Program 11111111111111111111111111111111 invoke [1]",
"Program 11111111111111111111111111111111 success" "Program 11111111111111111111111111111111 success"
], ],
"returnData":null,
"unitsConsumed":0 "unitsConsumed":0
} }
}, },
@ -6123,7 +6132,7 @@ pub mod tests {
assert_eq!( assert_eq!(
res, res,
Some( Some(
r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Blockhash not found","data":{"accounts":null,"err":"BlockhashNotFound","logs":[],"unitsConsumed":0}},"id":1}"#.to_string(), r#"{"jsonrpc":"2.0","error":{"code":-32002,"message":"Transaction simulation failed: Blockhash not found","data":{"accounts":null,"err":"BlockhashNotFound","logs":[],"returnData":null,"unitsConsumed":0}},"id":1}"#.to_string(),
) )
); );

View File

@ -34,7 +34,7 @@ impl TransactionStatusService {
enable_rpc_transaction_history: bool, enable_rpc_transaction_history: bool,
transaction_notifier: Option<TransactionNotifierLock>, transaction_notifier: Option<TransactionNotifierLock>,
blockstore: Arc<Blockstore>, blockstore: Arc<Blockstore>,
enable_cpi_and_log_storage: bool, enable_extended_tx_metadata_storage: bool,
exit: &Arc<AtomicBool>, exit: &Arc<AtomicBool>,
) -> Self { ) -> Self {
let exit = exit.clone(); let exit = exit.clone();
@ -51,7 +51,7 @@ impl TransactionStatusService {
enable_rpc_transaction_history, enable_rpc_transaction_history,
transaction_notifier.clone(), transaction_notifier.clone(),
&blockstore, &blockstore,
enable_cpi_and_log_storage, enable_extended_tx_metadata_storage,
) { ) {
break; break;
} }
@ -66,7 +66,7 @@ impl TransactionStatusService {
enable_rpc_transaction_history: bool, enable_rpc_transaction_history: bool,
transaction_notifier: Option<TransactionNotifierLock>, transaction_notifier: Option<TransactionNotifierLock>,
blockstore: &Arc<Blockstore>, blockstore: &Arc<Blockstore>,
enable_cpi_and_log_storage: bool, enable_extended_tx_metadata_storage: bool,
) -> Result<(), RecvTimeoutError> { ) -> Result<(), RecvTimeoutError> {
match write_transaction_status_receiver.recv_timeout(Duration::from_secs(1))? { match write_transaction_status_receiver.recv_timeout(Duration::from_secs(1))? {
TransactionStatusMessage::Batch(TransactionStatusBatch { TransactionStatusMessage::Batch(TransactionStatusBatch {
@ -101,6 +101,7 @@ impl TransactionStatusService {
log_messages, log_messages,
inner_instructions, inner_instructions,
durable_nonce_fee, durable_nonce_fee,
return_data,
} = details; } = details;
let lamports_per_signature = match durable_nonce_fee { let lamports_per_signature = match durable_nonce_fee {
Some(DurableNonceFee::Valid(lamports_per_signature)) => { Some(DurableNonceFee::Valid(lamports_per_signature)) => {
@ -156,6 +157,7 @@ impl TransactionStatusService {
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses, loaded_addresses,
return_data,
}; };
if let Some(transaction_notifier) = transaction_notifier.as_ref() { if let Some(transaction_notifier) = transaction_notifier.as_ref() {
@ -167,9 +169,11 @@ impl TransactionStatusService {
); );
} }
if !(enable_cpi_and_log_storage || transaction_notifier.is_some()) { if !(enable_extended_tx_metadata_storage || transaction_notifier.is_some())
{
transaction_status_meta.log_messages.take(); transaction_status_meta.log_messages.take();
transaction_status_meta.inner_instructions.take(); transaction_status_meta.inner_instructions.take();
transaction_status_meta.return_data.take();
} }
if enable_rpc_transaction_history { if enable_rpc_transaction_history {
@ -347,6 +351,7 @@ pub(crate) mod tests {
) )
.unwrap(), .unwrap(),
)), )),
return_data: None,
}); });
let balances = TransactionBalancesSet { let balances = TransactionBalancesSet {

View File

@ -1374,6 +1374,7 @@ mod tests {
log_messages: None, log_messages: None,
inner_instructions: None, inner_instructions: None,
durable_nonce_fee: nonce.map(DurableNonceFee::from), durable_nonce_fee: nonce.map(DurableNonceFee::from),
return_data: None,
}) })
} }

View File

@ -130,7 +130,10 @@ use {
MessageHash, Result, SanitizedTransaction, Transaction, TransactionError, MessageHash, Result, SanitizedTransaction, Transaction, TransactionError,
TransactionVerificationMode, VersionedTransaction, TransactionVerificationMode, VersionedTransaction,
}, },
transaction_context::{InstructionTrace, TransactionAccount, TransactionContext}, transaction_context::{
ExecutionRecord, InstructionTrace, TransactionAccount, TransactionContext,
TransactionReturnData,
},
}, },
solana_stake_program::stake_state::{ solana_stake_program::stake_state::{
self, InflationPointCalculationEvent, PointValue, StakeState, self, InflationPointCalculationEvent, PointValue, StakeState,
@ -579,6 +582,7 @@ pub struct TransactionExecutionDetails {
pub log_messages: Option<Vec<String>>, pub log_messages: Option<Vec<String>>,
pub inner_instructions: Option<InnerInstructionsList>, pub inner_instructions: Option<InnerInstructionsList>,
pub durable_nonce_fee: Option<DurableNonceFee>, pub durable_nonce_fee: Option<DurableNonceFee>,
pub return_data: Option<TransactionReturnData>,
} }
/// Type safe representation of a transaction execution attempt which /// Type safe representation of a transaction execution attempt which
@ -670,6 +674,7 @@ pub struct TransactionSimulationResult {
pub logs: TransactionLogMessages, pub logs: TransactionLogMessages,
pub post_simulation_accounts: Vec<TransactionAccount>, pub post_simulation_accounts: Vec<TransactionAccount>,
pub units_consumed: u64, pub units_consumed: u64,
pub return_data: Option<TransactionReturnData>,
} }
pub struct TransactionBalancesSet { pub struct TransactionBalancesSet {
pub pre_balances: TransactionBalances, pub pre_balances: TransactionBalances,
@ -3541,6 +3546,7 @@ impl Bank {
MAX_PROCESSING_AGE - MAX_TRANSACTION_FORWARDING_DELAY, MAX_PROCESSING_AGE - MAX_TRANSACTION_FORWARDING_DELAY,
false, false,
true, true,
true,
&mut timings, &mut timings,
); );
@ -3571,17 +3577,20 @@ impl Bank {
let execution_result = execution_results.pop().unwrap(); let execution_result = execution_results.pop().unwrap();
let flattened_result = execution_result.flattened_result(); let flattened_result = execution_result.flattened_result();
let logs = match execution_result { let (logs, return_data) = match execution_result {
TransactionExecutionResult::Executed(details) => details.log_messages, TransactionExecutionResult::Executed(details) => {
TransactionExecutionResult::NotExecuted(_) => None, (details.log_messages, details.return_data)
} }
.unwrap_or_default(); TransactionExecutionResult::NotExecuted(_) => (None, None),
};
let logs = logs.unwrap_or_default();
TransactionSimulationResult { TransactionSimulationResult {
result: flattened_result, result: flattened_result,
logs, logs,
post_simulation_accounts, post_simulation_accounts,
units_consumed, units_consumed,
return_data,
} }
} }
@ -3858,6 +3867,7 @@ impl Bank {
/// Execute a transaction using the provided loaded accounts and update /// Execute a transaction using the provided loaded accounts and update
/// the executors cache if the transaction was successful. /// the executors cache if the transaction was successful.
#[allow(clippy::too_many_arguments)]
fn execute_loaded_transaction( fn execute_loaded_transaction(
&self, &self,
tx: &SanitizedTransaction, tx: &SanitizedTransaction,
@ -3866,6 +3876,7 @@ impl Bank {
durable_nonce_fee: Option<DurableNonceFee>, durable_nonce_fee: Option<DurableNonceFee>,
enable_cpi_recording: bool, enable_cpi_recording: bool,
enable_log_recording: bool, enable_log_recording: bool,
enable_return_data_recording: bool,
timings: &mut ExecuteTimings, timings: &mut ExecuteTimings,
error_counters: &mut ErrorCounters, error_counters: &mut ErrorCounters,
) -> TransactionExecutionResult { ) -> TransactionExecutionResult {
@ -3960,7 +3971,11 @@ impl Bank {
.ok() .ok()
}); });
let (accounts, instruction_trace) = transaction_context.deconstruct(); let ExecutionRecord {
accounts,
instruction_trace,
mut return_data,
} = transaction_context.into();
loaded_transaction.accounts = accounts; loaded_transaction.accounts = accounts;
let inner_instructions = if enable_cpi_recording { let inner_instructions = if enable_cpi_recording {
@ -3971,11 +3986,25 @@ impl Bank {
None None
}; };
let return_data = if enable_return_data_recording {
if let Some(end_index) = return_data.data.iter().rposition(|&x| x != 0) {
let end_index = end_index.saturating_add(1);
error!("end index {}", end_index);
return_data.data.truncate(end_index);
Some(return_data)
} else {
None
}
} else {
None
};
TransactionExecutionResult::Executed(TransactionExecutionDetails { TransactionExecutionResult::Executed(TransactionExecutionDetails {
status, status,
log_messages, log_messages,
inner_instructions, inner_instructions,
durable_nonce_fee, durable_nonce_fee,
return_data,
}) })
} }
@ -3986,6 +4015,7 @@ impl Bank {
max_age: usize, max_age: usize,
enable_cpi_recording: bool, enable_cpi_recording: bool,
enable_log_recording: bool, enable_log_recording: bool,
enable_return_data_recording: bool,
timings: &mut ExecuteTimings, timings: &mut ExecuteTimings,
) -> LoadAndExecuteTransactionsOutput { ) -> LoadAndExecuteTransactionsOutput {
let sanitized_txs = batch.sanitized_transactions(); let sanitized_txs = batch.sanitized_transactions();
@ -4089,6 +4119,7 @@ impl Bank {
durable_nonce_fee, durable_nonce_fee,
enable_cpi_recording, enable_cpi_recording,
enable_log_recording, enable_log_recording,
enable_return_data_recording,
timings, timings,
&mut error_counters, &mut error_counters,
) )
@ -5240,6 +5271,7 @@ impl Bank {
collect_balances: bool, collect_balances: bool,
enable_cpi_recording: bool, enable_cpi_recording: bool,
enable_log_recording: bool, enable_log_recording: bool,
enable_return_data_recording: bool,
timings: &mut ExecuteTimings, timings: &mut ExecuteTimings,
) -> (TransactionResults, TransactionBalancesSet) { ) -> (TransactionResults, TransactionBalancesSet) {
let pre_balances = if collect_balances { let pre_balances = if collect_balances {
@ -5260,6 +5292,7 @@ impl Bank {
max_age, max_age,
enable_cpi_recording, enable_cpi_recording,
enable_log_recording, enable_log_recording,
enable_return_data_recording,
timings, timings,
); );
@ -5346,6 +5379,7 @@ impl Bank {
false, false,
false, false,
false, false,
false,
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),
) )
.0 .0
@ -6774,6 +6808,7 @@ pub(crate) mod tests {
message::{Message, MessageHeader}, message::{Message, MessageHeader},
nonce, nonce,
poh_config::PohConfig, poh_config::PohConfig,
program::MAX_RETURN_DATA,
rent::Rent, rent::Rent,
signature::{keypair_from_seed, Keypair, Signer}, signature::{keypair_from_seed, Keypair, Signer},
stake::{ stake::{
@ -6813,6 +6848,7 @@ pub(crate) mod tests {
log_messages: None, log_messages: None,
inner_instructions: None, inner_instructions: None,
durable_nonce_fee: nonce.map(DurableNonceFee::from), durable_nonce_fee: nonce.map(DurableNonceFee::from),
return_data: None,
}) })
} }
@ -9949,6 +9985,7 @@ pub(crate) mod tests {
false, false,
false, false,
false, false,
false,
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),
) )
.0 .0
@ -12482,6 +12519,7 @@ pub(crate) mod tests {
true, true,
false, false,
false, false,
false,
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),
); );
@ -15407,6 +15445,7 @@ pub(crate) mod tests {
false, false,
false, false,
true, true,
false,
&mut ExecuteTimings::default(), &mut ExecuteTimings::default(),
) )
.0 .0
@ -15449,6 +15488,91 @@ pub(crate) mod tests {
assert!(failure_log.contains(&"failed".to_string())); assert!(failure_log.contains(&"failed".to_string()));
} }
#[test]
fn test_tx_return_data() {
solana_logger::setup();
let GenesisConfigInfo {
genesis_config,
mint_keypair,
..
} = create_genesis_config_with_leader(
1_000_000_000_000_000,
&Pubkey::new_unique(),
bootstrap_validator_stake_lamports(),
);
let mut bank = Bank::new_for_tests(&genesis_config);
let mock_program_id = Pubkey::new(&[2u8; 32]);
fn mock_process_instruction(
_first_instruction_account: usize,
data: &[u8],
invoke_context: &mut InvokeContext,
) -> result::Result<(), InstructionError> {
let mock_program_id = Pubkey::new(&[2u8; 32]);
let transaction_context = &mut invoke_context.transaction_context;
let mut return_data = [0u8; MAX_RETURN_DATA];
if !data.is_empty() {
let index = usize::from_le_bytes(data.try_into().unwrap());
return_data[index] = 1;
transaction_context
.set_return_data(mock_program_id, return_data.to_vec())
.unwrap();
}
Ok(())
}
let blockhash = bank.last_blockhash();
bank.add_builtin("mock_program", &mock_program_id, mock_process_instruction);
for index in [
None,
Some(0),
Some(MAX_RETURN_DATA / 2),
Some(MAX_RETURN_DATA - 1),
] {
let data = if let Some(index) = index {
usize::to_le_bytes(index).to_vec()
} else {
Vec::new()
};
let txs = vec![Transaction::new_signed_with_payer(
&[Instruction {
program_id: mock_program_id,
data,
accounts: vec![AccountMeta::new(Pubkey::new_unique(), false)],
}],
Some(&mint_keypair.pubkey()),
&[&mint_keypair],
blockhash,
)];
let batch = bank.prepare_batch_for_tests(txs);
let return_data = bank
.load_execute_and_commit_transactions(
&batch,
MAX_PROCESSING_AGE,
false,
false,
false,
true,
&mut ExecuteTimings::default(),
)
.0
.execution_results[0]
.details()
.unwrap()
.return_data
.clone();
if let Some(index) = index {
let return_data = return_data.unwrap();
assert_eq!(return_data.program_id, mock_program_id);
let mut expected_data = vec![0u8; index];
expected_data.push(1u8);
assert_eq!(return_data.data, expected_data);
} else {
assert!(return_data.is_none());
}
}
}
#[test] #[test]
fn test_get_largest_accounts() { fn test_get_largest_accounts() {
let GenesisConfigInfo { genesis_config, .. } = let GenesisConfigInfo { genesis_config, .. } =

View File

@ -108,7 +108,7 @@ args=(
--rpc-faucet-address 127.0.0.1:9900 --rpc-faucet-address 127.0.0.1:9900
--log - --log -
--enable-rpc-transaction-history --enable-rpc-transaction-history
--enable-cpi-and-log-storage --enable-extended-tx-metadata-storage
--init-complete-file "$dataDir"/init-completed --init-complete-file "$dataDir"/init-completed
--snapshot-compression none --snapshot-compression none
--require-tower --require-tower

View File

@ -35,7 +35,7 @@ pub struct TransactionContext {
instruction_stack: Vec<usize>, instruction_stack: Vec<usize>,
number_of_instructions_at_transaction_level: usize, number_of_instructions_at_transaction_level: usize,
instruction_trace: InstructionTrace, instruction_trace: InstructionTrace,
return_data: (Pubkey, Vec<u8>), return_data: TransactionReturnData,
} }
impl TransactionContext { impl TransactionContext {
@ -57,25 +57,10 @@ impl TransactionContext {
instruction_stack: Vec::with_capacity(instruction_context_capacity), instruction_stack: Vec::with_capacity(instruction_context_capacity),
number_of_instructions_at_transaction_level, number_of_instructions_at_transaction_level,
instruction_trace: Vec::with_capacity(number_of_instructions_at_transaction_level), instruction_trace: Vec::with_capacity(number_of_instructions_at_transaction_level),
return_data: (Pubkey::default(), Vec::new()), return_data: TransactionReturnData::default(),
} }
} }
/// Used by the bank in the runtime to write back the processed accounts and recorded instructions
pub fn deconstruct(self) -> (Vec<TransactionAccount>, Vec<Vec<InstructionContext>>) {
(
Vec::from(Pin::into_inner(self.account_keys))
.into_iter()
.zip(
Vec::from(Pin::into_inner(self.accounts))
.into_iter()
.map(|account| account.into_inner()),
)
.collect(),
self.instruction_trace,
)
}
/// Used in mock_process_instruction /// Used in mock_process_instruction
pub fn deconstruct_without_keys(self) -> Result<Vec<AccountSharedData>, InstructionError> { pub fn deconstruct_without_keys(self) -> Result<Vec<AccountSharedData>, InstructionError> {
if !self.instruction_stack.is_empty() { if !self.instruction_stack.is_empty() {
@ -225,7 +210,7 @@ impl TransactionContext {
/// Gets the return data of the current InstructionContext or any above /// Gets the return data of the current InstructionContext or any above
pub fn get_return_data(&self) -> (&Pubkey, &[u8]) { pub fn get_return_data(&self) -> (&Pubkey, &[u8]) {
(&self.return_data.0, &self.return_data.1) (&self.return_data.program_id, &self.return_data.data)
} }
/// Set the return data of the current InstructionContext /// Set the return data of the current InstructionContext
@ -234,7 +219,7 @@ impl TransactionContext {
program_id: Pubkey, program_id: Pubkey,
data: Vec<u8>, data: Vec<u8>,
) -> Result<(), InstructionError> { ) -> Result<(), InstructionError> {
self.return_data = (program_id, data); self.return_data = TransactionReturnData { program_id, data };
Ok(()) Ok(())
} }
@ -254,6 +239,13 @@ impl TransactionContext {
} }
} }
/// Return data at the end of a transaction
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct TransactionReturnData {
pub program_id: Pubkey,
pub data: Vec<u8>,
}
/// List of (stack height, instruction) for each top-level instruction /// List of (stack height, instruction) for each top-level instruction
pub type InstructionTrace = Vec<Vec<InstructionContext>>; pub type InstructionTrace = Vec<Vec<InstructionContext>>;
@ -628,3 +620,27 @@ impl<'a> BorrowedAccount<'a> {
.unwrap_or_default() .unwrap_or_default()
} }
} }
/// Everything that needs to be recorded from a TransactionContext after execution
pub struct ExecutionRecord {
pub accounts: Vec<TransactionAccount>,
pub instruction_trace: InstructionTrace,
pub return_data: TransactionReturnData,
}
/// Used by the bank in the runtime to write back the processed accounts and recorded instructions
impl From<TransactionContext> for ExecutionRecord {
fn from(context: TransactionContext) -> Self {
Self {
accounts: Vec::from(Pin::into_inner(context.account_keys))
.into_iter()
.zip(
Vec::from(Pin::into_inner(context.accounts))
.into_iter()
.map(|account| account.into_inner()),
)
.collect(),
instruction_trace: context.instruction_trace,
return_data: context.return_data,
}
}
}

View File

@ -792,7 +792,7 @@ mod tests {
prost::Message, prost::Message,
solana_sdk::{ solana_sdk::{
hash::Hash, message::v0::LoadedAddresses, signature::Keypair, system_transaction, hash::Hash, message::v0::LoadedAddresses, signature::Keypair, system_transaction,
transaction::VersionedTransaction, transaction::VersionedTransaction, transaction_context::TransactionReturnData,
}, },
solana_storage_proto::convert::generated, solana_storage_proto::convert::generated,
solana_transaction_status::{ solana_transaction_status::{
@ -842,6 +842,7 @@ mod tests {
post_token_balances: Some(vec![]), post_token_balances: Some(vec![]),
rewards: Some(vec![]), rewards: Some(vec![]),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: Some(TransactionReturnData::default()),
}, },
}); });
let expected_block = ConfirmedBlock { let expected_block = ConfirmedBlock {
@ -899,6 +900,7 @@ mod tests {
meta.pre_token_balances = None; // Legacy bincode implementation does not support token balances meta.pre_token_balances = None; // Legacy bincode implementation does not support token balances
meta.post_token_balances = None; // Legacy bincode implementation does not support token balances meta.post_token_balances = None; // Legacy bincode implementation does not support token balances
meta.rewards = None; // Legacy bincode implementation does not support rewards meta.rewards = None; // Legacy bincode implementation does not support rewards
meta.return_data = None; // Legacy bincode implementation does not support return data
} }
assert_eq!(block, bincode_block.into()); assert_eq!(block, bincode_block.into());
} else { } else {

View File

@ -237,6 +237,7 @@ impl From<StoredConfirmedBlockTransactionStatusMeta> for TransactionStatusMeta {
post_token_balances: None, post_token_balances: None,
rewards: None, rewards: None,
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: None,
} }
} }
} }

View File

@ -57,6 +57,8 @@ message TransactionStatusMeta {
repeated Reward rewards = 9; repeated Reward rewards = 9;
repeated bytes loaded_writable_addresses = 12; repeated bytes loaded_writable_addresses = 12;
repeated bytes loaded_readonly_addresses = 13; repeated bytes loaded_readonly_addresses = 13;
ReturnData return_data = 14;
bool return_data_none = 15;
} }
message TransactionError { message TransactionError {
@ -88,6 +90,11 @@ message UiTokenAmount {
string ui_amount_string = 4; string ui_amount_string = 4;
} }
message ReturnData {
bytes program_id = 1;
bytes data = 2;
}
enum RewardType { enum RewardType {
Unspecified = 0; Unspecified = 0;
Fee = 1; Fee = 1;

View File

@ -12,6 +12,7 @@ use {
pubkey::Pubkey, pubkey::Pubkey,
signature::Signature, signature::Signature,
transaction::{Transaction, TransactionError, VersionedTransaction}, transaction::{Transaction, TransactionError, VersionedTransaction},
transaction_context::TransactionReturnData,
}, },
solana_transaction_status::{ solana_transaction_status::{
ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionByAddrInfo, ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionByAddrInfo,
@ -363,6 +364,7 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses, loaded_addresses,
return_data,
} = value; } = value;
let err = match status { let err = match status {
Ok(()) => None, Ok(()) => None,
@ -403,6 +405,8 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
.into_iter() .into_iter()
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(&key).into()) .map(|key| <Pubkey as AsRef<[u8]>>::as_ref(&key).into())
.collect(); .collect();
let return_data_none = return_data.is_none();
let return_data = return_data.map(|return_data| return_data.into());
Self { Self {
err, err,
@ -418,6 +422,8 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
rewards, rewards,
loaded_writable_addresses, loaded_writable_addresses,
loaded_readonly_addresses, loaded_readonly_addresses,
return_data,
return_data_none,
} }
} }
} }
@ -447,6 +453,8 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
rewards, rewards,
loaded_writable_addresses, loaded_writable_addresses,
loaded_readonly_addresses, loaded_readonly_addresses,
return_data,
return_data_none,
} = value; } = value;
let status = match &err { let status = match &err {
None => Ok(()), None => Ok(()),
@ -490,6 +498,11 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
.map(|key| Pubkey::new(&key)) .map(|key| Pubkey::new(&key))
.collect(), .collect(),
}; };
let return_data = if return_data_none {
None
} else {
return_data.map(|return_data| return_data.into())
};
Ok(Self { Ok(Self {
status, status,
fee, fee,
@ -501,6 +514,7 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses, loaded_addresses,
return_data,
}) })
} }
} }
@ -587,6 +601,24 @@ impl From<generated::MessageAddressTableLookup> for MessageAddressTableLookup {
} }
} }
impl From<TransactionReturnData> for generated::ReturnData {
fn from(value: TransactionReturnData) -> Self {
Self {
program_id: <Pubkey as AsRef<[u8]>>::as_ref(&value.program_id).into(),
data: value.data,
}
}
}
impl From<generated::ReturnData> for TransactionReturnData {
fn from(value: generated::ReturnData) -> Self {
Self {
program_id: Pubkey::new(&value.program_id),
data: value.data,
}
}
}
impl From<CompiledInstruction> for generated::CompiledInstruction { impl From<CompiledInstruction> for generated::CompiledInstruction {
fn from(value: CompiledInstruction) -> Self { fn from(value: CompiledInstruction) -> Self {
Self { Self {

View File

@ -6,6 +6,7 @@ use {
}, },
solana_sdk::{ solana_sdk::{
deserialize_utils::default_on_eof, message::v0::LoadedAddresses, transaction::Result, deserialize_utils::default_on_eof, message::v0::LoadedAddresses, transaction::Result,
transaction_context::TransactionReturnData,
}, },
solana_transaction_status::{ solana_transaction_status::{
InnerInstructions, Reward, RewardType, TransactionStatusMeta, TransactionTokenBalance, InnerInstructions, Reward, RewardType, TransactionStatusMeta, TransactionTokenBalance,
@ -167,6 +168,8 @@ pub struct StoredTransactionStatusMeta {
pub post_token_balances: Option<Vec<StoredTransactionTokenBalance>>, pub post_token_balances: Option<Vec<StoredTransactionTokenBalance>>,
#[serde(deserialize_with = "default_on_eof")] #[serde(deserialize_with = "default_on_eof")]
pub rewards: Option<Vec<StoredExtendedReward>>, pub rewards: Option<Vec<StoredExtendedReward>>,
#[serde(deserialize_with = "default_on_eof")]
pub return_data: Option<TransactionReturnData>,
} }
impl From<StoredTransactionStatusMeta> for TransactionStatusMeta { impl From<StoredTransactionStatusMeta> for TransactionStatusMeta {
@ -181,6 +184,7 @@ impl From<StoredTransactionStatusMeta> for TransactionStatusMeta {
pre_token_balances, pre_token_balances,
post_token_balances, post_token_balances,
rewards, rewards,
return_data,
} = value; } = value;
Self { Self {
status, status,
@ -196,6 +200,7 @@ impl From<StoredTransactionStatusMeta> for TransactionStatusMeta {
rewards: rewards rewards: rewards
.map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()), .map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()),
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data,
} }
} }
} }
@ -214,6 +219,7 @@ impl TryFrom<TransactionStatusMeta> for StoredTransactionStatusMeta {
post_token_balances, post_token_balances,
rewards, rewards,
loaded_addresses, loaded_addresses,
return_data,
} = value; } = value;
if !loaded_addresses.is_empty() { if !loaded_addresses.is_empty() {
@ -237,6 +243,7 @@ impl TryFrom<TransactionStatusMeta> for StoredTransactionStatusMeta {
.map(|balances| balances.into_iter().map(|balance| balance.into()).collect()), .map(|balances| balances.into_iter().map(|balance| balance.into()).collect()),
rewards: rewards rewards: rewards
.map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()), .map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()),
return_data,
}) })
} }
} }

View File

@ -22,6 +22,7 @@ use {
Result as TransactionResult, Transaction, TransactionError, TransactionVersion, Result as TransactionResult, Transaction, TransactionError, TransactionVersion,
VersionedTransaction, VersionedTransaction,
}, },
transaction_context::TransactionReturnData,
}, },
std::fmt, std::fmt,
thiserror::Error, thiserror::Error,
@ -279,6 +280,7 @@ pub struct TransactionStatusMeta {
pub post_token_balances: Option<Vec<TransactionTokenBalance>>, pub post_token_balances: Option<Vec<TransactionTokenBalance>>,
pub rewards: Option<Rewards>, pub rewards: Option<Rewards>,
pub loaded_addresses: LoadedAddresses, pub loaded_addresses: LoadedAddresses,
pub return_data: Option<TransactionReturnData>,
} }
impl Default for TransactionStatusMeta { impl Default for TransactionStatusMeta {
@ -294,6 +296,7 @@ impl Default for TransactionStatusMeta {
post_token_balances: None, post_token_balances: None,
rewards: None, rewards: None,
loaded_addresses: LoadedAddresses::default(), loaded_addresses: LoadedAddresses::default(),
return_data: None,
} }
} }
} }
@ -314,6 +317,7 @@ pub struct UiTransactionStatusMeta {
pub rewards: Option<Rewards>, pub rewards: Option<Rewards>,
#[serde(default, skip_serializing_if = "Option::is_none")] #[serde(default, skip_serializing_if = "Option::is_none")]
pub loaded_addresses: Option<UiLoadedAddresses>, pub loaded_addresses: Option<UiLoadedAddresses>,
pub return_data: Option<TransactionReturnData>,
} }
/// A duplicate representation of LoadedAddresses /// A duplicate representation of LoadedAddresses
@ -364,6 +368,7 @@ impl UiTransactionStatusMeta {
.map(|balance| balance.into_iter().map(Into::into).collect()), .map(|balance| balance.into_iter().map(Into::into).collect()),
rewards: meta.rewards, rewards: meta.rewards,
loaded_addresses: Some(UiLoadedAddresses::from(&meta.loaded_addresses)), loaded_addresses: Some(UiLoadedAddresses::from(&meta.loaded_addresses)),
return_data: meta.return_data,
} }
} }
} }
@ -388,6 +393,7 @@ impl From<TransactionStatusMeta> for UiTransactionStatusMeta {
.map(|balance| balance.into_iter().map(Into::into).collect()), .map(|balance| balance.into_iter().map(Into::into).collect()),
rewards: meta.rewards, rewards: meta.rewards,
loaded_addresses: Some(UiLoadedAddresses::from(&meta.loaded_addresses)), loaded_addresses: Some(UiLoadedAddresses::from(&meta.loaded_addresses)),
return_data: meta.return_data,
} }
} }
} }

View File

@ -657,7 +657,7 @@ fn main() {
) )
.rpc_config(JsonRpcConfig { .rpc_config(JsonRpcConfig {
enable_rpc_transaction_history: true, enable_rpc_transaction_history: true,
enable_cpi_and_log_storage: true, enable_extended_tx_metadata_storage: true,
rpc_bigtable_config, rpc_bigtable_config,
faucet_addr, faucet_addr,
..JsonRpcConfig::default_for_test() ..JsonRpcConfig::default_for_test()

View File

@ -656,8 +656,18 @@ pub fn main() {
.long("enable-cpi-and-log-storage") .long("enable-cpi-and-log-storage")
.requires("enable_rpc_transaction_history") .requires("enable_rpc_transaction_history")
.takes_value(false) .takes_value(false)
.help("Include CPI inner instructions and logs in the \ .hidden(true)
historical transaction info stored"), .help("Deprecated, please use \"enable-extended-tx-metadata-storage\". \
Include CPI inner instructions, logs and return data in \
the historical transaction info stored"),
)
.arg(
Arg::with_name("enable_extended_tx_metadata_storage")
.long("enable-extended-tx-metadata-storage")
.requires("enable_rpc_transaction_history")
.takes_value(false)
.help("Include CPI inner instructions, logs, and return data in \
the historical transaction info stored"),
) )
.arg( .arg(
Arg::with_name("rpc_max_multiple_accounts") Arg::with_name("rpc_max_multiple_accounts")
@ -2294,7 +2304,15 @@ pub fn main() {
}; };
if matches.is_present("minimal_rpc_api") { if matches.is_present("minimal_rpc_api") {
warn!("--minimal-rpc-api is now the default behavior. This flag is deprecated and can be removed from the launch args") warn!("--minimal-rpc-api is now the default behavior. This flag is deprecated and can be removed from the launch args");
}
if matches.is_present("enable_cpi_and_log_storage") {
warn!(
"--enable-cpi-and-log-storage is deprecated. Please update the \
launch args to use --enable-extended-tx-metadata-storage and remove \
--enable-cpi-and-log-storage"
);
} }
let rpc_bigtable_config = if matches.is_present("enable_rpc_bigtable_ledger_storage") let rpc_bigtable_config = if matches.is_present("enable_rpc_bigtable_ledger_storage")
@ -2325,7 +2343,8 @@ pub fn main() {
new_hard_forks: hardforks_of(&matches, "hard_forks"), new_hard_forks: hardforks_of(&matches, "hard_forks"),
rpc_config: JsonRpcConfig { rpc_config: JsonRpcConfig {
enable_rpc_transaction_history: matches.is_present("enable_rpc_transaction_history"), enable_rpc_transaction_history: matches.is_present("enable_rpc_transaction_history"),
enable_cpi_and_log_storage: matches.is_present("enable_cpi_and_log_storage"), enable_extended_tx_metadata_storage: matches.is_present("enable_cpi_and_log_storage")
|| matches.is_present("enable_extended_tx_metadata_storage"),
rpc_bigtable_config, rpc_bigtable_config,
faucet_addr: matches.value_of("rpc_faucet_addr").map(|address| { faucet_addr: matches.value_of("rpc_faucet_addr").map(|address| {
solana_net_utils::parse_host_port(address).expect("failed to parse faucet address") solana_net_utils::parse_host_port(address).expect("failed to parse faucet address")