1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
use solana_address::Address;
use solana_transaction::sanitized::SanitizedTransaction;
use crate::HPSVM;
/// Origin of a transaction observed by an [`Inspector`].
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub enum TransactionOrigin {
/// A caller-submitted transaction or instruction helper.
User,
/// The internal transfer used by [`HPSVM::airdrop`].
InternalAirdrop,
/// A transaction executed as part of a batch stage.
Batch {
/// Zero-based stage index in the computed batch plan.
stage_index: usize,
/// Index in the caller-provided transaction list.
transaction_index: usize,
},
}
impl TransactionOrigin {
const fn is_user(self) -> bool {
matches!(self, Self::User)
}
}
/// Observes transaction execution without mutating VM state.
pub trait Inspector: Send + Sync {
/// Called immediately before top-level instruction processing begins.
fn on_transaction_start(&self, _svm: &HPSVM, _tx: &SanitizedTransaction) {}
/// Called immediately before top-level instruction processing begins, with origin context.
fn on_transaction_start_with_origin(
&self,
origin: TransactionOrigin,
svm: &HPSVM,
tx: &SanitizedTransaction,
) {
if origin.is_user() {
self.on_transaction_start(svm, tx);
}
}
/// Called before each top-level instruction is executed.
fn on_instruction(&self, _svm: &HPSVM, _index: usize, _program_id: &Address) {}
/// Called before each top-level instruction is executed, with origin context.
fn on_instruction_with_origin(
&self,
origin: TransactionOrigin,
svm: &HPSVM,
index: usize,
program_id: &Address,
) {
if origin.is_user() {
self.on_instruction(svm, index, program_id);
}
}
/// Called after top-level instruction processing completes.
fn on_transaction_end(
&self,
_svm: &HPSVM,
_result: &solana_transaction_error::TransactionResult<()>,
) {
}
/// Called after top-level instruction processing completes, with origin context.
fn on_transaction_end_with_origin(
&self,
origin: TransactionOrigin,
svm: &HPSVM,
result: &solana_transaction_error::TransactionResult<()>,
) {
if origin.is_user() {
self.on_transaction_end(svm, result);
}
}
}
#[derive(Debug, Default)]
pub(crate) struct NoopInspector;
impl Inspector for NoopInspector {}