Skip to main content

light_sdk/
address.rs

1//! ## Addresses
2//! Address seed is 32 bytes. Multiple seeds are hashed
3//! into a single 32 bytes seed that is passed into the light system program for address creation.
4//! Addresses are created independently from compressed accounts.
5//! This means that an address can be used in a compressed account but does not have to be used.
6//!
7//! ### Address uniqueness
8//! Every address can only be created once per address tree.
9//! Addresses over all address trees are unique but
10//! address seeds can be reused in different address trees.
11//! If your program security requires global address uniqueness over all address trees,
12//! the used address Merkle tree must be checked.
13//! If your program just requires addresses to identify accounts but not uniqueness over all address trees
14//! the used address Merkle tree does not need to be checked.
15//!
16//!
17//! ### Create address example
18//! ```ignore
19//! let packed_address_tree_info = instruction_data.address_tree_info;
20//! let tree_accounts = cpi_accounts.tree_accounts();
21//!
22//! let address_tree_pubkey = tree_accounts[address_tree_info
23//!    .address_merkle_tree_pubkey_index as usize]
24//!    .key();
25//!
26//! let (address, address_seed) = derive_address(
27//!     &[b"counter"],
28//!     &address_tree_pubkey,
29//!     &crate::ID,
30//! );
31//!
32//! // Used in cpi to light-system program
33//! // to insert the new address into the address merkle tree.
34//! let new_address_params = packed_address_tree_info
35//!     .into_new_address_params_packed(address_seed);
36//! ```
37
38pub use light_compressed_account::instruction_data::data::NewAddressParams;
39/// Struct passed into the light system program cpi to create a new address.
40pub use light_compressed_account::instruction_data::data::NewAddressParamsPacked as PackedNewAddressParams;
41#[cfg(feature = "v2")]
42pub use light_compressed_account::instruction_data::data::{
43    NewAddressParamsAssigned, NewAddressParamsAssignedPacked, PackedReadOnlyAddress,
44    ReadOnlyAddress,
45};
46pub use light_sdk_types::address::AddressSeed;
47
48pub mod v1 {
49
50    use light_sdk_types::address::AddressSeed;
51
52    use crate::Pubkey;
53
54    /// Derives a single address seed for a compressed account, based on the
55    /// provided multiple `seeds`, `program_id` and `address_tree_pubkey`.
56    ///
57    /// # Examples
58    ///
59    /// ```ignore
60    /// use light_sdk::{address::derive_address, pubkey};
61    ///
62    /// let address = derive_address(
63    ///     &[b"my_compressed_account"],
64    ///     &crate::ID,
65    /// );
66    /// ```
67    pub fn derive_address_seed(seeds: &[&[u8]], program_id: &Pubkey) -> AddressSeed {
68        light_sdk_types::address::v1::derive_address_seed(seeds, &program_id.to_bytes())
69    }
70
71    /// Derives an address from provided seeds. Returns that address and a singular
72    /// seed.
73    ///
74    /// # Examples
75    ///
76    /// ```ignore
77    /// use light_sdk::{address::derive_address, pubkey};
78    ///
79    /// let address_tree_info = {
80    ///     address_merkle_tree_pubkey: pubkey!("amt1Ayt45jfbdw5YSo7iz6WZxUmnZsQTYXy82hVwyC2"),
81    ///     address_queue_pubkey: pubkey!("aq1S9z4reTSQAdgWHGD2zDaS39sjGrAxbR31vxJ2F4F"),
82    /// };
83    /// let (address, address_seed) = derive_address(
84    ///     &[b"my_compressed_account"],
85    ///     &address_tree_info,
86    ///     &crate::ID,
87    /// );
88    /// ```
89    pub fn derive_address(
90        seeds: &[&[u8]],
91        address_tree_pubkey: &Pubkey,
92        program_id: &Pubkey,
93    ) -> ([u8; 32], AddressSeed) {
94        light_sdk_types::address::v1::derive_address(
95            seeds,
96            &address_tree_pubkey.to_bytes(),
97            &program_id.to_bytes(),
98        )
99    }
100}
101
102#[cfg(feature = "v2")]
103pub mod v2 {
104    use light_sdk_types::address::AddressSeed;
105    use solana_pubkey::Pubkey;
106
107    /// Derives a single address seed for a compressed account, based on the
108    /// provided multiple `seeds`, and `address_tree_pubkey`.
109    ///
110    /// # Examples
111    ///
112    /// ```rust
113    /// use light_sdk::address::v2::derive_address_seed;
114    ///
115    /// let address = derive_address_seed(
116    ///     &[b"my_compressed_account".as_slice()],
117    /// );
118    /// ```
119    pub fn derive_address_seed(seeds: &[&[u8]]) -> AddressSeed {
120        light_sdk_types::address::v2::derive_address_seed(seeds)
121    }
122
123    /// Derives an address for a compressed account, based on the provided singular
124    /// `seed` and `address_tree_pubkey`:
125    pub fn derive_address_from_seed(
126        address_seed: &AddressSeed,
127        address_tree_pubkey: &Pubkey,
128        program_id: &Pubkey,
129    ) -> [u8; 32] {
130        light_sdk_types::address::v2::derive_address_from_seed(
131            address_seed,
132            &address_tree_pubkey.to_bytes(),
133            &program_id.to_bytes(),
134        )
135    }
136
137    /// Derive address from PDA using Pubkey types.
138    pub fn derive_compressed_address(
139        account_address: &Pubkey,
140        address_tree_pubkey: &Pubkey,
141        program_id: &Pubkey,
142    ) -> [u8; 32] {
143        derive_address(
144            &[account_address.to_bytes().as_ref()],
145            address_tree_pubkey,
146            program_id,
147        )
148        .0
149    }
150
151    /// Derives an address from provided seeds. Returns that address and a singular
152    /// seed.
153    ///
154    /// # Examples
155    ///
156    /// ```rust
157    /// use light_sdk::{address::v2::derive_address};
158    /// use solana_pubkey::pubkey;
159    ///
160    /// let program_id = pubkey!("GRLu2hKaAiMbxpkAM1HeXzks9YeGuz18SEgXEizVvPqX");
161    /// let address_tree_pubkey = pubkey!("amt2kaJA14v3urZbZvnc5v2np8jqvc4Z8zDep5wbtzx");
162    ///
163    /// let (address, address_seed) = derive_address(
164    ///     &[b"my_compressed_account".as_slice()],
165    ///     &address_tree_pubkey,
166    ///     &program_id,
167    /// );
168    /// ```
169    pub fn derive_address(
170        seeds: &[&[u8]],
171        address_tree_pubkey: &Pubkey,
172        program_id: &Pubkey,
173    ) -> ([u8; 32], AddressSeed) {
174        light_sdk_types::address::v2::derive_address(
175            seeds,
176            &address_tree_pubkey.to_bytes(),
177            &program_id.to_bytes(),
178        )
179    }
180}
181
182#[cfg(test)]
183mod test {
184    use solana_pubkey::pubkey;
185
186    use super::v1::*;
187    use crate::instruction::AddressTreeInfo;
188
189    #[test]
190    fn test_derive_address_seed() {
191        let program_id = pubkey!("7yucc7fL3JGbyMwg4neUaenNSdySS39hbAk89Ao3t1Hz");
192
193        let address_seed = derive_address_seed(&[b"foo", b"bar"], &program_id);
194        assert_eq!(
195            address_seed,
196            [
197                0, 246, 150, 3, 192, 95, 53, 123, 56, 139, 206, 179, 253, 133, 115, 103, 120, 155,
198                251, 72, 250, 47, 117, 217, 118, 59, 174, 207, 49, 101, 201, 110
199            ]
200            .into()
201        );
202
203        let address_seed = derive_address_seed(&[b"ayy", b"lmao"], &program_id);
204        assert_eq!(
205            address_seed,
206            [
207                0, 202, 44, 25, 221, 74, 144, 92, 69, 168, 38, 19, 206, 208, 29, 162, 53, 27, 120,
208                214, 152, 116, 15, 107, 212, 168, 33, 121, 187, 10, 76, 233
209            ]
210            .into()
211        );
212    }
213
214    #[test]
215    fn test_derive_address() {
216        let address_tree_info = AddressTreeInfo {
217            tree: pubkey!("11111111111111111111111111111111"),
218            queue: pubkey!("22222222222222222222222222222222222222222222"),
219        };
220        let program_id = pubkey!("7yucc7fL3JGbyMwg4neUaenNSdySS39hbAk89Ao3t1Hz");
221
222        let seeds: &[&[u8]] = &[b"foo", b"bar"];
223        let expected_address_seed = [
224            0, 246, 150, 3, 192, 95, 53, 123, 56, 139, 206, 179, 253, 133, 115, 103, 120, 155, 251,
225            72, 250, 47, 117, 217, 118, 59, 174, 207, 49, 101, 201, 110,
226        ];
227        let expected_address = pubkey!("139uhyyBtEh4e1CBDJ68ooK5nCeWoncZf9HPyAfRrukA");
228
229        let address_seed = derive_address_seed(seeds, &program_id);
230        assert_eq!(address_seed, expected_address_seed.into());
231        let (address, address_seed) = derive_address(seeds, &address_tree_info.tree, &program_id);
232        assert_eq!(address_seed, expected_address_seed.into());
233        assert_eq!(address, expected_address.to_bytes());
234
235        let seeds: &[&[u8]] = &[b"ayy", b"lmao"];
236        let expected_address_seed = [
237            0, 202, 44, 25, 221, 74, 144, 92, 69, 168, 38, 19, 206, 208, 29, 162, 53, 27, 120, 214,
238            152, 116, 15, 107, 212, 168, 33, 121, 187, 10, 76, 233,
239        ];
240        let expected_address = pubkey!("12bhHm6PQjbNmEn3Yu1Gq9k7XwVn2rZpzYokmLwbFazN");
241
242        let address_seed = derive_address_seed(seeds, &program_id);
243        assert_eq!(address_seed, expected_address_seed.into());
244        let (address, address_seed) = derive_address(seeds, &address_tree_info.tree, &program_id);
245        assert_eq!(address_seed, expected_address_seed.into());
246        assert_eq!(address, expected_address.to_bytes());
247    }
248}