solana_block_decoder/address/
address_table_lookup.rs

1
2use {
3    crate::{
4        errors::{
5            decode_error::DecodeError,
6        }
7    },
8    solana_short_vec as short_vec,
9    solana_pubkey::{Pubkey, ParsePubkeyError},
10    solana_transaction_status_client_types::{
11        UiAddressTableLookup,
12    },
13    serde::{
14        Deserialize, Serialize,
15    },
16    std::{
17        str::FromStr,
18    },
19};
20
21#[derive(Serialize, Deserialize, Default, Debug, PartialEq, Eq, Clone)]
22#[serde(rename_all = "camelCase")]
23pub struct MessageAddressTableLookup {
24    /// Address lookup table account key
25    pub account_key: Pubkey,
26    /// List of indexes used to load writable account addresses
27    #[serde(with = "short_vec")]
28    pub writable_indexes: Vec<u8>,
29    /// List of indexes used to load readonly account addresses
30    #[serde(with = "short_vec")]
31    pub readonly_indexes: Vec<u8>,
32}
33
34impl TryFrom<&UiAddressTableLookup> for MessageAddressTableLookup {
35    type Error = DecodeError;
36
37    fn try_from(lookup: &UiAddressTableLookup) -> Result<Self, Self::Error> {
38        let account_key = Pubkey::from_str(&lookup.account_key)
39            .map_err(|_| DecodeError::ParsePubkeyFailed(ParsePubkeyError::Invalid))?;
40        Ok(Self {
41            account_key,
42            writable_indexes: lookup.writable_indexes.clone(),
43            readonly_indexes: lookup.readonly_indexes.clone(),
44        })
45    }
46}
47
48impl From<MessageAddressTableLookup> for solana_message::v0::MessageAddressTableLookup {
49    fn from(lookup: MessageAddressTableLookup) -> Self {
50        Self {
51            account_key: lookup.account_key,
52            writable_indexes: lookup.writable_indexes,
53            readonly_indexes: lookup.readonly_indexes,
54        }
55    }
56}
57
58
59#[cfg(test)]
60mod tests {
61    use super::*;
62    use std::str::FromStr;
63    use solana_message::v0::MessageAddressTableLookup as SolanaMessageAddressTableLookup;
64    use solana_transaction_status_client_types::UiAddressTableLookup;
65    use serde_json::{self, Value};
66
67    #[test]
68    fn test_try_from_ui_address_table_lookup_success() {
69        let ui_lookup = UiAddressTableLookup {
70            account_key: "11111111111111111111111111111111".to_string(),
71            writable_indexes: vec![1, 2, 3],
72            readonly_indexes: vec![4, 5, 6],
73        };
74
75        let lookup = MessageAddressTableLookup::try_from(&ui_lookup).unwrap();
76        assert_eq!(
77            lookup.account_key,
78            Pubkey::from_str("11111111111111111111111111111111").unwrap()
79        );
80        assert_eq!(lookup.writable_indexes, vec![1, 2, 3]);
81        assert_eq!(lookup.readonly_indexes, vec![4, 5, 6]);
82    }
83
84    #[test]
85    fn test_try_from_ui_address_table_lookup_invalid_pubkey() {
86        let ui_lookup = UiAddressTableLookup {
87            account_key: "invalid_pubkey".to_string(),
88            writable_indexes: vec![1, 2, 3],
89            readonly_indexes: vec![4, 5, 6],
90        };
91
92        let result = MessageAddressTableLookup::try_from(&ui_lookup);
93        assert!(result.is_err());
94    }
95
96    #[test]
97    fn test_from_message_address_table_lookup_to_solana_message() {
98        let lookup = MessageAddressTableLookup {
99            account_key: Pubkey::from_str("11111111111111111111111111111111").unwrap(),
100            writable_indexes: vec![1, 2, 3],
101            readonly_indexes: vec![4, 5, 6],
102        };
103
104        let solana_lookup: SolanaMessageAddressTableLookup = lookup.clone().into();
105        assert_eq!(solana_lookup.account_key, lookup.account_key);
106        assert_eq!(solana_lookup.writable_indexes, lookup.writable_indexes);
107        assert_eq!(solana_lookup.readonly_indexes, lookup.readonly_indexes);
108    }
109
110    #[test]
111    fn test_serde_serialization() {
112        let lookup = MessageAddressTableLookup {
113            // "11111111111111111111111111111111" decodes to 32 bytes of zero in memory
114            account_key: Pubkey::from_str("11111111111111111111111111111111").unwrap(),
115            writable_indexes: vec![1, 2, 3],
116            readonly_indexes: vec![4, 5, 6],
117        };
118
119        let serialized = serde_json::to_string(&lookup).unwrap();
120        // println!("Serialized JSON: {}", serialized);
121
122        // Build the 32 zeros with a local variable
123        let zeros = vec![0u8; 32];
124        let expected_value = serde_json::json!({
125            "accountKey": zeros,                // 32 zeros for "11111111111111111111111111111111"
126            "writableIndexes": [[3], 1, 2, 3],  // short-vec format
127            "readonlyIndexes": [[3], 4, 5, 6]
128        });
129
130        let serialized_value: serde_json::Value = serde_json::from_str(&serialized).unwrap();
131        assert_eq!(serialized_value, expected_value);
132    }
133
134    #[test]
135    fn test_serde_deserialization() {
136        // Create a 32-zero array for base58 "11111111111111111111111111111111"
137        let zeros = vec![0u8; 32];
138        let json_str = serde_json::json!({
139            "accountKey": zeros,
140            "writableIndexes": [[3], 1, 2, 3],
141            "readonlyIndexes": [[3], 4, 5, 6]
142        }).to_string();
143
144        let deserialized: MessageAddressTableLookup = serde_json::from_str(&json_str).unwrap();
145
146        // The internal bytes are all zeros, which `Pubkey::to_string()` yields as "11111111111111111111111111111111".
147        assert_eq!(deserialized.account_key.to_string(), "11111111111111111111111111111111");
148        assert_eq!(deserialized.writable_indexes, vec![1, 2, 3]);
149        assert_eq!(deserialized.readonly_indexes, vec![4, 5, 6]);
150    }
151}