1#![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 cbe_sdk::feature_set,
18 transaction::{Result, Transaction, TransactionError, VersionedTransaction},
19 },
20 cbe_program::message::SanitizedVersionedMessage,
21};
22
23pub const MAX_TX_ACCOUNT_LOCKS: usize = 128;
27
28#[derive(Debug, Clone, Eq, PartialEq)]
30pub struct SanitizedTransaction {
31 message: SanitizedMessage,
32 message_hash: Hash,
33 is_simple_vote_tx: bool,
34 signatures: Vec<Signature>,
35}
36
37#[derive(Debug, Clone, Default, Eq, PartialEq)]
39pub struct TransactionAccountLocks<'a> {
40 pub readonly: Vec<&'a Pubkey>,
42 pub writable: Vec<&'a Pubkey>,
44}
45
46pub enum MessageHash {
49 Precomputed(Hash),
50 Compute,
51}
52
53impl From<Hash> for MessageHash {
54 fn from(hash: Hash) -> Self {
55 Self::Precomputed(hash)
56 }
57}
58
59impl SanitizedTransaction {
60 pub fn try_new(
64 tx: SanitizedVersionedTransaction,
65 message_hash: Hash,
66 is_simple_vote_tx: bool,
67 address_loader: impl AddressLoader,
68 ) -> Result<Self> {
69 let signatures = tx.signatures;
70 let SanitizedVersionedMessage { message } = tx.message;
71 let message = match message {
72 VersionedMessage::Legacy(message) => {
73 SanitizedMessage::Legacy(LegacyMessage::new(message))
74 }
75 VersionedMessage::V0(message) => {
76 let loaded_addresses =
77 address_loader.load_addresses(&message.address_table_lookups)?;
78 SanitizedMessage::V0(v0::LoadedMessage::new(message, loaded_addresses))
79 }
80 };
81
82 Ok(Self {
83 message,
84 message_hash,
85 is_simple_vote_tx,
86 signatures,
87 })
88 }
89
90 pub fn try_create(
94 tx: VersionedTransaction,
95 message_hash: impl Into<MessageHash>,
96 is_simple_vote_tx: Option<bool>,
97 address_loader: impl AddressLoader,
98 require_static_program_ids: bool,
99 ) -> Result<Self> {
100 tx.sanitize(require_static_program_ids)?;
101
102 let message_hash = match message_hash.into() {
103 MessageHash::Compute => tx.message.hash(),
104 MessageHash::Precomputed(hash) => hash,
105 };
106
107 let signatures = tx.signatures;
108 let message = match tx.message {
109 VersionedMessage::Legacy(message) => {
110 SanitizedMessage::Legacy(LegacyMessage::new(message))
111 }
112 VersionedMessage::V0(message) => {
113 let loaded_addresses =
114 address_loader.load_addresses(&message.address_table_lookups)?;
115 SanitizedMessage::V0(v0::LoadedMessage::new(message, loaded_addresses))
116 }
117 };
118
119 let is_simple_vote_tx = is_simple_vote_tx.unwrap_or_else(|| {
120 let mut ix_iter = message.program_instructions_iter();
122 ix_iter.next().map(|(program_id, _ix)| program_id) == Some(&crate::vote::program::id())
123 });
124
125 Ok(Self {
126 message,
127 message_hash,
128 is_simple_vote_tx,
129 signatures,
130 })
131 }
132
133 pub fn try_from_legacy_transaction(tx: Transaction) -> Result<Self> {
134 tx.sanitize()?;
135
136 Ok(Self {
137 message_hash: tx.message.hash(),
138 message: SanitizedMessage::Legacy(LegacyMessage::new(tx.message)),
139 is_simple_vote_tx: false,
140 signatures: tx.signatures,
141 })
142 }
143
144 pub fn from_transaction_for_tests(tx: Transaction) -> Self {
146 Self::try_from_legacy_transaction(tx).unwrap()
147 }
148
149 pub fn signature(&self) -> &Signature {
157 &self.signatures[0]
158 }
159
160 pub fn signatures(&self) -> &[Signature] {
162 &self.signatures
163 }
164
165 pub fn message(&self) -> &SanitizedMessage {
167 &self.message
168 }
169
170 pub fn message_hash(&self) -> &Hash {
172 &self.message_hash
173 }
174
175 pub fn is_simple_vote_transaction(&self) -> bool {
177 self.is_simple_vote_tx
178 }
179
180 pub fn to_versioned_transaction(&self) -> VersionedTransaction {
183 let signatures = self.signatures.clone();
184 match &self.message {
185 SanitizedMessage::V0(sanitized_msg) => VersionedTransaction {
186 signatures,
187 message: VersionedMessage::V0(v0::Message::clone(&sanitized_msg.message)),
188 },
189 SanitizedMessage::Legacy(legacy_message) => VersionedTransaction {
190 signatures,
191 message: VersionedMessage::Legacy(legacy::Message::clone(&legacy_message.message)),
192 },
193 }
194 }
195
196 pub fn get_account_locks(
198 &self,
199 tx_account_lock_limit: usize,
200 ) -> Result<TransactionAccountLocks> {
201 Self::validate_account_locks(self.message(), tx_account_lock_limit)?;
202 Ok(self.get_account_locks_unchecked())
203 }
204
205 pub fn get_account_locks_unchecked(&self) -> TransactionAccountLocks {
207 let message = &self.message;
208 let account_keys = message.account_keys();
209 let num_readonly_accounts = message.num_readonly_accounts();
210 let num_writable_accounts = account_keys.len().saturating_sub(num_readonly_accounts);
211
212 let mut account_locks = TransactionAccountLocks {
213 writable: Vec::with_capacity(num_writable_accounts),
214 readonly: Vec::with_capacity(num_readonly_accounts),
215 };
216
217 for (i, key) in account_keys.iter().enumerate() {
218 if message.is_writable(i) {
219 account_locks.writable.push(key);
220 } else {
221 account_locks.readonly.push(key);
222 }
223 }
224
225 account_locks
226 }
227
228 pub fn get_loaded_addresses(&self) -> LoadedAddresses {
230 match &self.message {
231 SanitizedMessage::Legacy(_) => LoadedAddresses::default(),
232 SanitizedMessage::V0(message) => LoadedAddresses::clone(&message.loaded_addresses),
233 }
234 }
235
236 pub fn get_durable_nonce(&self) -> Option<&Pubkey> {
238 self.message.get_durable_nonce()
239 }
240
241 fn message_data(&self) -> Vec<u8> {
243 match &self.message {
244 SanitizedMessage::Legacy(legacy_message) => legacy_message.message.serialize(),
245 SanitizedMessage::V0(loaded_msg) => loaded_msg.message.serialize(),
246 }
247 }
248
249 pub fn verify(&self) -> Result<()> {
251 let message_bytes = self.message_data();
252 if self
253 .signatures
254 .iter()
255 .zip(self.message.account_keys().iter())
256 .map(|(signature, pubkey)| signature.verify(pubkey.as_ref(), &message_bytes))
257 .any(|verified| !verified)
258 {
259 Err(TransactionError::SignatureFailure)
260 } else {
261 Ok(())
262 }
263 }
264
265 pub fn verify_precompiles(&self, feature_set: &feature_set::FeatureSet) -> Result<()> {
267 for (program_id, instruction) in self.message.program_instructions_iter() {
268 verify_if_precompile(
269 program_id,
270 instruction,
271 self.message().instructions(),
272 feature_set,
273 )
274 .map_err(|_| TransactionError::InvalidAccountIndex)?;
275 }
276 Ok(())
277 }
278
279 pub fn validate_account_locks(
281 message: &SanitizedMessage,
282 tx_account_lock_limit: usize,
283 ) -> Result<()> {
284 if message.has_duplicates() {
285 Err(TransactionError::AccountLoadedTwice)
286 } else if message.account_keys().len() > tx_account_lock_limit {
287 Err(TransactionError::TooManyAccountLocks)
288 } else {
289 Ok(())
290 }
291 }
292}