use solana_account::AccountSharedData;
use solana_address::Address;
use solana_signature::Signature;
use solana_transaction_error::TransactionError;
use crate::{
HPSVM,
accounts_db::AccountsDb,
error::HPSVMError,
history::TransactionHistory,
types::{ExecutionOutcome, FailedTransactionMetadata, TransactionResult},
};
#[derive(Debug, Clone)]
pub(crate) struct CommitDelta {
post_accounts: Vec<(Address, AccountSharedData)>,
history_entry: Option<(Signature, TransactionResult)>,
}
impl CommitDelta {
pub(crate) const fn new(
post_accounts: Vec<(Address, AccountSharedData)>,
history_entry: Option<(Signature, TransactionResult)>,
) -> Self {
Self { post_accounts, history_entry }
}
pub(crate) const fn mutates_state(&self) -> bool {
!self.post_accounts.is_empty() || self.history_entry.is_some()
}
}
pub(crate) fn apply_commit_delta(
accounts: &mut AccountsDb,
history: &mut TransactionHistory,
delta: CommitDelta,
) -> Result<(), HPSVMError> {
accounts.sync_accounts(delta.post_accounts)?;
if let Some((signature, entry)) = delta.history_entry {
history.add_new_transaction(signature, entry);
}
Ok(())
}
pub(crate) fn outcome_into_result_and_delta(
outcome: ExecutionOutcome,
history_enabled: bool,
) -> (TransactionResult, CommitDelta) {
let ExecutionOutcome { meta, post_accounts, status, included, .. } = outcome;
let signature = meta.signature;
let result = match status {
Ok(()) => TransactionResult::Ok(meta),
Err(err) => TransactionResult::Err(FailedTransactionMetadata { err, meta }),
};
let delta = if included {
let history_entry = history_enabled.then(|| (signature, result.clone()));
CommitDelta::new(post_accounts, history_entry)
} else {
CommitDelta::new(Vec::new(), None)
};
(result, delta)
}
pub(crate) fn commit_execution_outcome(
vm: &mut HPSVM,
outcome: ExecutionOutcome,
) -> TransactionResult {
let origin_vm_instance_id = outcome.origin_vm_instance_id;
let origin_state_version = outcome.origin_state_version;
if origin_vm_instance_id != vm.instance_id || origin_state_version != vm.state_version {
return TransactionResult::Err(FailedTransactionMetadata {
err: TransactionError::ResanitizationNeeded,
meta: outcome.meta,
});
}
let history_enabled = vm.history.is_enabled();
let (result, delta) = crate::hotpath_block!(
"hpsvm::commit::outcome_into_result_and_delta",
outcome_into_result_and_delta(outcome, history_enabled)
);
let mutates_state = delta.mutates_state();
crate::hotpath_block!("hpsvm::commit::apply_commit_delta", {
apply_commit_delta(&mut vm.accounts, &mut vm.history, delta)
.expect("It shouldn't be possible to write invalid sysvars in send_transaction.");
});
if mutates_state {
vm.invalidate_execution_outcomes();
}
result
}