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:
parent
359e2de090
commit
7af48465fa
|
@ -4424,6 +4424,7 @@ dependencies = [
|
|||
"ed25519-dalek",
|
||||
"humantime",
|
||||
"indicatif",
|
||||
"pretty-hex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"solana-account-decoder",
|
||||
|
@ -5309,6 +5310,7 @@ dependencies = [
|
|||
name = "solana-program-test"
|
||||
version = "1.11.0"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"async-trait",
|
||||
"base64 0.13.0",
|
||||
"bincode",
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use {
|
||||
solana_sdk::{transaction::TransactionError, transport::TransportError},
|
||||
solana_sdk::{
|
||||
transaction::TransactionError, transaction_context::TransactionReturnData,
|
||||
transport::TransportError,
|
||||
},
|
||||
std::io,
|
||||
tarpc::client::RpcError,
|
||||
thiserror::Error,
|
||||
|
@ -25,6 +28,7 @@ pub enum BanksClientError {
|
|||
err: TransactionError,
|
||||
logs: Vec<String>,
|
||||
units_consumed: u64,
|
||||
return_data: Option<TransactionReturnData>,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -247,6 +247,7 @@ impl BanksClient {
|
|||
err,
|
||||
logs: simulation_details.logs,
|
||||
units_consumed: simulation_details.units_consumed,
|
||||
return_data: simulation_details.return_data,
|
||||
}),
|
||||
BanksTransactionResultWithSimulation {
|
||||
result: Some(result),
|
||||
|
|
|
@ -12,6 +12,7 @@ use {
|
|||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
transaction::{self, Transaction, TransactionError},
|
||||
transaction_context::TransactionReturnData,
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -35,6 +36,7 @@ pub struct TransactionStatus {
|
|||
pub struct TransactionSimulationDetails {
|
||||
pub logs: Vec<String>,
|
||||
pub units_consumed: u64,
|
||||
pub return_data: Option<TransactionReturnData>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
|
|
|
@ -266,6 +266,7 @@ impl Banks for BanksServer {
|
|||
logs,
|
||||
post_simulation_accounts: _,
|
||||
units_consumed,
|
||||
return_data,
|
||||
} = self
|
||||
.bank(commitment)
|
||||
.simulate_transaction_unchecked(sanitized_transaction)
|
||||
|
@ -275,6 +276,7 @@ impl Banks for BanksServer {
|
|||
simulation_details: Some(TransactionSimulationDetails {
|
||||
logs,
|
||||
units_consumed,
|
||||
return_data,
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ clap = "2.33.0"
|
|||
console = "0.15.0"
|
||||
humantime = "2.0.1"
|
||||
indicatif = "0.16.2"
|
||||
pretty-hex = "0.2.1"
|
||||
serde = "1.0.136"
|
||||
serde_json = "1.0.79"
|
||||
solana-account-decoder = { path = "../account-decoder", version = "=1.11.0" }
|
||||
|
|
|
@ -14,6 +14,7 @@ use {
|
|||
signature::Signature,
|
||||
stake,
|
||||
transaction::{TransactionError, TransactionVersion, VersionedTransaction},
|
||||
transaction_context::TransactionReturnData,
|
||||
},
|
||||
solana_transaction_status::{Rewards, UiTransactionStatusMeta},
|
||||
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_balances(w, transaction_status, 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)?;
|
||||
} else {
|
||||
writeln!(w, "{}Status: Unavailable", prefix)?;
|
||||
|
@ -576,6 +578,25 @@ fn write_balances<W: io::Write>(
|
|||
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>(
|
||||
w: &mut W,
|
||||
log_messages: Option<&Vec<String>>,
|
||||
|
@ -750,6 +771,10 @@ mod test {
|
|||
commission: None,
|
||||
}]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data: Some(TransactionReturnData {
|
||||
program_id: Pubkey::new_from_array([2u8; 32]),
|
||||
data: vec![1, 2, 3],
|
||||
}),
|
||||
};
|
||||
|
||||
let output = {
|
||||
|
@ -786,6 +811,9 @@ Status: Ok
|
|||
Account 1 balance: ◎0.00001 -> ◎0.0000099
|
||||
Log Messages:
|
||||
Test message
|
||||
Return Data from Program 8qbHbw2BbbTHBW1sbeqakYXVKRQM8Ne7pLK7m6CVfeR:
|
||||
Length: 3 (0x3) bytes
|
||||
0000: 01 02 03 ...
|
||||
Rewards:
|
||||
Address Type Amount New Balance \0
|
||||
4vJ9JU1bJJE96FWSJKvHsmmFADCg4gpZQff4P3bkLKi rent -◎0.000000100 ◎0.000009900 \0
|
||||
|
@ -820,6 +848,10 @@ Rewards:
|
|||
commission: None,
|
||||
}]),
|
||||
loaded_addresses,
|
||||
return_data: Some(TransactionReturnData {
|
||||
program_id: Pubkey::new_from_array([2u8; 32]),
|
||||
data: vec![1, 2, 3],
|
||||
}),
|
||||
};
|
||||
|
||||
let output = {
|
||||
|
@ -865,6 +897,9 @@ Status: Ok
|
|||
Account 3 balance: ◎0.00002
|
||||
Log Messages:
|
||||
Test message
|
||||
Return Data from Program 8qbHbw2BbbTHBW1sbeqakYXVKRQM8Ne7pLK7m6CVfeR:
|
||||
Length: 3 (0x3) bytes
|
||||
0000: 01 02 03 ...
|
||||
Rewards:
|
||||
Address Type Amount New Balance \0
|
||||
CktRuQ2mttgRGkXJtyksdKHjUdc2C4TgDzyB98oEzy8 rent -◎0.000000100 ◎0.000014900 \0
|
||||
|
|
|
@ -229,6 +229,7 @@ impl RpcSender for MockSender {
|
|||
post_token_balances: None,
|
||||
rewards: None,
|
||||
loaded_addresses: None,
|
||||
return_data: None,
|
||||
}),
|
||||
},
|
||||
block_time: Some(1628633791),
|
||||
|
@ -340,6 +341,7 @@ impl RpcSender for MockSender {
|
|||
logs: None,
|
||||
accounts: None,
|
||||
units_consumed: None,
|
||||
return_data: None,
|
||||
},
|
||||
})?,
|
||||
"getMinimumBalanceForRentExemption" => json![20],
|
||||
|
|
|
@ -7,6 +7,7 @@ use {
|
|||
hash::Hash,
|
||||
inflation::Inflation,
|
||||
transaction::{Result, TransactionError},
|
||||
transaction_context::TransactionReturnData,
|
||||
},
|
||||
solana_transaction_status::{
|
||||
ConfirmedTransactionStatusWithSignature, TransactionConfirmationStatus, UiConfirmedBlock,
|
||||
|
@ -347,6 +348,7 @@ pub struct RpcSimulateTransactionResult {
|
|||
pub logs: Option<Vec<String>>,
|
||||
pub accounts: Option<Vec<Option<UiAccount>>>,
|
||||
pub units_consumed: Option<u64>,
|
||||
pub return_data: Option<TransactionReturnData>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
|
|
|
@ -1180,6 +1180,7 @@ impl BankingStage {
|
|||
MAX_PROCESSING_AGE,
|
||||
transaction_status_sender.is_some(),
|
||||
transaction_status_sender.is_some(),
|
||||
transaction_status_sender.is_some(),
|
||||
&mut execute_and_commit_timings.execute_timings,
|
||||
)
|
||||
},
|
||||
|
@ -2149,6 +2150,7 @@ mod tests {
|
|||
log_messages: None,
|
||||
inner_instructions: None,
|
||||
durable_nonce_fee: None,
|
||||
return_data: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -1331,7 +1331,7 @@ fn load_blockstore(
|
|||
blockstore.clone(),
|
||||
exit,
|
||||
enable_rpc_transaction_history,
|
||||
config.rpc_config.enable_cpi_and_log_storage,
|
||||
config.rpc_config.enable_extended_tx_metadata_storage,
|
||||
transaction_notifier,
|
||||
)
|
||||
} else {
|
||||
|
@ -1538,7 +1538,7 @@ fn initialize_rpc_transaction_history_services(
|
|||
blockstore: Arc<Blockstore>,
|
||||
exit: &Arc<AtomicBool>,
|
||||
enable_rpc_transaction_history: bool,
|
||||
enable_cpi_and_log_storage: bool,
|
||||
enable_extended_tx_metadata_storage: bool,
|
||||
transaction_notifier: Option<TransactionNotifierLock>,
|
||||
) -> TransactionHistoryServices {
|
||||
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,
|
||||
transaction_notifier.clone(),
|
||||
blockstore.clone(),
|
||||
enable_cpi_and_log_storage,
|
||||
enable_extended_tx_metadata_storage,
|
||||
exit,
|
||||
));
|
||||
|
||||
|
|
|
@ -4697,6 +4697,7 @@ pub mod tests {
|
|||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
transaction::{Transaction, TransactionError},
|
||||
transaction_context::TransactionReturnData,
|
||||
},
|
||||
solana_storage_proto::convert::generated,
|
||||
solana_transaction_status::{InnerInstructions, Reward, Rewards, TransactionTokenBalance},
|
||||
|
@ -6858,6 +6859,7 @@ pub mod tests {
|
|||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data: Some(TransactionReturnData::default()),
|
||||
}
|
||||
.into();
|
||||
blockstore
|
||||
|
@ -6875,6 +6877,7 @@ pub mod tests {
|
|||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data: Some(TransactionReturnData::default()),
|
||||
}
|
||||
.into();
|
||||
blockstore
|
||||
|
@ -6892,6 +6895,7 @@ pub mod tests {
|
|||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data: Some(TransactionReturnData::default()),
|
||||
}
|
||||
.into();
|
||||
blockstore
|
||||
|
@ -6911,6 +6915,7 @@ pub mod tests {
|
|||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data: Some(TransactionReturnData::default()),
|
||||
},
|
||||
}
|
||||
})
|
||||
|
@ -7023,6 +7028,10 @@ pub mod tests {
|
|||
writable: 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
|
||||
assert!(transaction_status_cf
|
||||
|
@ -7042,6 +7051,7 @@ pub mod tests {
|
|||
post_token_balances: Some(post_token_balances_vec.clone()),
|
||||
rewards: Some(rewards_vec.clone()),
|
||||
loaded_addresses: test_loaded_addresses.clone(),
|
||||
return_data: Some(test_return_data.clone()),
|
||||
}
|
||||
.into();
|
||||
assert!(transaction_status_cf
|
||||
|
@ -7060,6 +7070,7 @@ pub mod tests {
|
|||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses,
|
||||
return_data,
|
||||
} = transaction_status_cf
|
||||
.get_protobuf_or_bincode::<StoredTransactionStatusMeta>((0, Signature::default(), 0))
|
||||
.unwrap()
|
||||
|
@ -7076,6 +7087,7 @@ pub mod tests {
|
|||
assert_eq!(post_token_balances.unwrap(), post_token_balances_vec);
|
||||
assert_eq!(rewards.unwrap(), rewards_vec);
|
||||
assert_eq!(loaded_addresses, test_loaded_addresses);
|
||||
assert_eq!(return_data.unwrap(), test_return_data);
|
||||
|
||||
// insert value
|
||||
let status = TransactionStatusMeta {
|
||||
|
@ -7089,6 +7101,7 @@ pub mod tests {
|
|||
post_token_balances: Some(post_token_balances_vec.clone()),
|
||||
rewards: Some(rewards_vec.clone()),
|
||||
loaded_addresses: test_loaded_addresses.clone(),
|
||||
return_data: Some(test_return_data.clone()),
|
||||
}
|
||||
.into();
|
||||
assert!(transaction_status_cf
|
||||
|
@ -7107,6 +7120,7 @@ pub mod tests {
|
|||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses,
|
||||
return_data,
|
||||
} = transaction_status_cf
|
||||
.get_protobuf_or_bincode::<StoredTransactionStatusMeta>((
|
||||
0,
|
||||
|
@ -7129,6 +7143,7 @@ pub mod tests {
|
|||
assert_eq!(post_token_balances.unwrap(), post_token_balances_vec);
|
||||
assert_eq!(rewards.unwrap(), rewards_vec);
|
||||
assert_eq!(loaded_addresses, test_loaded_addresses);
|
||||
assert_eq!(return_data.unwrap(), test_return_data);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -7357,6 +7372,7 @@ pub mod tests {
|
|||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data: Some(TransactionReturnData::default()),
|
||||
}
|
||||
.into();
|
||||
|
||||
|
@ -7552,6 +7568,7 @@ pub mod tests {
|
|||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data: Some(TransactionReturnData::default()),
|
||||
}
|
||||
.into();
|
||||
|
||||
|
@ -7723,6 +7740,10 @@ pub mod tests {
|
|||
let post_token_balances = Some(vec![]);
|
||||
let rewards = Some(vec![]);
|
||||
let signature = transaction.signatures[0];
|
||||
let return_data = Some(TransactionReturnData {
|
||||
program_id: Pubkey::new_unique(),
|
||||
data: vec![1, 2, 3],
|
||||
});
|
||||
let status = TransactionStatusMeta {
|
||||
status: Ok(()),
|
||||
fee: 42,
|
||||
|
@ -7734,6 +7755,7 @@ pub mod tests {
|
|||
post_token_balances: post_token_balances.clone(),
|
||||
rewards: rewards.clone(),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data: return_data.clone(),
|
||||
}
|
||||
.into();
|
||||
blockstore
|
||||
|
@ -7753,6 +7775,7 @@ pub mod tests {
|
|||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
@ -7824,6 +7847,10 @@ pub mod tests {
|
|||
let pre_token_balances = Some(vec![]);
|
||||
let post_token_balances = 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 status = TransactionStatusMeta {
|
||||
status: Ok(()),
|
||||
|
@ -7836,6 +7863,7 @@ pub mod tests {
|
|||
post_token_balances: post_token_balances.clone(),
|
||||
rewards: rewards.clone(),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data: return_data.clone(),
|
||||
}
|
||||
.into();
|
||||
blockstore
|
||||
|
@ -7855,6 +7883,7 @@ pub mod tests {
|
|||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
@ -8614,6 +8643,7 @@ pub mod tests {
|
|||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data: Some(TransactionReturnData::default()),
|
||||
}
|
||||
.into();
|
||||
transaction_status_cf
|
||||
|
@ -9171,6 +9201,10 @@ pub mod tests {
|
|||
commission: None,
|
||||
}]),
|
||||
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 protobuf_status: generated::TransactionStatusMeta = status.into();
|
||||
|
|
|
@ -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(),
|
||||
timings,
|
||||
);
|
||||
|
||||
|
@ -3510,6 +3511,7 @@ pub mod tests {
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
&mut ExecuteTimings::default(),
|
||||
);
|
||||
let (err, signature) = get_first_error(&batch, fee_collection_results).unwrap();
|
||||
|
|
|
@ -55,6 +55,9 @@ while [[ -n $1 ]]; do
|
|||
elif [[ $1 = --enable-cpi-and-log-storage ]]; then
|
||||
args+=("$1")
|
||||
shift
|
||||
elif [[ $1 = --enable-extended-tx-metadata-storage ]]; then
|
||||
args+=("$1")
|
||||
shift
|
||||
elif [[ $1 = --enable-rpc-bigtable-ledger-storage ]]; then
|
||||
args+=("$1")
|
||||
shift
|
||||
|
|
|
@ -141,6 +141,9 @@ while [[ -n $1 ]]; do
|
|||
elif [[ $1 = --enable-cpi-and-log-storage ]]; then
|
||||
args+=("$1")
|
||||
shift
|
||||
elif [[ $1 = --enable-extended-tx-metadata-storage ]]; then
|
||||
args+=("$1")
|
||||
shift
|
||||
elif [[ $1 = --skip-poh-verify ]]; then
|
||||
args+=("$1")
|
||||
shift
|
||||
|
|
|
@ -280,7 +280,7 @@ EOF
|
|||
|
||||
if $maybeFullRpc; then
|
||||
args+=(--enable-rpc-transaction-history)
|
||||
args+=(--enable-cpi-and-log-storage)
|
||||
args+=(--enable-extended-tx-metadata-storage)
|
||||
fi
|
||||
|
||||
if [[ $airdropsEnabled = true ]]; then
|
||||
|
@ -408,7 +408,7 @@ EOF
|
|||
|
||||
if $maybeFullRpc; then
|
||||
args+=(--enable-rpc-transaction-history)
|
||||
args+=(--enable-cpi-and-log-storage)
|
||||
args+=(--enable-extended-tx-metadata-storage)
|
||||
fi
|
||||
|
||||
cat >> ~/solana/on-reboot <<EOF
|
||||
|
|
|
@ -8,6 +8,7 @@ repository = "https://github.com/solana-labs/solana"
|
|||
version = "1.11.0"
|
||||
|
||||
[dependencies]
|
||||
assert_matches = "1.5.0"
|
||||
async-trait = "0.1.52"
|
||||
base64 = "0.13.0"
|
||||
bincode = "1.3.3"
|
||||
|
|
|
@ -1,14 +1,19 @@
|
|||
use {
|
||||
assert_matches::assert_matches,
|
||||
solana_banks_client::BanksClientError,
|
||||
solana_program_test::{processor, ProgramTest},
|
||||
solana_sdk::{
|
||||
account_info::{next_account_info, AccountInfo},
|
||||
commitment_config::CommitmentLevel,
|
||||
entrypoint::ProgramResult,
|
||||
instruction::{AccountMeta, Instruction},
|
||||
msg,
|
||||
program::{get_return_data, invoke, set_return_data},
|
||||
program_error::ProgramError,
|
||||
pubkey::Pubkey,
|
||||
signature::Signer,
|
||||
transaction::Transaction,
|
||||
transaction_context::TransactionReturnData,
|
||||
},
|
||||
std::str::from_utf8,
|
||||
};
|
||||
|
@ -85,3 +90,55 @@ async fn return_data() {
|
|||
.await
|
||||
.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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1862,6 +1862,12 @@ version = "0.2.16"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
|
||||
[[package]]
|
||||
name = "pretty-hex"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5c99d529f0d30937f6f4b8a86d988047327bb88d04d2c4afc356de74722131"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-crate"
|
||||
version = "0.1.5"
|
||||
|
@ -3075,6 +3081,7 @@ dependencies = [
|
|||
"console",
|
||||
"humantime",
|
||||
"indicatif",
|
||||
"pretty-hex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"solana-account-decoder",
|
||||
|
@ -3419,6 +3426,7 @@ dependencies = [
|
|||
name = "solana-program-test"
|
||||
version = "1.11.0"
|
||||
dependencies = [
|
||||
"assert_matches",
|
||||
"async-trait",
|
||||
"base64 0.13.0",
|
||||
"bincode",
|
||||
|
|
|
@ -323,6 +323,7 @@ fn process_transaction_and_record_inner(
|
|||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
&mut ExecuteTimings::default(),
|
||||
)
|
||||
.0;
|
||||
|
@ -364,6 +365,7 @@ fn execute_transactions(
|
|||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
&mut timings,
|
||||
);
|
||||
let tx_post_token_balances = collect_token_balances(&bank, &batch, &mut mint_decimals);
|
||||
|
@ -392,6 +394,7 @@ fn execute_transactions(
|
|||
log_messages,
|
||||
inner_instructions,
|
||||
durable_nonce_fee,
|
||||
return_data,
|
||||
} = details;
|
||||
|
||||
let lamports_per_signature = match durable_nonce_fee {
|
||||
|
@ -432,6 +435,7 @@ fn execute_transactions(
|
|||
log_messages,
|
||||
rewards: None,
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data,
|
||||
};
|
||||
|
||||
Ok(ConfirmedTransactionWithStatusMeta {
|
||||
|
|
|
@ -143,7 +143,7 @@ fn is_finalized(
|
|||
#[derive(Debug, Default, Clone)]
|
||||
pub struct JsonRpcConfig {
|
||||
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 health_check_slot_distance: u64,
|
||||
pub rpc_bigtable_config: Option<RpcBigtableConfig>,
|
||||
|
@ -3551,6 +3551,7 @@ pub mod rpc_full {
|
|||
logs,
|
||||
post_simulation_accounts: _,
|
||||
units_consumed,
|
||||
return_data,
|
||||
} = preflight_bank.simulate_transaction(transaction)
|
||||
{
|
||||
match err {
|
||||
|
@ -3568,6 +3569,7 @@ pub mod rpc_full {
|
|||
logs: Some(logs),
|
||||
accounts: None,
|
||||
units_consumed: Some(units_consumed),
|
||||
return_data,
|
||||
},
|
||||
}
|
||||
.into());
|
||||
|
@ -3625,6 +3627,7 @@ pub mod rpc_full {
|
|||
logs,
|
||||
post_simulation_accounts,
|
||||
units_consumed,
|
||||
return_data,
|
||||
} = bank.simulate_transaction(transaction);
|
||||
|
||||
let accounts = if let Some(config_accounts) = config.accounts {
|
||||
|
@ -3676,6 +3679,7 @@ pub mod rpc_full {
|
|||
logs: Some(logs),
|
||||
accounts,
|
||||
units_consumed: Some(units_consumed),
|
||||
return_data,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -5574,6 +5578,7 @@ pub mod tests {
|
|||
"Program 11111111111111111111111111111111 invoke [1]",
|
||||
"Program 11111111111111111111111111111111 success"
|
||||
],
|
||||
"returnData":null,
|
||||
"unitsConsumed":0
|
||||
}
|
||||
},
|
||||
|
@ -5660,6 +5665,7 @@ pub mod tests {
|
|||
"Program 11111111111111111111111111111111 invoke [1]",
|
||||
"Program 11111111111111111111111111111111 success"
|
||||
],
|
||||
"returnData":null,
|
||||
"unitsConsumed":0
|
||||
}
|
||||
},
|
||||
|
@ -5688,6 +5694,7 @@ pub mod tests {
|
|||
"Program 11111111111111111111111111111111 invoke [1]",
|
||||
"Program 11111111111111111111111111111111 success"
|
||||
],
|
||||
"returnData":null,
|
||||
"unitsConsumed":0
|
||||
}
|
||||
},
|
||||
|
@ -5737,6 +5744,7 @@ pub mod tests {
|
|||
"err":"BlockhashNotFound",
|
||||
"accounts":null,
|
||||
"logs":[],
|
||||
"returnData":null,
|
||||
"unitsConsumed":0
|
||||
}
|
||||
},
|
||||
|
@ -5766,6 +5774,7 @@ pub mod tests {
|
|||
"Program 11111111111111111111111111111111 invoke [1]",
|
||||
"Program 11111111111111111111111111111111 success"
|
||||
],
|
||||
"returnData":null,
|
||||
"unitsConsumed":0
|
||||
}
|
||||
},
|
||||
|
@ -6123,7 +6132,7 @@ pub mod tests {
|
|||
assert_eq!(
|
||||
res,
|
||||
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(),
|
||||
)
|
||||
);
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ impl TransactionStatusService {
|
|||
enable_rpc_transaction_history: bool,
|
||||
transaction_notifier: Option<TransactionNotifierLock>,
|
||||
blockstore: Arc<Blockstore>,
|
||||
enable_cpi_and_log_storage: bool,
|
||||
enable_extended_tx_metadata_storage: bool,
|
||||
exit: &Arc<AtomicBool>,
|
||||
) -> Self {
|
||||
let exit = exit.clone();
|
||||
|
@ -51,7 +51,7 @@ impl TransactionStatusService {
|
|||
enable_rpc_transaction_history,
|
||||
transaction_notifier.clone(),
|
||||
&blockstore,
|
||||
enable_cpi_and_log_storage,
|
||||
enable_extended_tx_metadata_storage,
|
||||
) {
|
||||
break;
|
||||
}
|
||||
|
@ -66,7 +66,7 @@ impl TransactionStatusService {
|
|||
enable_rpc_transaction_history: bool,
|
||||
transaction_notifier: Option<TransactionNotifierLock>,
|
||||
blockstore: &Arc<Blockstore>,
|
||||
enable_cpi_and_log_storage: bool,
|
||||
enable_extended_tx_metadata_storage: bool,
|
||||
) -> Result<(), RecvTimeoutError> {
|
||||
match write_transaction_status_receiver.recv_timeout(Duration::from_secs(1))? {
|
||||
TransactionStatusMessage::Batch(TransactionStatusBatch {
|
||||
|
@ -101,6 +101,7 @@ impl TransactionStatusService {
|
|||
log_messages,
|
||||
inner_instructions,
|
||||
durable_nonce_fee,
|
||||
return_data,
|
||||
} = details;
|
||||
let lamports_per_signature = match durable_nonce_fee {
|
||||
Some(DurableNonceFee::Valid(lamports_per_signature)) => {
|
||||
|
@ -156,6 +157,7 @@ impl TransactionStatusService {
|
|||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses,
|
||||
return_data,
|
||||
};
|
||||
|
||||
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.inner_instructions.take();
|
||||
transaction_status_meta.return_data.take();
|
||||
}
|
||||
|
||||
if enable_rpc_transaction_history {
|
||||
|
@ -347,6 +351,7 @@ pub(crate) mod tests {
|
|||
)
|
||||
.unwrap(),
|
||||
)),
|
||||
return_data: None,
|
||||
});
|
||||
|
||||
let balances = TransactionBalancesSet {
|
||||
|
|
|
@ -1374,6 +1374,7 @@ mod tests {
|
|||
log_messages: None,
|
||||
inner_instructions: None,
|
||||
durable_nonce_fee: nonce.map(DurableNonceFee::from),
|
||||
return_data: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -130,7 +130,10 @@ use {
|
|||
MessageHash, Result, SanitizedTransaction, Transaction, TransactionError,
|
||||
TransactionVerificationMode, VersionedTransaction,
|
||||
},
|
||||
transaction_context::{InstructionTrace, TransactionAccount, TransactionContext},
|
||||
transaction_context::{
|
||||
ExecutionRecord, InstructionTrace, TransactionAccount, TransactionContext,
|
||||
TransactionReturnData,
|
||||
},
|
||||
},
|
||||
solana_stake_program::stake_state::{
|
||||
self, InflationPointCalculationEvent, PointValue, StakeState,
|
||||
|
@ -579,6 +582,7 @@ pub struct TransactionExecutionDetails {
|
|||
pub log_messages: Option<Vec<String>>,
|
||||
pub inner_instructions: Option<InnerInstructionsList>,
|
||||
pub durable_nonce_fee: Option<DurableNonceFee>,
|
||||
pub return_data: Option<TransactionReturnData>,
|
||||
}
|
||||
|
||||
/// Type safe representation of a transaction execution attempt which
|
||||
|
@ -670,6 +674,7 @@ pub struct TransactionSimulationResult {
|
|||
pub logs: TransactionLogMessages,
|
||||
pub post_simulation_accounts: Vec<TransactionAccount>,
|
||||
pub units_consumed: u64,
|
||||
pub return_data: Option<TransactionReturnData>,
|
||||
}
|
||||
pub struct TransactionBalancesSet {
|
||||
pub pre_balances: TransactionBalances,
|
||||
|
@ -3541,6 +3546,7 @@ impl Bank {
|
|||
MAX_PROCESSING_AGE - MAX_TRANSACTION_FORWARDING_DELAY,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
&mut timings,
|
||||
);
|
||||
|
||||
|
@ -3571,17 +3577,20 @@ impl Bank {
|
|||
|
||||
let execution_result = execution_results.pop().unwrap();
|
||||
let flattened_result = execution_result.flattened_result();
|
||||
let logs = match execution_result {
|
||||
TransactionExecutionResult::Executed(details) => details.log_messages,
|
||||
TransactionExecutionResult::NotExecuted(_) => None,
|
||||
}
|
||||
.unwrap_or_default();
|
||||
let (logs, return_data) = match execution_result {
|
||||
TransactionExecutionResult::Executed(details) => {
|
||||
(details.log_messages, details.return_data)
|
||||
}
|
||||
TransactionExecutionResult::NotExecuted(_) => (None, None),
|
||||
};
|
||||
let logs = logs.unwrap_or_default();
|
||||
|
||||
TransactionSimulationResult {
|
||||
result: flattened_result,
|
||||
logs,
|
||||
post_simulation_accounts,
|
||||
units_consumed,
|
||||
return_data,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3858,6 +3867,7 @@ impl Bank {
|
|||
|
||||
/// Execute a transaction using the provided loaded accounts and update
|
||||
/// the executors cache if the transaction was successful.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn execute_loaded_transaction(
|
||||
&self,
|
||||
tx: &SanitizedTransaction,
|
||||
|
@ -3866,6 +3876,7 @@ impl Bank {
|
|||
durable_nonce_fee: Option<DurableNonceFee>,
|
||||
enable_cpi_recording: bool,
|
||||
enable_log_recording: bool,
|
||||
enable_return_data_recording: bool,
|
||||
timings: &mut ExecuteTimings,
|
||||
error_counters: &mut ErrorCounters,
|
||||
) -> TransactionExecutionResult {
|
||||
|
@ -3960,7 +3971,11 @@ impl Bank {
|
|||
.ok()
|
||||
});
|
||||
|
||||
let (accounts, instruction_trace) = transaction_context.deconstruct();
|
||||
let ExecutionRecord {
|
||||
accounts,
|
||||
instruction_trace,
|
||||
mut return_data,
|
||||
} = transaction_context.into();
|
||||
loaded_transaction.accounts = accounts;
|
||||
|
||||
let inner_instructions = if enable_cpi_recording {
|
||||
|
@ -3971,11 +3986,25 @@ impl Bank {
|
|||
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 {
|
||||
status,
|
||||
log_messages,
|
||||
inner_instructions,
|
||||
durable_nonce_fee,
|
||||
return_data,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -3986,6 +4015,7 @@ impl Bank {
|
|||
max_age: usize,
|
||||
enable_cpi_recording: bool,
|
||||
enable_log_recording: bool,
|
||||
enable_return_data_recording: bool,
|
||||
timings: &mut ExecuteTimings,
|
||||
) -> LoadAndExecuteTransactionsOutput {
|
||||
let sanitized_txs = batch.sanitized_transactions();
|
||||
|
@ -4089,6 +4119,7 @@ impl Bank {
|
|||
durable_nonce_fee,
|
||||
enable_cpi_recording,
|
||||
enable_log_recording,
|
||||
enable_return_data_recording,
|
||||
timings,
|
||||
&mut error_counters,
|
||||
)
|
||||
|
@ -5240,6 +5271,7 @@ impl Bank {
|
|||
collect_balances: bool,
|
||||
enable_cpi_recording: bool,
|
||||
enable_log_recording: bool,
|
||||
enable_return_data_recording: bool,
|
||||
timings: &mut ExecuteTimings,
|
||||
) -> (TransactionResults, TransactionBalancesSet) {
|
||||
let pre_balances = if collect_balances {
|
||||
|
@ -5260,6 +5292,7 @@ impl Bank {
|
|||
max_age,
|
||||
enable_cpi_recording,
|
||||
enable_log_recording,
|
||||
enable_return_data_recording,
|
||||
timings,
|
||||
);
|
||||
|
||||
|
@ -5346,6 +5379,7 @@ impl Bank {
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
&mut ExecuteTimings::default(),
|
||||
)
|
||||
.0
|
||||
|
@ -6774,6 +6808,7 @@ pub(crate) mod tests {
|
|||
message::{Message, MessageHeader},
|
||||
nonce,
|
||||
poh_config::PohConfig,
|
||||
program::MAX_RETURN_DATA,
|
||||
rent::Rent,
|
||||
signature::{keypair_from_seed, Keypair, Signer},
|
||||
stake::{
|
||||
|
@ -6813,6 +6848,7 @@ pub(crate) mod tests {
|
|||
log_messages: None,
|
||||
inner_instructions: None,
|
||||
durable_nonce_fee: nonce.map(DurableNonceFee::from),
|
||||
return_data: None,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -9949,6 +9985,7 @@ pub(crate) mod tests {
|
|||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
&mut ExecuteTimings::default(),
|
||||
)
|
||||
.0
|
||||
|
@ -12482,6 +12519,7 @@ pub(crate) mod tests {
|
|||
true,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
&mut ExecuteTimings::default(),
|
||||
);
|
||||
|
||||
|
@ -15407,6 +15445,7 @@ pub(crate) mod tests {
|
|||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
&mut ExecuteTimings::default(),
|
||||
)
|
||||
.0
|
||||
|
@ -15449,6 +15488,91 @@ pub(crate) mod tests {
|
|||
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]
|
||||
fn test_get_largest_accounts() {
|
||||
let GenesisConfigInfo { genesis_config, .. } =
|
||||
|
|
|
@ -108,7 +108,7 @@ args=(
|
|||
--rpc-faucet-address 127.0.0.1:9900
|
||||
--log -
|
||||
--enable-rpc-transaction-history
|
||||
--enable-cpi-and-log-storage
|
||||
--enable-extended-tx-metadata-storage
|
||||
--init-complete-file "$dataDir"/init-completed
|
||||
--snapshot-compression none
|
||||
--require-tower
|
||||
|
|
|
@ -35,7 +35,7 @@ pub struct TransactionContext {
|
|||
instruction_stack: Vec<usize>,
|
||||
number_of_instructions_at_transaction_level: usize,
|
||||
instruction_trace: InstructionTrace,
|
||||
return_data: (Pubkey, Vec<u8>),
|
||||
return_data: TransactionReturnData,
|
||||
}
|
||||
|
||||
impl TransactionContext {
|
||||
|
@ -57,25 +57,10 @@ impl TransactionContext {
|
|||
instruction_stack: Vec::with_capacity(instruction_context_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
|
||||
pub fn deconstruct_without_keys(self) -> Result<Vec<AccountSharedData>, InstructionError> {
|
||||
if !self.instruction_stack.is_empty() {
|
||||
|
@ -225,7 +210,7 @@ impl TransactionContext {
|
|||
|
||||
/// Gets the return data of the current InstructionContext or any above
|
||||
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
|
||||
|
@ -234,7 +219,7 @@ impl TransactionContext {
|
|||
program_id: Pubkey,
|
||||
data: Vec<u8>,
|
||||
) -> Result<(), InstructionError> {
|
||||
self.return_data = (program_id, data);
|
||||
self.return_data = TransactionReturnData { program_id, data };
|
||||
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
|
||||
pub type InstructionTrace = Vec<Vec<InstructionContext>>;
|
||||
|
||||
|
@ -628,3 +620,27 @@ impl<'a> BorrowedAccount<'a> {
|
|||
.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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -792,7 +792,7 @@ mod tests {
|
|||
prost::Message,
|
||||
solana_sdk::{
|
||||
hash::Hash, message::v0::LoadedAddresses, signature::Keypair, system_transaction,
|
||||
transaction::VersionedTransaction,
|
||||
transaction::VersionedTransaction, transaction_context::TransactionReturnData,
|
||||
},
|
||||
solana_storage_proto::convert::generated,
|
||||
solana_transaction_status::{
|
||||
|
@ -842,6 +842,7 @@ mod tests {
|
|||
post_token_balances: Some(vec![]),
|
||||
rewards: Some(vec![]),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data: Some(TransactionReturnData::default()),
|
||||
},
|
||||
});
|
||||
let expected_block = ConfirmedBlock {
|
||||
|
@ -899,6 +900,7 @@ mod tests {
|
|||
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.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());
|
||||
} else {
|
||||
|
|
|
@ -237,6 +237,7 @@ impl From<StoredConfirmedBlockTransactionStatusMeta> for TransactionStatusMeta {
|
|||
post_token_balances: None,
|
||||
rewards: None,
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,8 @@ message TransactionStatusMeta {
|
|||
repeated Reward rewards = 9;
|
||||
repeated bytes loaded_writable_addresses = 12;
|
||||
repeated bytes loaded_readonly_addresses = 13;
|
||||
ReturnData return_data = 14;
|
||||
bool return_data_none = 15;
|
||||
}
|
||||
|
||||
message TransactionError {
|
||||
|
@ -88,6 +90,11 @@ message UiTokenAmount {
|
|||
string ui_amount_string = 4;
|
||||
}
|
||||
|
||||
message ReturnData {
|
||||
bytes program_id = 1;
|
||||
bytes data = 2;
|
||||
}
|
||||
|
||||
enum RewardType {
|
||||
Unspecified = 0;
|
||||
Fee = 1;
|
||||
|
|
|
@ -12,6 +12,7 @@ use {
|
|||
pubkey::Pubkey,
|
||||
signature::Signature,
|
||||
transaction::{Transaction, TransactionError, VersionedTransaction},
|
||||
transaction_context::TransactionReturnData,
|
||||
},
|
||||
solana_transaction_status::{
|
||||
ConfirmedBlock, InnerInstructions, Reward, RewardType, TransactionByAddrInfo,
|
||||
|
@ -363,6 +364,7 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
|
|||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses,
|
||||
return_data,
|
||||
} = value;
|
||||
let err = match status {
|
||||
Ok(()) => None,
|
||||
|
@ -403,6 +405,8 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
|
|||
.into_iter()
|
||||
.map(|key| <Pubkey as AsRef<[u8]>>::as_ref(&key).into())
|
||||
.collect();
|
||||
let return_data_none = return_data.is_none();
|
||||
let return_data = return_data.map(|return_data| return_data.into());
|
||||
|
||||
Self {
|
||||
err,
|
||||
|
@ -418,6 +422,8 @@ impl From<TransactionStatusMeta> for generated::TransactionStatusMeta {
|
|||
rewards,
|
||||
loaded_writable_addresses,
|
||||
loaded_readonly_addresses,
|
||||
return_data,
|
||||
return_data_none,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -447,6 +453,8 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
|
|||
rewards,
|
||||
loaded_writable_addresses,
|
||||
loaded_readonly_addresses,
|
||||
return_data,
|
||||
return_data_none,
|
||||
} = value;
|
||||
let status = match &err {
|
||||
None => Ok(()),
|
||||
|
@ -490,6 +498,11 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
|
|||
.map(|key| Pubkey::new(&key))
|
||||
.collect(),
|
||||
};
|
||||
let return_data = if return_data_none {
|
||||
None
|
||||
} else {
|
||||
return_data.map(|return_data| return_data.into())
|
||||
};
|
||||
Ok(Self {
|
||||
status,
|
||||
fee,
|
||||
|
@ -501,6 +514,7 @@ impl TryFrom<generated::TransactionStatusMeta> for TransactionStatusMeta {
|
|||
post_token_balances,
|
||||
rewards,
|
||||
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 {
|
||||
fn from(value: CompiledInstruction) -> Self {
|
||||
Self {
|
||||
|
|
|
@ -6,6 +6,7 @@ use {
|
|||
},
|
||||
solana_sdk::{
|
||||
deserialize_utils::default_on_eof, message::v0::LoadedAddresses, transaction::Result,
|
||||
transaction_context::TransactionReturnData,
|
||||
},
|
||||
solana_transaction_status::{
|
||||
InnerInstructions, Reward, RewardType, TransactionStatusMeta, TransactionTokenBalance,
|
||||
|
@ -167,6 +168,8 @@ pub struct StoredTransactionStatusMeta {
|
|||
pub post_token_balances: Option<Vec<StoredTransactionTokenBalance>>,
|
||||
#[serde(deserialize_with = "default_on_eof")]
|
||||
pub rewards: Option<Vec<StoredExtendedReward>>,
|
||||
#[serde(deserialize_with = "default_on_eof")]
|
||||
pub return_data: Option<TransactionReturnData>,
|
||||
}
|
||||
|
||||
impl From<StoredTransactionStatusMeta> for TransactionStatusMeta {
|
||||
|
@ -181,6 +184,7 @@ impl From<StoredTransactionStatusMeta> for TransactionStatusMeta {
|
|||
pre_token_balances,
|
||||
post_token_balances,
|
||||
rewards,
|
||||
return_data,
|
||||
} = value;
|
||||
Self {
|
||||
status,
|
||||
|
@ -196,6 +200,7 @@ impl From<StoredTransactionStatusMeta> for TransactionStatusMeta {
|
|||
rewards: rewards
|
||||
.map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()),
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,6 +219,7 @@ impl TryFrom<TransactionStatusMeta> for StoredTransactionStatusMeta {
|
|||
post_token_balances,
|
||||
rewards,
|
||||
loaded_addresses,
|
||||
return_data,
|
||||
} = value;
|
||||
|
||||
if !loaded_addresses.is_empty() {
|
||||
|
@ -237,6 +243,7 @@ impl TryFrom<TransactionStatusMeta> for StoredTransactionStatusMeta {
|
|||
.map(|balances| balances.into_iter().map(|balance| balance.into()).collect()),
|
||||
rewards: rewards
|
||||
.map(|rewards| rewards.into_iter().map(|reward| reward.into()).collect()),
|
||||
return_data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ use {
|
|||
Result as TransactionResult, Transaction, TransactionError, TransactionVersion,
|
||||
VersionedTransaction,
|
||||
},
|
||||
transaction_context::TransactionReturnData,
|
||||
},
|
||||
std::fmt,
|
||||
thiserror::Error,
|
||||
|
@ -279,6 +280,7 @@ pub struct TransactionStatusMeta {
|
|||
pub post_token_balances: Option<Vec<TransactionTokenBalance>>,
|
||||
pub rewards: Option<Rewards>,
|
||||
pub loaded_addresses: LoadedAddresses,
|
||||
pub return_data: Option<TransactionReturnData>,
|
||||
}
|
||||
|
||||
impl Default for TransactionStatusMeta {
|
||||
|
@ -294,6 +296,7 @@ impl Default for TransactionStatusMeta {
|
|||
post_token_balances: None,
|
||||
rewards: None,
|
||||
loaded_addresses: LoadedAddresses::default(),
|
||||
return_data: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -314,6 +317,7 @@ pub struct UiTransactionStatusMeta {
|
|||
pub rewards: Option<Rewards>,
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub loaded_addresses: Option<UiLoadedAddresses>,
|
||||
pub return_data: Option<TransactionReturnData>,
|
||||
}
|
||||
|
||||
/// A duplicate representation of LoadedAddresses
|
||||
|
@ -364,6 +368,7 @@ impl UiTransactionStatusMeta {
|
|||
.map(|balance| balance.into_iter().map(Into::into).collect()),
|
||||
rewards: meta.rewards,
|
||||
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()),
|
||||
rewards: meta.rewards,
|
||||
loaded_addresses: Some(UiLoadedAddresses::from(&meta.loaded_addresses)),
|
||||
return_data: meta.return_data,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -657,7 +657,7 @@ fn main() {
|
|||
)
|
||||
.rpc_config(JsonRpcConfig {
|
||||
enable_rpc_transaction_history: true,
|
||||
enable_cpi_and_log_storage: true,
|
||||
enable_extended_tx_metadata_storage: true,
|
||||
rpc_bigtable_config,
|
||||
faucet_addr,
|
||||
..JsonRpcConfig::default_for_test()
|
||||
|
|
|
@ -656,8 +656,18 @@ pub fn main() {
|
|||
.long("enable-cpi-and-log-storage")
|
||||
.requires("enable_rpc_transaction_history")
|
||||
.takes_value(false)
|
||||
.help("Include CPI inner instructions and logs in the \
|
||||
historical transaction info stored"),
|
||||
.hidden(true)
|
||||
.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::with_name("rpc_max_multiple_accounts")
|
||||
|
@ -2294,7 +2304,15 @@ pub fn main() {
|
|||
};
|
||||
|
||||
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")
|
||||
|
@ -2325,7 +2343,8 @@ pub fn main() {
|
|||
new_hard_forks: hardforks_of(&matches, "hard_forks"),
|
||||
rpc_config: JsonRpcConfig {
|
||||
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,
|
||||
faucet_addr: matches.value_of("rpc_faucet_addr").map(|address| {
|
||||
solana_net_utils::parse_host_port(address).expect("failed to parse faucet address")
|
||||
|
|
Loading…
Reference in New Issue