solana_sdk/transaction/
sanitized.rs1#![cfg(feature = "full")]
2
3pub use crate::message::{AddressLoader, SimpleAddressLoader};
4use {
5 super::SanitizedVersionedTransaction,
6 crate::{
7 hash::Hash,
8 message::{
9 legacy,
10 v0::{self, LoadedAddresses},
11 LegacyMessage, SanitizedMessage, VersionedMessage,
12 },
13 precompiles::verify_if_precompile,
14 pubkey::Pubkey,
15 sanitize::Sanitize,
16 signature::Signature,
17 solana_sdk::feature_set,
18 transaction::{Result, Transaction, TransactionError, VersionedTransaction},
19 },
20 solana_program::message::SanitizedVersionedMessage,
21 std::sync::Arc,
22};
23
24pub const MAX_TX_ACCOUNT_LOCKS: usize = 128;
28
29#[derive(Debug, Clone)]
31pub struct SanitizedTransaction {
32 message: SanitizedMessage,
33 message_hash: Hash,
34 is_simple_vote_tx: bool,
35 signatures: Vec<Signature>,
36}
37
38#[derive(Debug, Clone, Default)]
40pub struct TransactionAccountLocks<'a> {
41 pub readonly: Vec<&'a Pubkey>,
43 pub writable: Vec<&'a Pubkey>,
45}
46
47pub enum MessageHash {
50 Precomputed(Hash),
51 Compute,
52}
53
54impl From<Hash> for MessageHash {
55 fn from(hash: Hash) -> Self {
56 Self::Precomputed(hash)
57 }
58}
59
60impl SanitizedTransaction {
61 pub fn try_new(
65 tx: SanitizedVersionedTransaction,
66 message_hash: Hash,
67 is_simple_vote_tx: bool,
68 address_loader: impl AddressLoader,
69 ) -> Result<Self> {
70 let signatures = tx.signatures;
71 let SanitizedVersionedMessage { message } = tx.message;
72 let message = match message {
73 VersionedMessage::Legacy(message) => {
74 SanitizedMessage::Legacy(LegacyMessage::new(message))
75 }
76 VersionedMessage::V0(message) => {
77 let loaded_addresses =
78 address_loader.load_addresses(&message.address_table_lookups)?;
79 SanitizedMessage::V0(v0::LoadedMessage::new(message, loaded_addresses))
80 }
81 };
82
83 Ok(Self {
84 message,
85 message_hash,
86 is_simple_vote_tx,
87 signatures,
88 })
89 }
90
91 pub fn try_create(
95 tx: VersionedTransaction,
96 message_hash: impl Into<MessageHash>,
97 is_simple_vote_tx: Option<bool>,
98 address_loader: impl AddressLoader,
99 require_static_program_ids: bool,
100 ) -> Result<Self> {
101 tx.sanitize(require_static_program_ids)?;
102
103 let message_hash = match message_hash.into() {
104 MessageHash::Compute => tx.message.hash(),
105 MessageHash::Precomputed(hash) => hash,
106 };
107
108 let signatures = tx.signatures;
109 let message = match tx.message {
110 VersionedMessage::Legacy(message) => {
111 SanitizedMessage::Legacy(LegacyMessage::new(message))
112 }
113 VersionedMessage::V0(message) => {
114 let loaded_addresses =
115 address_loader.load_addresses(&message.address_table_lookups)?;
116 SanitizedMessage::V0(v0::LoadedMessage::new(message, loaded_addresses))
117 }
118 };
119
120 let is_simple_vote_tx = is_simple_vote_tx.unwrap_or_else(|| {
121 let mut ix_iter = message.program_instructions_iter();
123 ix_iter.next().map(|(program_id, _ix)| program_id) == Some(&crate::vote::program::id())
124 });
125
126 Ok(Self {
127 message,
128 message_hash,
129 is_simple_vote_tx,
130 signatures,
131 })
132 }
133
134 pub fn try_from_legacy_transaction(tx: Transaction) -> Result<Self> {
135 tx.sanitize()?;
136
137 Ok(Self {
138 message_hash: tx.message.hash(),
139 message: SanitizedMessage::Legacy(LegacyMessage::new(tx.message)),
140 is_simple_vote_tx: false,
141 signatures: tx.signatures,
142 })
143 }
144
145 pub fn from_transaction_for_tests(tx: Transaction) -> Self {
147 Self::try_from_legacy_transaction(tx).unwrap()
148 }
149
150 pub fn signature(&self) -> &Signature {
158 &self.signatures[0]
159 }
160
161 pub fn signatures(&self) -> &[Signature] {
163 &self.signatures
164 }
165
166 pub fn message(&self) -> &SanitizedMessage {
168 &self.message
169 }
170
171 pub fn message_hash(&self) -> &Hash {
173 &self.message_hash
174 }
175
176 pub fn is_simple_vote_transaction(&self) -> bool {
178 self.is_simple_vote_tx
179 }
180
181 pub fn to_versioned_transaction(&self) -> VersionedTransaction {
184 let signatures = self.signatures.clone();
185 match &self.message {
186 SanitizedMessage::V0(sanitized_msg) => VersionedTransaction {
187 signatures,
188 message: VersionedMessage::V0(v0::Message::clone(&sanitized_msg.message)),
189 },
190 SanitizedMessage::Legacy(legacy_message) => VersionedTransaction {
191 signatures,
192 message: VersionedMessage::Legacy(legacy::Message::clone(&legacy_message.message)),
193 },
194 }
195 }
196
197 pub fn get_account_locks(
199 &self,
200 tx_account_lock_limit: usize,
201 ) -> Result<TransactionAccountLocks> {
202 if self.message.has_duplicates() {
203 Err(TransactionError::AccountLoadedTwice)
204 } else if self.message.account_keys().len() > tx_account_lock_limit {
205 Err(TransactionError::TooManyAccountLocks)
206 } else {
207 Ok(self.get_account_locks_unchecked())
208 }
209 }
210
211 pub fn get_account_locks_unchecked(&self) -> TransactionAccountLocks {
213 let message = &self.message;
214 let account_keys = message.account_keys();
215 let num_readonly_accounts = message.num_readonly_accounts();
216 let num_writable_accounts = account_keys.len().saturating_sub(num_readonly_accounts);
217
218 let mut account_locks = TransactionAccountLocks {
219 writable: Vec::with_capacity(num_writable_accounts),
220 readonly: Vec::with_capacity(num_readonly_accounts),
221 };
222
223 for (i, key) in account_keys.iter().enumerate() {
224 if message.is_writable(i) {
225 account_locks.writable.push(key);
226 } else {
227 account_locks.readonly.push(key);
228 }
229 }
230
231 account_locks
232 }
233
234 pub fn get_loaded_addresses(&self) -> LoadedAddresses {
236 match &self.message {
237 SanitizedMessage::Legacy(_) => LoadedAddresses::default(),
238 SanitizedMessage::V0(message) => LoadedAddresses::clone(&message.loaded_addresses),
239 }
240 }
241
242 pub fn get_durable_nonce(&self) -> Option<&Pubkey> {
244 self.message.get_durable_nonce()
245 }
246
247 fn message_data(&self) -> Vec<u8> {
249 match &self.message {
250 SanitizedMessage::Legacy(legacy_message) => legacy_message.message.serialize(),
251 SanitizedMessage::V0(loaded_msg) => loaded_msg.message.serialize(),
252 }
253 }
254
255 pub fn verify(&self) -> Result<()> {
257 let message_bytes = self.message_data();
258 if self
259 .signatures
260 .iter()
261 .zip(self.message.account_keys().iter())
262 .map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), &message_bytes))
263 .any(|verified| !verified)
264 {
265 Err(TransactionError::SignatureFailure)
266 } else {
267 Ok(())
268 }
269 }
270
271 pub fn verify_precompiles(&self, feature_set: &Arc<feature_set::FeatureSet>) -> Result<()> {
273 for (program_id, instruction) in self.message.program_instructions_iter() {
274 verify_if_precompile(
275 program_id,
276 instruction,
277 self.message().instructions(),
278 feature_set,
279 )
280 .map_err(|_| TransactionError::InvalidAccountIndex)?;
281 }
282 Ok(())
283 }
284}