light_sdk_types/
address.rs

1#[derive(Debug, PartialEq, Clone, Copy)]
2pub struct AddressSeed(pub [u8; 32]);
3
4impl From<[u8; 32]> for AddressSeed {
5    fn from(value: [u8; 32]) -> Self {
6        AddressSeed(value)
7    }
8}
9
10impl From<AddressSeed> for [u8; 32] {
11    fn from(address_seed: AddressSeed) -> Self {
12        address_seed.0
13    }
14}
15
16pub type CompressedAddress = [u8; 32];
17pub mod v1 {
18    use light_hasher::{
19        hash_to_field_size::hashv_to_bn254_field_size_be_const_array, Hasher, Keccak,
20    };
21
22    use super::AddressSeed;
23
24    /// Derives a single address seed for a compressed account, based on the
25    /// provided multiple `seeds`, `program_id` and `address_tree_pubkey`.
26    ///
27    /// # Examples
28    ///
29    /// ```ignore
30    /// use light_sdk::{address::derive_address, pubkey};
31    ///
32    /// let address = derive_address(
33    ///     &[b"my_compressed_account"],
34    ///     &crate::ID,
35    /// );
36    /// ```
37    pub fn derive_address_seed(seeds: &[&[u8]], program_id: &[u8; 32]) -> AddressSeed {
38        let mut inputs: [&[u8]; 16] = [&[]; 16];
39
40        inputs[0] = program_id.as_slice();
41
42        for (i, seed) in seeds.iter().enumerate() {
43            inputs[i + 1] = seed;
44        }
45
46        let seed = hashv_to_bn254_field_size_be_legacy(inputs.as_slice());
47        AddressSeed(seed)
48    }
49
50    fn hashv_to_bn254_field_size_be_legacy(bytes: &[&[u8]]) -> [u8; 32] {
51        let mut hashed_value: [u8; 32] = Keccak::hashv(bytes)
52            .expect("Keccak::hashv should be infallible when keccak feature is enabled");
53        // Truncates to 31 bytes so that value is less than bn254 Fr modulo
54        // field size.
55        hashed_value[0] = 0;
56        hashed_value
57    }
58
59    /// Derives an address for a compressed account, based on the provided singular
60    /// `seed` and `address_tree_pubkey`:
61    pub(crate) fn derive_address_from_seed(
62        address_seed: &AddressSeed,
63        address_tree_pubkey: &[u8; 32],
64    ) -> [u8; 32] {
65        let input = [address_tree_pubkey.as_slice(), address_seed.0.as_slice()];
66        hashv_to_bn254_field_size_be_const_array::<3>(input.as_slice()).unwrap()
67    }
68
69    /// Derives an address from provided seeds. Returns that address and a singular
70    /// seed.
71    ///
72    /// # Examples
73    ///
74    /// ```ignore
75    /// use light_sdk::{address::derive_address, pubkey};
76    ///
77    /// let address_tree_info = {
78    ///     address_merkle_tree_pubkey: pubkey!("amt1Ayt45jfbdw5YSo7iz6WZxUmnZsQTYXy82hVwyC2"),
79    ///     address_queue_pubkey: pubkey!("aq1S9z4reTSQAdgWHGD2zDaS39sjGrAxbR31vxJ2F4F"),
80    /// };
81    /// let (address, address_seed) = derive_address(
82    ///     &[b"my_compressed_account"],
83    ///     &address_tree_info,
84    ///     &crate::ID,
85    /// );
86    /// ```
87    pub fn derive_address(
88        seeds: &[&[u8]],
89        address_tree_pubkey: &[u8; 32],
90        program_id: &[u8; 32],
91    ) -> ([u8; 32], AddressSeed) {
92        let address_seed = derive_address_seed(seeds, program_id);
93        let address = derive_address_from_seed(&address_seed, address_tree_pubkey);
94
95        (address, address_seed)
96    }
97}
98
99pub mod v2 {
100    use light_hasher::hash_to_field_size::hashv_to_bn254_field_size_be_const_array;
101
102    use super::AddressSeed;
103
104    /// Derives a single address seed for a compressed account, based on the
105    /// provided multiple `seeds`, and `address_tree_pubkey`.
106    ///
107    /// # Examples
108    ///
109    /// ```rust
110    /// use light_sdk_types::address::v2::derive_address_seed;
111    ///
112    /// let address = derive_address_seed(
113    ///     &[b"my_compressed_account".as_slice()],
114    /// );
115    /// ```
116    pub fn derive_address_seed(seeds: &[&[u8]]) -> AddressSeed {
117        // Max 16 seeds + 1 for bump
118        AddressSeed(hashv_to_bn254_field_size_be_const_array::<17>(seeds).unwrap())
119    }
120
121    /// Derives an address for a compressed account, based on the provided singular
122    /// `seed` and `address_tree_pubkey`:
123    pub fn derive_address_from_seed(
124        address_seed: &AddressSeed,
125        address_tree_pubkey: &[u8; 32],
126        program_id: &[u8; 32],
127    ) -> [u8; 32] {
128        light_compressed_account::address::derive_address(
129            &address_seed.0,
130            address_tree_pubkey,
131            program_id,
132        )
133    }
134
135    /// Derives an address from provided seeds. Returns that address and a singular
136    /// seed.
137    ///
138    /// # Examples
139    ///
140    /// ```rust
141    /// use light_sdk_types::{address::v2::derive_address};
142    /// use solana_pubkey::pubkey;
143    ///
144    /// let program_id = pubkey!("GRLu2hKaAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPqX");
145    /// let address_tree_pubkey = pubkey!("amt2kaJA14v3urZbZvnc5v2np8jqvc4Z8zDep5wbtzx");
146    ///
147    /// let (address, address_seed) = derive_address(
148    ///     &[b"my_compressed_account".as_slice()],
149    ///     &address_tree_pubkey.to_bytes(),
150    ///     &program_id.to_bytes(),
151    /// );
152    /// ```
153    pub fn derive_address(
154        seeds: &[&[u8]],
155        address_tree_pubkey: &[u8; 32],
156        program_id: &[u8; 32],
157    ) -> ([u8; 32], AddressSeed) {
158        let address_seed = derive_address_seed(seeds);
159        let address = derive_address_from_seed(&address_seed, address_tree_pubkey, program_id);
160        (address, address_seed)
161    }
162}
163
164#[cfg(test)]
165mod test {
166    use super::v1::*;
167
168    #[allow(dead_code)]
169    #[derive(Debug)]
170    struct AddressTreeInfo {
171        pub address_merkle_tree_pubkey: [u8; 32],
172        pub address_queue_pubkey: [u8; 32],
173    }
174
175    #[test]
176    fn test_derive_address_seed() {
177        use light_macros::pubkey_array;
178        let program_id = pubkey_array!("7yucc7fL3JGbyMwg4neUaenNSdySS39hbAk89Ao3t1Hz");
179
180        let address_seed = derive_address_seed(&[b"foo", b"bar"], &program_id);
181        assert_eq!(
182            address_seed,
183            [
184                0, 246, 150, 3, 192, 95, 53, 123, 56, 139, 206, 179, 253, 133, 115, 103, 120, 155,
185                251, 72, 250, 47, 117, 217, 118, 59, 174, 207, 49, 101, 201, 110
186            ]
187            .into()
188        );
189
190        let address_seed = derive_address_seed(&[b"ayy", b"lmao"], &program_id);
191        assert_eq!(
192            address_seed,
193            [
194                0, 202, 44, 25, 221, 74, 144, 92, 69, 168, 38, 19, 206, 208, 29, 162, 53, 27, 120,
195                214, 152, 116, 15, 107, 212, 168, 33, 121, 187, 10, 76, 233
196            ]
197            .into()
198        );
199    }
200
201    #[test]
202    fn test_derive_address() {
203        use light_macros::pubkey_array;
204        let address_tree_info = AddressTreeInfo {
205            address_merkle_tree_pubkey: [0; 32],
206            address_queue_pubkey: [0; 32],
207        };
208        let program_id = pubkey_array!("7yucc7fL3JGbyMwg4neUaenNSdySS39hbAk89Ao3t1Hz");
209
210        let seeds: &[&[u8]] = &[b"foo", b"bar"];
211        let expected_address_seed = [
212            0, 246, 150, 3, 192, 95, 53, 123, 56, 139, 206, 179, 253, 133, 115, 103, 120, 155, 251,
213            72, 250, 47, 117, 217, 118, 59, 174, 207, 49, 101, 201, 110,
214        ];
215        let expected_address = [
216            0, 141, 60, 24, 250, 156, 15, 250, 237, 196, 171, 243, 182, 10, 8, 66, 147, 57, 27,
217            209, 222, 86, 109, 234, 161, 219, 142, 43, 121, 104, 16, 63,
218        ];
219
220        let address_seed = derive_address_seed(seeds, &program_id);
221        assert_eq!(address_seed, expected_address_seed.into());
222        let (address, address_seed) = derive_address(
223            seeds,
224            &address_tree_info.address_merkle_tree_pubkey,
225            &program_id,
226        );
227        assert_eq!(address_seed, expected_address_seed.into());
228        assert_eq!(address, expected_address);
229
230        let seeds: &[&[u8]] = &[b"ayy", b"lmao"];
231        let expected_address_seed = [
232            0, 202, 44, 25, 221, 74, 144, 92, 69, 168, 38, 19, 206, 208, 29, 162, 53, 27, 120, 214,
233            152, 116, 15, 107, 212, 168, 33, 121, 187, 10, 76, 233,
234        ];
235        let expected_address = [
236            0, 104, 207, 102, 176, 61, 126, 178, 11, 174, 213, 195, 17, 36, 71, 95, 0, 231, 179,
237            87, 218, 195, 114, 84, 47, 97, 176, 93, 106, 175, 72, 115,
238        ];
239
240        let address_seed = derive_address_seed(seeds, &program_id);
241        assert_eq!(address_seed, expected_address_seed.into());
242        let (address, address_seed) = derive_address(
243            seeds,
244            &address_tree_info.address_merkle_tree_pubkey,
245            &program_id,
246        );
247        assert_eq!(address_seed, expected_address_seed.into());
248        assert_eq!(address, expected_address);
249    }
250
251    #[test]
252    fn test_v2_derive_address_seed() {
253        let seeds: &[&[u8]] = &[b"foo", b"bar"];
254        let address_seed = super::v2::derive_address_seed(seeds);
255
256        assert_eq!(
257            address_seed.0,
258            [
259                0, 177, 134, 198, 24, 76, 116, 207, 56, 127, 189, 181, 87, 237, 154, 181, 246, 54,
260                131, 21, 150, 248, 106, 75, 26, 80, 147, 245, 3, 23, 136, 56
261            ]
262        );
263
264        let seeds: &[&[u8]] = &[b"ayy", b"lmao"];
265        let address_seed = super::v2::derive_address_seed(seeds);
266
267        assert_eq!(
268            address_seed.0,
269            [
270                0, 224, 206, 65, 137, 189, 70, 157, 163, 133, 247, 140, 198, 252, 169, 250, 18, 18,
271                16, 189, 164, 131, 225, 113, 197, 225, 64, 81, 175, 154, 221, 28
272            ]
273        );
274    }
275
276    #[test]
277    fn test_v2_derive_address() {
278        use light_macros::pubkey_array;
279        let address_tree_pubkey = [0u8; 32];
280        let program_id = pubkey_array!("7yucc7fL3JGbyMwg4neUaenNSdySS39hbAk89Ao3t1Hz");
281
282        let seeds: &[&[u8]] = &[b"foo", b"bar"];
283
284        let expected_address_seed = [
285            0, 177, 134, 198, 24, 76, 116, 207, 56, 127, 189, 181, 87, 237, 154, 181, 246, 54, 131,
286            21, 150, 248, 106, 75, 26, 80, 147, 245, 3, 23, 136, 56,
287        ];
288        let expected_address = [
289            0, 16, 227, 141, 38, 32, 23, 82, 252, 50, 202, 3, 183, 186, 236, 133, 86, 112, 59, 23,
290            128, 162, 11, 84, 91, 127, 179, 208, 25, 178, 1, 240,
291        ];
292
293        let address_seed = super::v2::derive_address_seed(seeds);
294        assert_eq!(address_seed.0, expected_address_seed);
295
296        let (address, address_seed) =
297            super::v2::derive_address(seeds, &address_tree_pubkey, &program_id);
298        assert_eq!(address_seed.0, expected_address_seed);
299        assert_eq!(address, expected_address);
300
301        let seeds: &[&[u8]] = &[b"ayy", b"lmao"];
302
303        let expected_address_seed = [
304            0, 224, 206, 65, 137, 189, 70, 157, 163, 133, 247, 140, 198, 252, 169, 250, 18, 18, 16,
305            189, 164, 131, 225, 113, 197, 225, 64, 81, 175, 154, 221, 28,
306        ];
307        let expected_address = [
308            0, 226, 28, 142, 199, 153, 126, 212, 37, 54, 82, 232, 244, 161, 108, 12, 67, 84, 111,
309            66, 107, 111, 8, 126, 153, 233, 239, 192, 83, 117, 25, 6,
310        ];
311
312        let address_seed = super::v2::derive_address_seed(seeds);
313        assert_eq!(address_seed.0, expected_address_seed);
314
315        let (address, address_seed) =
316            super::v2::derive_address(seeds, &address_tree_pubkey, &program_id);
317        assert_eq!(address_seed.0, expected_address_seed);
318        assert_eq!(address, expected_address);
319    }
320}