1use {
2 crate::config::ExecutionCost,
3 sbpf_vm::{
4 compute::ComputeMeter,
5 errors::{SbpfVmError, SbpfVmResult},
6 memory::Memory,
7 },
8 solana_address::Address,
9};
10
11const MAX_SEED_LEN: usize = 32;
12const MAX_SEEDS: usize = 16;
13
14fn read_seeds(memory: &Memory, seeds_addr: u64, seeds_len: u64) -> SbpfVmResult<Vec<Vec<u8>>> {
15 if seeds_len as usize > MAX_SEEDS {
16 return Err(SbpfVmError::MaxSeedLengthExceeded);
17 }
18
19 let mut seeds = Vec::with_capacity(seeds_len as usize);
20 for i in 0..seeds_len {
21 let slice_addr = seeds_addr.saturating_add(i.saturating_mul(16));
22 let ptr = memory.read_u64(slice_addr)?;
23 let len = memory.read_u64(slice_addr.saturating_add(8))?;
24
25 if len as usize > MAX_SEED_LEN {
26 return Err(SbpfVmError::MaxSeedLengthExceeded);
27 }
28
29 seeds.push(memory.read_bytes(ptr, len as usize)?.to_vec());
30 }
31 Ok(seeds)
32}
33
34pub fn sol_create_program_address(
35 registers: [u64; 5],
36 memory: &mut Memory,
37 compute: &ComputeMeter,
38 costs: &ExecutionCost,
39) -> SbpfVmResult<u64> {
40 let seeds_addr = registers[0];
41 let seeds_len = registers[1];
42 let program_id_addr = registers[2];
43 let address_addr = registers[3];
44
45 compute.consume(costs.create_program_address_units)?;
46
47 let seeds = read_seeds(memory, seeds_addr, seeds_len)?;
48 let program_id = Address::from(
49 <[u8; 32]>::try_from(memory.read_bytes(program_id_addr, 32)?)
50 .map_err(|_| SbpfVmError::InvalidSliceConversion)?,
51 );
52
53 let seed_slices: Vec<&[u8]> = seeds.iter().map(|s| s.as_slice()).collect();
54 match Address::create_program_address(&seed_slices, &program_id) {
55 Ok(addr) => {
56 memory.write_bytes(address_addr, addr.as_ref())?;
57 Ok(0)
58 }
59 Err(_) => Ok(1),
60 }
61}
62
63pub fn sol_try_find_program_address(
64 registers: [u64; 5],
65 memory: &mut Memory,
66 compute: &ComputeMeter,
67 costs: &ExecutionCost,
68) -> SbpfVmResult<u64> {
69 let seeds_addr = registers[0];
70 let seeds_len = registers[1];
71 let program_id_addr = registers[2];
72 let address_addr = registers[3];
73 let bump_seed_addr = registers[4];
74
75 let cost = costs.create_program_address_units;
77 compute.consume(cost)?;
78
79 let seeds = read_seeds(memory, seeds_addr, seeds_len)?;
80 let program_id = Address::from(
81 <[u8; 32]>::try_from(memory.read_bytes(program_id_addr, 32)?)
82 .map_err(|_| SbpfVmError::InvalidSliceConversion)?,
83 );
84
85 let mut bump_seed = [u8::MAX];
86 for _ in 0..u8::MAX {
87 let mut seeds_with_bump: Vec<&[u8]> = seeds.iter().map(|s| s.as_slice()).collect();
88 seeds_with_bump.push(&bump_seed);
89
90 if let Ok(addr) = Address::create_program_address(&seeds_with_bump, &program_id) {
91 memory.write_u8(bump_seed_addr, bump_seed[0])?;
92 memory.write_bytes(address_addr, addr.as_ref())?;
93 return Ok(0);
94 }
95
96 bump_seed[0] = bump_seed[0].checked_sub(1).unwrap();
97 compute.consume(cost)?;
99 }
100 Ok(1)
101}
102
103#[cfg(test)]
104mod tests {
105 use {
106 super::*,
107 crate::syscalls::tests::test_helpers::{costs, make_memory, meter},
108 sbpf_vm::{errors::SbpfVmError, memory::Memory},
109 solana_address::Address,
110 };
111
112 fn setup_seeds(
113 memory: &mut Memory,
114 seeds: &[&[u8]],
115 program_id: &Address,
116 ) -> (u64, u64, u64, u64) {
117 let mut seed_data_addrs = Vec::new();
119 for (i, seed) in seeds.iter().enumerate() {
120 let addr = Memory::HEAP_START + (i as u64 * 64);
121 memory.write_bytes(addr, seed).unwrap();
122 seed_data_addrs.push((addr, seed.len() as u64));
123 }
124
125 let seeds_addr = Memory::HEAP_START + 1024;
127 for (i, (ptr, len)) in seed_data_addrs.iter().enumerate() {
128 let desc = seeds_addr + (i as u64 * 16);
129 memory.write_u64(desc, *ptr).unwrap();
130 memory.write_u64(desc + 8, *len).unwrap();
131 }
132
133 let program_id_addr = Memory::HEAP_START + 2048;
134 memory
135 .write_bytes(program_id_addr, program_id.as_ref())
136 .unwrap();
137
138 let address_out_addr = Memory::HEAP_START + 2096;
139
140 (
141 seeds_addr,
142 seeds.len() as u64,
143 program_id_addr,
144 address_out_addr,
145 )
146 }
147
148 #[test]
149 fn test_create_program_address_valid_pda() {
150 let mut memory = make_memory();
151 let program_id = Address::from([2u8; 32]);
152
153 let (expected_addr, bump) = Address::find_program_address(&[b"test"], &program_id);
155 let bump_bytes = [bump];
156
157 let (seeds_addr, seeds_len, program_id_addr, address_out_addr) =
158 setup_seeds(&mut memory, &[b"test", &bump_bytes], &program_id);
159
160 let registers = [seeds_addr, seeds_len, program_id_addr, address_out_addr, 0];
161 let ret = sol_create_program_address(registers, &mut memory, &meter(1_000_000), &costs())
162 .unwrap();
163
164 assert_eq!(ret, 0, "should return 0 for a valid PDA");
165 let written_addr: [u8; 32] = memory
166 .read_bytes(address_out_addr, 32)
167 .unwrap()
168 .try_into()
169 .unwrap();
170 assert_eq!(Address::from(written_addr), expected_addr);
171 }
172
173 #[test]
174 fn test_create_program_address_too_many_seeds() {
175 let mut memory = make_memory();
176 let program_id = Address::from([1u8; 32]);
177 let seeds: Vec<&[u8]> = (0..17).map(|_| b"s".as_ref()).collect();
179 let (seeds_addr, seeds_len, program_id_addr, address_out_addr) =
180 setup_seeds(&mut memory, &seeds, &program_id);
181
182 let registers = [seeds_addr, seeds_len, program_id_addr, address_out_addr, 0];
183 assert!(matches!(
184 sol_create_program_address(registers, &mut memory, &meter(1_000_000), &costs()),
185 Err(SbpfVmError::MaxSeedLengthExceeded)
186 ));
187 }
188
189 #[test]
190 fn test_create_program_address_seed_too_long() {
191 let mut memory = make_memory();
192 let program_id = Address::from([1u8; 32]);
193 let long_seed = [0u8; 33];
195 let (seeds_addr, seeds_len, program_id_addr, address_out_addr) =
196 setup_seeds(&mut memory, &[&long_seed], &program_id);
197
198 let registers = [seeds_addr, seeds_len, program_id_addr, address_out_addr, 0];
199 assert!(matches!(
200 sol_create_program_address(registers, &mut memory, &meter(1_000_000), &costs()),
201 Err(SbpfVmError::MaxSeedLengthExceeded)
202 ));
203 }
204
205 #[test]
206 fn test_create_program_address_compute_exhausted() {
207 let mut memory = make_memory();
208 let program_id = Address::from([1u8; 32]);
209 let (seeds_addr, seeds_len, program_id_addr, address_out_addr) =
210 setup_seeds(&mut memory, &[b"seed"], &program_id);
211
212 let registers = [seeds_addr, seeds_len, program_id_addr, address_out_addr, 0];
213 assert!(matches!(
215 sol_create_program_address(registers, &mut memory, &meter(1499), &costs()),
216 Err(SbpfVmError::ComputeBudgetExceeded { .. })
217 ));
218 }
219
220 #[test]
221 fn test_try_find_program_address_matches_library() {
222 let mut memory = make_memory();
223 let program_id = Address::from([3u8; 32]);
224
225 let (seeds_addr, seeds_len, program_id_addr, address_out_addr) =
226 setup_seeds(&mut memory, &[b"find-me"], &program_id);
227
228 let bump_out_addr = Memory::HEAP_START + 2200;
229
230 let registers = [
231 seeds_addr,
232 seeds_len,
233 program_id_addr,
234 address_out_addr,
235 bump_out_addr,
236 ];
237 let ret = sol_try_find_program_address(registers, &mut memory, &meter(1_000_000), &costs())
238 .unwrap();
239
240 assert_eq!(ret, 0, "should find a valid PDA");
241
242 let written_bump = memory.read_u8(bump_out_addr).unwrap();
243 let written_addr: [u8; 32] = memory
244 .read_bytes(address_out_addr, 32)
245 .unwrap()
246 .try_into()
247 .unwrap();
248
249 let (expected_addr, expected_bump) =
250 Address::find_program_address(&[b"find-me"], &program_id);
251
252 assert_eq!(written_bump, expected_bump);
253 assert_eq!(Address::from(written_addr), expected_addr);
254 }
255
256 #[test]
257 fn test_try_find_program_address_too_many_seeds() {
258 let mut memory = make_memory();
259 let program_id = Address::from([1u8; 32]);
260 let seeds: Vec<&[u8]> = (0..17).map(|_| b"x".as_ref()).collect();
261 let (seeds_addr, seeds_len, program_id_addr, address_out_addr) =
262 setup_seeds(&mut memory, &seeds, &program_id);
263
264 let bump_out_addr = Memory::HEAP_START + 2200;
265 let registers = [
266 seeds_addr,
267 seeds_len,
268 program_id_addr,
269 address_out_addr,
270 bump_out_addr,
271 ];
272 assert!(matches!(
273 sol_try_find_program_address(registers, &mut memory, &meter(1_000_000), &costs()),
274 Err(SbpfVmError::MaxSeedLengthExceeded)
275 ));
276 }
277}