light_sdk_types/
address.rs

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