Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

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

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ syscalls = ["syscall-v2"]
syscall-v2 = [
"trident-syscall-stubs-v2",
"solana-sbpf",
"solana-transaction-context",
"solana-program-entrypoint",
]

Expand All @@ -36,6 +35,7 @@ solana-sysvar-id = "2.2"
solana-keypair = "2.2"
solana-transaction-error = "2.2"
solana-transaction = {version = "2.2", features = ["blake3"]}
solana-message = "2.2"
solana-clock = "2.2"
solana-rent = "2.2"
solana-signer = "2.2"
Expand All @@ -57,9 +57,10 @@ agave-precompiles = "2.3"

# agave-syscalls = "0" ## placeholder, might be used later

solana-transaction-context = "2.2"

# Stubs
solana-sbpf = {version = "0.11", optional = true}
solana-transaction-context = {version = "2.2", optional = true}
solana-program-entrypoint = {version = "2.2", optional = true}

# Misc
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub mod processor {

pub mod prelude {
pub use super::trident_svm_log;
pub use crate::types::transaction_result::TridentTransactionProcessingResult;
pub use crate::types::transaction_result::TridentTransactionResult;
pub use log::Level;
pub use solana_svm;
}
90 changes: 40 additions & 50 deletions src/methods/trident_svm_transactions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,122 +14,112 @@ use solana_svm::transaction_processor::TransactionProcessingConfig;
use solana_svm::transaction_processor::TransactionProcessingEnvironment;

use crate::trident_svm::TridentSVM;
use crate::types::transaction_result::TridentTransactionProcessingResult;
use crate::types::transaction_result::TridentTransactionResult;

const LOG_MESSAGES_BYTES_LIMIT: usize = 100 * 1000;

fn recording_config() -> ExecutionRecordingConfig {
ExecutionRecordingConfig {
enable_cpi_recording: true,
enable_log_recording: true,
enable_return_data_recording: true,
enable_transaction_balance_recording: false,
}
}

impl TridentSVM {
pub fn process_transaction(
&mut self,
transaction: Transaction,
) -> TridentTransactionProcessingResult {
pub fn process_transaction(&mut self, transaction: Transaction) -> TridentTransactionResult {
let tx_processing_environment = TransactionProcessingEnvironment::<'_> {
feature_set: *self.feature_set,
..Default::default()
};

let tx_processing_config = TransactionProcessingConfig::default();
let tx_processing_config = TransactionProcessingConfig {
log_messages_bytes_limit: Some(LOG_MESSAGES_BYTES_LIMIT),
recording_config: recording_config(),
..Default::default()
};

// reset sysvar cache
self.processor.reset_sysvar_cache();

// replenish sysvar cache with sysvars from the accounts db
self.processor.fill_missing_sysvar_cache_entries(self);

// create sanitized transaction
let sanitezed_tx =
let sanitized_tx =
SanitizedTransaction::try_from_legacy_transaction(transaction, &HashSet::new())
.unwrap();
.expect("Failed to create sanitized transaction");

// get current transaction timestamp
let transaction_timestamp =
self.accounts.deserialize_sysvar::<Clock>().unix_timestamp as u64;
let transaction_timestamp = self.accounts.deserialize_sysvar::<Clock>().unix_timestamp;

// execute transaction
let res = self.processor.load_and_execute_sanitized_transactions(
let output = self.processor.load_and_execute_sanitized_transactions(
self,
&[sanitezed_tx],
&[sanitized_tx],
get_transaction_check_results(1),
&tx_processing_environment,
&tx_processing_config,
);

// update clock
self.accounts.update_clock();

// return transaction processing result
TridentTransactionProcessingResult::new(res, transaction_timestamp)
TridentTransactionResult::from_svm_output(output, transaction_timestamp)
}

pub fn process_transaction_with_settle(
&mut self,
transaction: Transaction,
) -> TridentTransactionProcessingResult {
) -> TridentTransactionResult {
let tx_processing_environment = TransactionProcessingEnvironment::<'_> {
feature_set: *self.feature_set,
..Default::default()
};

let tx_processing_config = TransactionProcessingConfig {
log_messages_bytes_limit: Some(20 * 1000),
recording_config: ExecutionRecordingConfig::new_single_setting(true),
log_messages_bytes_limit: Some(LOG_MESSAGES_BYTES_LIMIT),
recording_config: recording_config(),
..Default::default()
};

// reset sysvar cache
self.processor.reset_sysvar_cache();

// replenish sysvar cache with sysvars from the accounts db
self.processor.fill_missing_sysvar_cache_entries(self);

// create sanitized transaction
let sanitezed_tx =
let sanitized_tx =
SanitizedTransaction::try_from_legacy_transaction(transaction, &HashSet::new())
.expect("Trident SVM is not able to create sanitized transaction");
.expect("Failed to create sanitized transaction");

// get current transaction timestamp
let transaction_timestamp =
self.accounts.deserialize_sysvar::<Clock>().unix_timestamp as u64;
let transaction_timestamp = self.accounts.deserialize_sysvar::<Clock>().unix_timestamp;

// execute transaction
let result = self.processor.load_and_execute_sanitized_transactions(
let output = self.processor.load_and_execute_sanitized_transactions(
self,
&[sanitezed_tx],
&[sanitized_tx],
get_transaction_check_results(1),
&tx_processing_environment,
&tx_processing_config,
);

// update clock
self.accounts.update_clock();

let processed_transaction = result.processing_results[0]
let processed_transaction = output.processing_results[0]
.processed_transaction()
.expect("Transaction was not processed");

match &processed_transaction {
solana_svm::transaction_processing_result::ProcessedTransaction::Executed(
executed_tx,
) => match &executed_tx.execution_details.status {
Ok(()) => {
) => {
if executed_tx.execution_details.status.is_ok() {
self.settle_accounts(&executed_tx.loaded_transaction.accounts);
}
Err(_transaction_error) => {
// in case of transaction error, we don't need to do anything
}
},
solana_svm::transaction_processing_result::ProcessedTransaction::FeesOnly(
_transaction_error,
) => {
// in case of transaction error, we don't need to do anything
}
solana_svm::transaction_processing_result::ProcessedTransaction::FeesOnly(_) => {}
}
TridentTransactionProcessingResult::new(result, transaction_timestamp)

TridentTransactionResult::from_svm_output(output, transaction_timestamp)
}
}

pub(crate) fn get_transaction_check_results(
len: usize,
) -> Vec<solana_transaction_error::TransactionResult<CheckedTransactionDetails>> {
let compute_budget_limit = ComputeBudgetLimits {
updated_heap_bytes: 256 * 1024, // max heap frame
updated_heap_bytes: 256 * 1024,
..ComputeBudgetLimits::default()
};
vec![
Expand Down
154 changes: 141 additions & 13 deletions src/types/transaction_result.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,154 @@
use solana_account::AccountSharedData;
use solana_instruction::error::InstructionError;
use solana_message::inner_instruction::InnerInstructionsList;
use solana_pubkey::Pubkey;
use solana_svm::transaction_processing_result::ProcessedTransaction;
use solana_svm::transaction_processing_result::TransactionProcessingResultExtensions;
use solana_svm::transaction_processor::LoadAndExecuteSanitizedTransactionsOutput;
use solana_transaction_context::TransactionReturnData;
use solana_transaction_error::TransactionError;

pub struct TridentTransactionProcessingResult {
result: LoadAndExecuteSanitizedTransactionsOutput,
transaction_timestamp: u64,
/// A resolved CPI (cross-program invocation) with actual pubkeys instead of
/// raw index references into the transaction's account key table.
#[derive(Debug, Clone)]
pub struct ResolvedInnerInstruction {
pub program_id: Pubkey,
pub accounts: Vec<Pubkey>,
pub data: Vec<u8>,
pub stack_height: u8,
}

impl TridentTransactionProcessingResult {
pub fn new(
result: LoadAndExecuteSanitizedTransactionsOutput,
transaction_timestamp: u64,
/// Per top-level instruction, the list of CPIs it made.
pub type ResolvedInnerInstructions = Vec<ResolvedInnerInstruction>;

/// One entry per top-level instruction in the transaction.
pub type ResolvedInnerInstructionsList = Vec<ResolvedInnerInstructions>;

fn resolve_inner_instructions(
raw: &InnerInstructionsList,
account_keys: &[(Pubkey, AccountSharedData)],
) -> ResolvedInnerInstructionsList {
raw.iter()
.map(|outer| {
outer
.iter()
.map(|inner| {
let program_id = account_keys
.get(inner.instruction.program_id_index as usize)
.map(|(key, _)| *key)
.unwrap_or_default();

let accounts = inner
.instruction
.accounts
.iter()
.map(|&idx| {
account_keys
.get(idx as usize)
.map(|(key, _)| *key)
.unwrap_or_default()
})
.collect();

ResolvedInnerInstruction {
program_id,
accounts,
data: inner.instruction.data.clone(),
stack_height: inner.stack_height,
}
})
.collect()
})
.collect()
}

pub struct TridentTransactionResult {
pub(crate) status: Result<(), TransactionError>,
pub(crate) logs: Vec<String>,
pub(crate) compute_units_consumed: u64,
pub(crate) inner_instructions: Option<ResolvedInnerInstructionsList>,
pub(crate) return_data: Option<TransactionReturnData>,
pub(crate) transaction_timestamp: i64,
}

impl TridentTransactionResult {
pub(crate) fn from_svm_output(
output: LoadAndExecuteSanitizedTransactionsOutput,
transaction_timestamp: i64,
) -> Self {
Self {
result,
transaction_timestamp,
let processing_result = &output.processing_results[0];

match processing_result.processed_transaction() {
Some(ProcessedTransaction::Executed(executed_tx)) => {
let details = &executed_tx.execution_details;
let accounts = &executed_tx.loaded_transaction.accounts;

let inner_instructions = details
.inner_instructions
.as_ref()
.map(|raw| resolve_inner_instructions(raw, accounts));

Self {
status: details.status.clone(),
logs: details.log_messages.clone().unwrap_or_default(),
compute_units_consumed: details.executed_units,
inner_instructions,
return_data: details.return_data.clone(),
transaction_timestamp,
}
}
Some(ProcessedTransaction::FeesOnly(fees_only)) => Self {
status: Err(TransactionError::clone(&fees_only.load_error)),
logs: Vec::new(),
compute_units_consumed: 0,
inner_instructions: None,
return_data: None,
transaction_timestamp,
},
None => Self {
status: processing_result.flattened_result(),
logs: Vec::new(),
compute_units_consumed: 0,
inner_instructions: None,
return_data: None,
transaction_timestamp,
},
}
}

pub fn get_result(&self) -> &LoadAndExecuteSanitizedTransactionsOutput {
&self.result
pub fn status(&self) -> &Result<(), TransactionError> {
&self.status
}

pub fn get_transaction_timestamp(&self) -> u64 {
pub fn is_success(&self) -> bool {
self.status.is_ok()
}

pub fn logs(&self) -> String {
format!("{:#?}", self.logs)
}

pub fn compute_units_consumed(&self) -> u64 {
self.compute_units_consumed
}

pub fn inner_instructions(&self) -> Option<&ResolvedInnerInstructionsList> {
self.inner_instructions.as_ref()
}

pub fn return_data(&self) -> Option<&TransactionReturnData> {
self.return_data.as_ref()
}

pub fn transaction_timestamp(&self) -> i64 {
self.transaction_timestamp
}

pub fn is_program_failed_to_complete(&self) -> bool {
self.status.is_err()
&& matches!(
self.status.as_ref().unwrap_err(),
TransactionError::InstructionError(_, InstructionError::ProgramFailedToComplete)
)
}
}
Loading