solana_program/message/versions/v0/
loaded.rs

1use {
2    crate::{
3        bpf_loader_upgradeable,
4        message::{v0, AccountKeys},
5        pubkey::Pubkey,
6    },
7    std::{borrow::Cow, collections::HashSet},
8};
9
10/// Combination of a version #0 message and its loaded addresses
11#[derive(Debug, Clone, Eq, PartialEq)]
12pub struct LoadedMessage<'a> {
13    /// Message which loaded a collection of lookup table addresses
14    pub message: Cow<'a, v0::Message>,
15    /// Addresses loaded with on-chain address lookup tables
16    pub loaded_addresses: Cow<'a, LoadedAddresses>,
17    /// List of boolean with same length as account_keys(), each boolean value indicates if
18    /// corresponding account key is writable or not.
19    pub is_writable_account_cache: Vec<bool>,
20}
21
22/// Collection of addresses loaded from on-chain lookup tables, split
23/// by readonly and writable.
24#[derive(Clone, Default, Debug, PartialEq, Eq, Serialize, Deserialize)]
25pub struct LoadedAddresses {
26    /// List of addresses for writable loaded accounts
27    pub writable: Vec<Pubkey>,
28    /// List of addresses for read-only loaded accounts
29    pub readonly: Vec<Pubkey>,
30}
31
32impl FromIterator<LoadedAddresses> for LoadedAddresses {
33    fn from_iter<T: IntoIterator<Item = LoadedAddresses>>(iter: T) -> Self {
34        let (writable, readonly): (Vec<Vec<Pubkey>>, Vec<Vec<Pubkey>>) = iter
35            .into_iter()
36            .map(|addresses| (addresses.writable, addresses.readonly))
37            .unzip();
38        LoadedAddresses {
39            writable: writable.into_iter().flatten().collect(),
40            readonly: readonly.into_iter().flatten().collect(),
41        }
42    }
43}
44
45impl LoadedAddresses {
46    /// Checks if there are no writable or readonly addresses
47    pub fn is_empty(&self) -> bool {
48        self.len() == 0
49    }
50
51    /// Combined length of loaded writable and readonly addresses
52    pub fn len(&self) -> usize {
53        self.writable.len().saturating_add(self.readonly.len())
54    }
55}
56
57impl<'a> LoadedMessage<'a> {
58    pub fn new(
59        message: v0::Message,
60        loaded_addresses: LoadedAddresses,
61        reserved_account_keys: &HashSet<Pubkey>,
62    ) -> Self {
63        let mut loaded_message = Self {
64            message: Cow::Owned(message),
65            loaded_addresses: Cow::Owned(loaded_addresses),
66            is_writable_account_cache: Vec::default(),
67        };
68        loaded_message.set_is_writable_account_cache(reserved_account_keys);
69        loaded_message
70    }
71
72    pub fn new_borrowed(
73        message: &'a v0::Message,
74        loaded_addresses: &'a LoadedAddresses,
75        reserved_account_keys: &HashSet<Pubkey>,
76    ) -> Self {
77        let mut loaded_message = Self {
78            message: Cow::Borrowed(message),
79            loaded_addresses: Cow::Borrowed(loaded_addresses),
80            is_writable_account_cache: Vec::default(),
81        };
82        loaded_message.set_is_writable_account_cache(reserved_account_keys);
83        loaded_message
84    }
85
86    fn set_is_writable_account_cache(&mut self, reserved_account_keys: &HashSet<Pubkey>) {
87        let is_writable_account_cache = self
88            .account_keys()
89            .iter()
90            .enumerate()
91            .map(|(i, _key)| self.is_writable_internal(i, reserved_account_keys))
92            .collect::<Vec<_>>();
93        let _ = std::mem::replace(
94            &mut self.is_writable_account_cache,
95            is_writable_account_cache,
96        );
97    }
98
99    /// Returns the full list of static and dynamic account keys that are loaded for this message.
100    pub fn account_keys(&self) -> AccountKeys {
101        AccountKeys::new(&self.message.account_keys, Some(&self.loaded_addresses))
102    }
103
104    /// Returns the list of static account keys that are loaded for this message.
105    pub fn static_account_keys(&self) -> &[Pubkey] {
106        &self.message.account_keys
107    }
108
109    /// Returns true if any account keys are duplicates
110    pub fn has_duplicates(&self) -> bool {
111        let mut uniq = HashSet::new();
112        self.account_keys().iter().any(|x| !uniq.insert(x))
113    }
114
115    /// Returns true if the account at the specified index was requested to be
116    /// writable.  This method should not be used directly.
117    fn is_writable_index(&self, key_index: usize) -> bool {
118        let header = &self.message.header;
119        let num_account_keys = self.message.account_keys.len();
120        let num_signed_accounts = usize::from(header.num_required_signatures);
121        if key_index >= num_account_keys {
122            let loaded_addresses_index = key_index.saturating_sub(num_account_keys);
123            loaded_addresses_index < self.loaded_addresses.writable.len()
124        } else if key_index >= num_signed_accounts {
125            let num_unsigned_accounts = num_account_keys.saturating_sub(num_signed_accounts);
126            let num_writable_unsigned_accounts = num_unsigned_accounts
127                .saturating_sub(usize::from(header.num_readonly_unsigned_accounts));
128            let unsigned_account_index = key_index.saturating_sub(num_signed_accounts);
129            unsigned_account_index < num_writable_unsigned_accounts
130        } else {
131            let num_writable_signed_accounts = num_signed_accounts
132                .saturating_sub(usize::from(header.num_readonly_signed_accounts));
133            key_index < num_writable_signed_accounts
134        }
135    }
136
137    /// Returns true if the account at the specified index was loaded as writable
138    fn is_writable_internal(
139        &self,
140        key_index: usize,
141        reserved_account_keys: &HashSet<Pubkey>,
142    ) -> bool {
143        if self.is_writable_index(key_index) {
144            if let Some(key) = self.account_keys().get(key_index) {
145                return !(reserved_account_keys.contains(key) || self.demote_program_id(key_index));
146            }
147        }
148        false
149    }
150
151    pub fn is_writable(&self, key_index: usize) -> bool {
152        *self
153            .is_writable_account_cache
154            .get(key_index)
155            .unwrap_or(&false)
156    }
157
158    pub fn is_signer(&self, i: usize) -> bool {
159        i < self.message.header.num_required_signatures as usize
160    }
161
162    pub fn demote_program_id(&self, i: usize) -> bool {
163        self.is_key_called_as_program(i) && !self.is_upgradeable_loader_present()
164    }
165
166    /// Returns true if the account at the specified index is called as a program by an instruction
167    pub fn is_key_called_as_program(&self, key_index: usize) -> bool {
168        if let Ok(key_index) = u8::try_from(key_index) {
169            self.message
170                .instructions
171                .iter()
172                .any(|ix| ix.program_id_index == key_index)
173        } else {
174            false
175        }
176    }
177
178    /// Returns true if any account is the bpf upgradeable loader
179    pub fn is_upgradeable_loader_present(&self) -> bool {
180        self.account_keys()
181            .iter()
182            .any(|&key| key == bpf_loader_upgradeable::id())
183    }
184}
185
186#[cfg(test)]
187mod tests {
188    use {
189        super::*,
190        crate::{instruction::CompiledInstruction, message::MessageHeader, system_program, sysvar},
191        itertools::Itertools,
192    };
193
194    fn check_test_loaded_message() -> (LoadedMessage<'static>, [Pubkey; 6]) {
195        let key0 = Pubkey::new_unique();
196        let key1 = Pubkey::new_unique();
197        let key2 = Pubkey::new_unique();
198        let key3 = Pubkey::new_unique();
199        let key4 = Pubkey::new_unique();
200        let key5 = Pubkey::new_unique();
201
202        let message = LoadedMessage::new(
203            v0::Message {
204                header: MessageHeader {
205                    num_required_signatures: 2,
206                    num_readonly_signed_accounts: 1,
207                    num_readonly_unsigned_accounts: 1,
208                },
209                account_keys: vec![key0, key1, key2, key3],
210                ..v0::Message::default()
211            },
212            LoadedAddresses {
213                writable: vec![key4],
214                readonly: vec![key5],
215            },
216            &HashSet::default(),
217        );
218
219        (message, [key0, key1, key2, key3, key4, key5])
220    }
221
222    #[test]
223    fn test_has_duplicates() {
224        let message = check_test_loaded_message().0;
225
226        assert!(!message.has_duplicates());
227    }
228
229    #[test]
230    fn test_has_duplicates_with_dupe_keys() {
231        let create_message_with_dupe_keys = |mut keys: Vec<Pubkey>| {
232            LoadedMessage::new(
233                v0::Message {
234                    account_keys: keys.split_off(2),
235                    ..v0::Message::default()
236                },
237                LoadedAddresses {
238                    writable: keys.split_off(2),
239                    readonly: keys,
240                },
241                &HashSet::default(),
242            )
243        };
244
245        let key0 = Pubkey::new_unique();
246        let key1 = Pubkey::new_unique();
247        let key2 = Pubkey::new_unique();
248        let key3 = Pubkey::new_unique();
249        let dupe_key = Pubkey::new_unique();
250
251        let keys = vec![key0, key1, key2, key3, dupe_key, dupe_key];
252        let keys_len = keys.len();
253        for keys in keys.into_iter().permutations(keys_len).unique() {
254            let message = create_message_with_dupe_keys(keys);
255            assert!(message.has_duplicates());
256        }
257    }
258
259    #[test]
260    fn test_is_writable_index() {
261        let message = check_test_loaded_message().0;
262
263        assert!(message.is_writable_index(0));
264        assert!(!message.is_writable_index(1));
265        assert!(message.is_writable_index(2));
266        assert!(!message.is_writable_index(3));
267        assert!(message.is_writable_index(4));
268        assert!(!message.is_writable_index(5));
269    }
270
271    #[test]
272    fn test_is_writable() {
273        solana_logger::setup();
274
275        let reserved_account_keys = HashSet::from_iter([sysvar::clock::id(), system_program::id()]);
276        let create_message_with_keys = |keys: Vec<Pubkey>| {
277            LoadedMessage::new(
278                v0::Message {
279                    header: MessageHeader {
280                        num_required_signatures: 1,
281                        num_readonly_signed_accounts: 0,
282                        num_readonly_unsigned_accounts: 1,
283                    },
284                    account_keys: keys[..2].to_vec(),
285                    ..v0::Message::default()
286                },
287                LoadedAddresses {
288                    writable: keys[2..=2].to_vec(),
289                    readonly: keys[3..].to_vec(),
290                },
291                &reserved_account_keys,
292            )
293        };
294
295        let key0 = Pubkey::new_unique();
296        let key1 = Pubkey::new_unique();
297        let key2 = Pubkey::new_unique();
298        {
299            let message = create_message_with_keys(vec![sysvar::clock::id(), key0, key1, key2]);
300            assert!(message.is_writable_index(0));
301            assert!(!message.is_writable(0));
302        }
303
304        {
305            let message = create_message_with_keys(vec![system_program::id(), key0, key1, key2]);
306            assert!(message.is_writable_index(0));
307            assert!(!message.is_writable(0));
308        }
309
310        {
311            let message = create_message_with_keys(vec![key0, key1, system_program::id(), key2]);
312            assert!(message.is_writable_index(2));
313            assert!(!message.is_writable(2));
314        }
315    }
316
317    #[test]
318    fn test_demote_writable_program() {
319        let key0 = Pubkey::new_unique();
320        let key1 = Pubkey::new_unique();
321        let key2 = Pubkey::new_unique();
322        let message = LoadedMessage::new(
323            v0::Message {
324                header: MessageHeader {
325                    num_required_signatures: 1,
326                    num_readonly_signed_accounts: 0,
327                    num_readonly_unsigned_accounts: 0,
328                },
329                account_keys: vec![key0],
330                instructions: vec![CompiledInstruction {
331                    program_id_index: 2,
332                    accounts: vec![1],
333                    data: vec![],
334                }],
335                ..v0::Message::default()
336            },
337            LoadedAddresses {
338                writable: vec![key1, key2],
339                readonly: vec![],
340            },
341            &HashSet::default(),
342        );
343
344        assert!(message.is_writable_index(2));
345        assert!(!message.is_writable(2));
346    }
347}