gemachain_program/message/
mapped.rs

1use {
2    crate::{
3        message::{legacy::BUILTIN_PROGRAMS_KEYS, v0},
4        pubkey::Pubkey,
5        sysvar,
6    },
7    std::{collections::HashSet, convert::TryFrom},
8};
9
10/// Combination of a version #0 message and its mapped addresses
11#[derive(Debug, Clone)]
12pub struct MappedMessage {
13    /// Message which loaded a collection of mapped addresses
14    pub message: v0::Message,
15    /// Collection of mapped addresses loaded by this message
16    pub mapped_addresses: MappedAddresses,
17}
18
19/// Collection of mapped addresses loaded succinctly by a transaction using
20/// on-chain address map accounts.
21#[derive(Clone, Default, Debug, PartialEq, Serialize, Deserialize)]
22pub struct MappedAddresses {
23    /// List of addresses for writable loaded accounts
24    pub writable: Vec<Pubkey>,
25    /// List of addresses for read-only loaded accounts
26    pub readonly: Vec<Pubkey>,
27}
28
29impl MappedMessage {
30    /// Returns an iterator of account key segments. The ordering of segments
31    /// affects how account indexes from compiled instructions are resolved and
32    /// so should not be changed.
33    fn account_keys_segment_iter(&self) -> impl Iterator<Item = &Vec<Pubkey>> {
34        vec![
35            &self.message.account_keys,
36            &self.mapped_addresses.writable,
37            &self.mapped_addresses.readonly,
38        ]
39        .into_iter()
40    }
41
42    /// Returns the total length of loaded accounts for this message
43    pub fn account_keys_len(&self) -> usize {
44        let mut len = 0usize;
45        for key_segment in self.account_keys_segment_iter() {
46            len = len.saturating_add(key_segment.len());
47        }
48        len
49    }
50
51    /// Iterator for the addresses of the loaded accounts for this message
52    pub fn account_keys_iter(&self) -> impl Iterator<Item = &Pubkey> {
53        self.account_keys_segment_iter().flatten()
54    }
55
56    /// Returns true if any account keys are duplicates
57    pub fn has_duplicates(&self) -> bool {
58        let mut uniq = HashSet::new();
59        self.account_keys_iter().any(|x| !uniq.insert(x))
60    }
61
62    /// Returns the address of the account at the specified index of the list of
63    /// message account keys constructed from unmapped keys, followed by mapped
64    /// writable addresses, and lastly the list of mapped readonly addresses.
65    pub fn get_account_key(&self, mut index: usize) -> Option<&Pubkey> {
66        for key_segment in self.account_keys_segment_iter() {
67            if index < key_segment.len() {
68                return Some(&key_segment[index]);
69            }
70            index = index.saturating_sub(key_segment.len());
71        }
72
73        None
74    }
75
76    /// Returns true if the account at the specified index was requested to be
77    /// writable.  This method should not be used directly.
78    fn is_writable_index(&self, key_index: usize) -> bool {
79        let header = &self.message.header;
80        let num_account_keys = self.message.account_keys.len();
81        let num_signed_accounts = usize::from(header.num_required_signatures);
82        if key_index >= num_account_keys {
83            let mapped_addresses_index = key_index.saturating_sub(num_account_keys);
84            mapped_addresses_index < self.mapped_addresses.writable.len()
85        } else if key_index >= num_signed_accounts {
86            let num_unsigned_accounts = num_account_keys.saturating_sub(num_signed_accounts);
87            let num_writable_unsigned_accounts = num_unsigned_accounts
88                .saturating_sub(usize::from(header.num_readonly_unsigned_accounts));
89            let unsigned_account_index = key_index.saturating_sub(num_signed_accounts);
90            unsigned_account_index < num_writable_unsigned_accounts
91        } else {
92            let num_writable_signed_accounts = num_signed_accounts
93                .saturating_sub(usize::from(header.num_readonly_signed_accounts));
94            key_index < num_writable_signed_accounts
95        }
96    }
97
98    /// Returns true if the account at the specified index was loaded as writable
99    pub fn is_writable(&self, key_index: usize, demote_program_write_locks: bool) -> bool {
100        if self.is_writable_index(key_index) {
101            if let Some(key) = self.get_account_key(key_index) {
102                return !(sysvar::is_sysvar_id(key) || BUILTIN_PROGRAMS_KEYS.contains(key)
103                    || (demote_program_write_locks && self.is_key_called_as_program(key_index)));
104            }
105        }
106        false
107    }
108
109    /// Returns true if the account at the specified index is called as a program by an instruction
110    pub fn is_key_called_as_program(&self, key_index: usize) -> bool {
111        if let Ok(key_index) = u8::try_from(key_index) {
112            self.message.instructions
113                .iter()
114                .any(|ix| ix.program_id_index == key_index)
115        } else {
116            false
117        }
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124    use crate::{instruction::CompiledInstruction, message::MessageHeader, system_program, sysvar};
125    use itertools::Itertools;
126
127    fn create_test_mapped_message() -> (MappedMessage, [Pubkey; 6]) {
128        let key0 = Pubkey::new_unique();
129        let key1 = Pubkey::new_unique();
130        let key2 = Pubkey::new_unique();
131        let key3 = Pubkey::new_unique();
132        let key4 = Pubkey::new_unique();
133        let key5 = Pubkey::new_unique();
134
135        let message = MappedMessage {
136            message: v0::Message {
137                header: MessageHeader {
138                    num_required_signatures: 2,
139                    num_readonly_signed_accounts: 1,
140                    num_readonly_unsigned_accounts: 1,
141                },
142                account_keys: vec![key0, key1, key2, key3],
143                ..v0::Message::default()
144            },
145            mapped_addresses: MappedAddresses {
146                writable: vec![key4],
147                readonly: vec![key5],
148            },
149        };
150
151        (message, [key0, key1, key2, key3, key4, key5])
152    }
153
154    #[test]
155    fn test_account_keys_segment_iter() {
156        let (message, keys) = create_test_mapped_message();
157
158        let expected_segments = vec![
159            vec![keys[0], keys[1], keys[2], keys[3]],
160            vec![keys[4]],
161            vec![keys[5]],
162        ];
163
164        let mut iter = message.account_keys_segment_iter();
165        for expected_segment in expected_segments {
166            assert_eq!(iter.next(), Some(&expected_segment));
167        }
168    }
169
170    #[test]
171    fn test_account_keys_len() {
172        let (message, keys) = create_test_mapped_message();
173
174        assert_eq!(message.account_keys_len(), keys.len());
175    }
176
177    #[test]
178    fn test_account_keys_iter() {
179        let (message, keys) = create_test_mapped_message();
180
181        let mut iter = message.account_keys_iter();
182        for expected_key in keys {
183            assert_eq!(iter.next(), Some(&expected_key));
184        }
185    }
186
187    #[test]
188    fn test_has_duplicates() {
189        let message = create_test_mapped_message().0;
190
191        assert!(!message.has_duplicates());
192    }
193
194    #[test]
195    fn test_has_duplicates_with_dupe_keys() {
196        let create_message_with_dupe_keys = |mut keys: Vec<Pubkey>| MappedMessage {
197            message: v0::Message {
198                account_keys: keys.split_off(2),
199                ..v0::Message::default()
200            },
201            mapped_addresses: MappedAddresses {
202                writable: keys.split_off(2),
203                readonly: keys,
204            },
205        };
206
207        let key0 = Pubkey::new_unique();
208        let key1 = Pubkey::new_unique();
209        let key2 = Pubkey::new_unique();
210        let key3 = Pubkey::new_unique();
211        let dupe_key = Pubkey::new_unique();
212
213        let keys = vec![key0, key1, key2, key3, dupe_key, dupe_key];
214        let keys_len = keys.len();
215        for keys in keys.into_iter().permutations(keys_len).unique() {
216            let message = create_message_with_dupe_keys(keys);
217            assert!(message.has_duplicates());
218        }
219    }
220
221    #[test]
222    fn test_get_account_key() {
223        let (message, keys) = create_test_mapped_message();
224
225        assert_eq!(message.get_account_key(0), Some(&keys[0]));
226        assert_eq!(message.get_account_key(1), Some(&keys[1]));
227        assert_eq!(message.get_account_key(2), Some(&keys[2]));
228        assert_eq!(message.get_account_key(3), Some(&keys[3]));
229        assert_eq!(message.get_account_key(4), Some(&keys[4]));
230        assert_eq!(message.get_account_key(5), Some(&keys[5]));
231    }
232
233    #[test]
234    fn test_is_writable_index() {
235        let message = create_test_mapped_message().0;
236
237        assert!(message.is_writable_index(0));
238        assert!(!message.is_writable_index(1));
239        assert!(message.is_writable_index(2));
240        assert!(!message.is_writable_index(3));
241        assert!(message.is_writable_index(4));
242        assert!(!message.is_writable_index(5));
243    }
244
245    #[test]
246    fn test_is_writable() {
247        let mut mapped_msg = create_test_mapped_message().0;
248
249        mapped_msg.message.account_keys[0] = sysvar::clock::id();
250        assert!(mapped_msg.is_writable_index(0));
251        assert!(!mapped_msg.is_writable(0, /*demote_program_write_locks=*/ true));
252
253        mapped_msg.message.account_keys[0] = system_program::id();
254        assert!(mapped_msg.is_writable_index(0));
255        assert!(!mapped_msg.is_writable(0, /*demote_program_write_locks=*/ true));
256    }
257
258    #[test]
259    fn test_demote_writable_program() {
260        let key0 = Pubkey::new_unique();
261        let key1 = Pubkey::new_unique();
262        let key2 = Pubkey::new_unique();
263        let mapped_msg = MappedMessage {
264            message: v0::Message {
265                header: MessageHeader {
266                    num_required_signatures: 1,
267                    num_readonly_signed_accounts: 0,
268                    num_readonly_unsigned_accounts: 0,
269                },
270                account_keys: vec![key0],
271                instructions: vec![
272                    CompiledInstruction {
273                        program_id_index: 2,
274                        accounts: vec![1],
275                        data: vec![],
276                    }
277                ],
278                ..v0::Message::default()
279            },
280            mapped_addresses: MappedAddresses {
281                writable: vec![key1, key2],
282                readonly: vec![],
283            },
284        };
285
286        assert!(mapped_msg.is_writable_index(2));
287        assert!(!mapped_msg.is_writable(2, /*demote_program_write_locks=*/ true));
288    }
289}