hpsvm/
precompile_registry.rs1use std::{collections::HashMap, fmt, marker::PhantomData, sync::Arc};
13
14use agave_feature_set::FeatureSet;
15use solana_address::Address;
16use solana_precompile_error::PrecompileError;
17
18use crate::SvmTypesHost;
19
20pub trait Precompile<T: SvmTypesHost>: Send + Sync {
28 fn program_id(&self) -> Address;
30
31 fn is_enabled(&self, _feature_set: &FeatureSet) -> bool {
37 true
38 }
39
40 fn verify(
42 &self,
43 data: &[u8],
44 slices: &[&[u8]],
45 feature_set: &FeatureSet,
46 ) -> Result<(), PrecompileError>;
47}
48
49pub struct PrecompileSet<T: SvmTypesHost> {
54 entries: HashMap<Address, Arc<dyn Precompile<T>>>,
55 _marker: PhantomData<fn() -> T>,
58}
59
60impl<T: SvmTypesHost> PrecompileSet<T> {
61 pub fn new() -> Self {
63 Self { entries: HashMap::new(), _marker: PhantomData }
64 }
65
66 pub fn insert(&mut self, precompile: Arc<dyn Precompile<T>>) -> Option<Arc<dyn Precompile<T>>> {
69 self.entries.insert(precompile.program_id(), precompile)
70 }
71
72 pub fn get(&self, program_id: &Address) -> Option<Arc<dyn Precompile<T>>> {
74 self.entries.get(program_id).cloned()
75 }
76
77 pub fn contains(&self, program_id: &Address) -> bool {
79 self.entries.contains_key(program_id)
80 }
81
82 pub fn len(&self) -> usize {
84 self.entries.len()
85 }
86
87 pub fn is_empty(&self) -> bool {
89 self.entries.is_empty()
90 }
91
92 pub fn program_ids(&self) -> Vec<Address> {
94 self.entries.keys().cloned().collect()
95 }
96}
97
98impl<T: SvmTypesHost> Clone for PrecompileSet<T> {
99 fn clone(&self) -> Self {
100 Self { entries: self.entries.clone(), _marker: PhantomData }
101 }
102}
103
104impl<T: SvmTypesHost> Default for PrecompileSet<T> {
105 fn default() -> Self {
106 Self::new()
107 }
108}
109
110impl<T: SvmTypesHost> fmt::Debug for PrecompileSet<T> {
111 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112 f.debug_struct("PrecompileSet").field("program_ids", &self.program_ids()).finish()
113 }
114}
115
116pub trait PrecompileProvider<T: SvmTypesHost>: Send + Sync {
125 fn get(&self, program_id: &Address) -> Option<Arc<dyn Precompile<T>>>;
127
128 fn program_ids(&self) -> Vec<Address>;
130}
131
132impl<T: SvmTypesHost> PrecompileProvider<T> for PrecompileSet<T> {
133 fn get(&self, program_id: &Address) -> Option<Arc<dyn Precompile<T>>> {
134 self.entries.get(program_id).cloned()
135 }
136
137 fn program_ids(&self) -> Vec<Address> {
138 self.entries.keys().cloned().collect()
139 }
140}
141
142#[derive(Clone, Copy, Debug, Default)]
147pub struct NoPrecompiles;
148
149impl<T: SvmTypesHost> PrecompileProvider<T> for NoPrecompiles {
150 fn get(&self, _program_id: &Address) -> Option<Arc<dyn Precompile<T>>> {
151 None
152 }
153
154 fn program_ids(&self) -> Vec<Address> {
155 Vec::new()
156 }
157}
158
159#[cfg(feature = "precompiles")]
167pub struct NativePrecompiles<T: SvmTypesHost> {
168 set: PrecompileSet<T>,
169}
170
171#[cfg(feature = "precompiles")]
172impl<T: SvmTypesHost> NativePrecompiles<T> {
173 pub fn new() -> Self {
175 let mut set = PrecompileSet::new();
176 for precompile in agave_precompiles::get_precompiles() {
177 set.insert(Arc::new(NativePrecompile {
178 program_id: precompile.program_id,
179 feature: precompile.feature,
180 verify_fn: precompile.verify_fn,
181 }));
182 }
183 Self { set }
184 }
185
186 pub fn into_set(self) -> PrecompileSet<T> {
190 self.set
191 }
192}
193
194#[cfg(feature = "precompiles")]
195impl<T: SvmTypesHost> Clone for NativePrecompiles<T> {
196 fn clone(&self) -> Self {
197 Self { set: self.set.clone() }
198 }
199}
200
201#[cfg(feature = "precompiles")]
202impl<T: SvmTypesHost> Default for NativePrecompiles<T> {
203 fn default() -> Self {
204 Self::new()
205 }
206}
207
208#[cfg(feature = "precompiles")]
209impl<T: SvmTypesHost> fmt::Debug for NativePrecompiles<T> {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 f.debug_struct("NativePrecompiles").field("program_ids", &self.set.program_ids()).finish()
212 }
213}
214
215#[cfg(feature = "precompiles")]
216impl<T: SvmTypesHost> PrecompileProvider<T> for NativePrecompiles<T> {
217 fn get(&self, program_id: &Address) -> Option<Arc<dyn Precompile<T>>> {
218 self.set.entries.get(program_id).cloned()
219 }
220
221 fn program_ids(&self) -> Vec<Address> {
222 self.set.program_ids()
223 }
224}
225
226#[cfg(feature = "precompiles")]
229#[derive(Clone, Copy, Debug)]
230struct NativePrecompile {
231 program_id: Address,
232 feature: Option<Address>,
233 verify_fn: agave_precompiles::Verify,
234}
235
236#[cfg(feature = "precompiles")]
237impl NativePrecompile {
238 fn enabled(&self, feature_set: &FeatureSet) -> bool {
240 self.feature.is_none_or(|feature| feature_set.is_active(&feature))
244 }
245}
246
247#[cfg(feature = "precompiles")]
248impl<T: SvmTypesHost> Precompile<T> for NativePrecompile {
249 fn program_id(&self) -> Address {
250 self.program_id
251 }
252
253 fn is_enabled(&self, feature_set: &FeatureSet) -> bool {
254 self.enabled(feature_set)
255 }
256
257 fn verify(
258 &self,
259 data: &[u8],
260 slices: &[&[u8]],
261 feature_set: &FeatureSet,
262 ) -> Result<(), PrecompileError> {
263 if self.enabled(feature_set) {
268 (self.verify_fn)(data, slices, feature_set)
269 } else {
270 Err(PrecompileError::InvalidPublicKey)
271 }
272 }
273}
274
275#[cfg(test)]
276mod tests {
277 use super::*;
278 use crate::BaseSvmTypes;
279
280 #[derive(Debug)]
281 struct StubPrecompile {
282 program_id: Address,
283 }
284
285 impl<T: SvmTypesHost> Precompile<T> for StubPrecompile {
286 fn program_id(&self) -> Address {
287 self.program_id
288 }
289
290 fn verify(
291 &self,
292 _data: &[u8],
293 _slices: &[&[u8]],
294 _feature_set: &FeatureSet,
295 ) -> Result<(), PrecompileError> {
296 Ok(())
297 }
298 }
299
300 fn stub() -> Arc<dyn Precompile<BaseSvmTypes>> {
301 Arc::new(StubPrecompile { program_id: Address::new_unique() })
302 }
303
304 #[test]
305 fn new_set_is_empty() {
306 let set = PrecompileSet::<BaseSvmTypes>::new();
307 assert!(set.is_empty());
308 assert_eq!(set.len(), 0);
309 assert!(set.program_ids().is_empty());
310 }
311
312 #[test]
313 fn insert_get_contains_and_ids() {
314 let first = stub();
315 let second = stub();
316 let mut set = PrecompileSet::<BaseSvmTypes>::new();
317 assert!(set.insert(Arc::clone(&first)).is_none());
318 assert!(set.insert(Arc::clone(&second)).is_none());
319
320 assert_eq!(set.len(), 2);
321 assert!(!set.is_empty());
322 assert!(set.contains(&first.program_id()));
323 assert!(set.contains(&second.program_id()));
324 assert!(set.get(&first.program_id()).is_some());
325 assert!(set.get(&Address::new_unique()).is_none());
326
327 let mut ids = set.program_ids();
328 ids.sort();
329 let mut want = [first.program_id(), second.program_id()];
330 want.sort();
331 assert_eq!(ids, want);
332 }
333
334 #[test]
335 fn insert_under_same_id_shadows_previous_entry() {
336 let program_id = Address::new_unique();
337 let first: Arc<dyn Precompile<BaseSvmTypes>> = Arc::new(StubPrecompile { program_id });
338 let second: Arc<dyn Precompile<BaseSvmTypes>> = Arc::new(StubPrecompile { program_id });
339 let mut set = PrecompileSet::<BaseSvmTypes>::new();
340 assert!(set.insert(Arc::clone(&first)).is_none());
341
342 let shadowed = set.insert(Arc::clone(&second));
343 assert!(shadowed.is_some());
344 assert!(Arc::ptr_eq(&shadowed.unwrap(), &first));
345 assert_eq!(set.len(), 1);
346 assert!(Arc::ptr_eq(&set.get(&program_id).unwrap(), &second));
347 }
348
349 #[test]
350 fn clone_shares_handlers() {
351 let entry = stub();
352 let mut set = PrecompileSet::<BaseSvmTypes>::new();
353 set.insert(Arc::clone(&entry));
354 let cloned = set.clone();
355
356 assert!(Arc::ptr_eq(&set.get(&entry.program_id()).unwrap(), &entry));
357 assert!(Arc::ptr_eq(&cloned.get(&entry.program_id()).unwrap(), &entry));
358 }
359
360 #[test]
361 fn no_precompiles_enables_nothing() {
362 let provider = NoPrecompiles;
363 let typed: &dyn PrecompileProvider<BaseSvmTypes> = &provider;
364 assert!(typed.get(&Address::new_unique()).is_none());
365 assert!(typed.program_ids().is_empty());
366 }
367
368 #[test]
369 fn set_is_its_own_provider() {
370 let entry = stub();
371 let mut set = PrecompileSet::<BaseSvmTypes>::new();
372 set.insert(Arc::clone(&entry));
373 let provider: &dyn PrecompileProvider<BaseSvmTypes> = &set;
374
375 assert!(provider.get(&entry.program_id()).is_some());
376 assert_eq!(provider.program_ids(), [entry.program_id()]);
377 }
378
379 #[cfg(feature = "precompiles")]
380 #[test]
381 fn native_set_resolves_every_table_id() {
382 let native = NativePrecompiles::<BaseSvmTypes>::new();
383 for precompile in agave_precompiles::get_precompiles() {
384 let handler = native.get(&precompile.program_id);
385 assert!(handler.is_some(), "missing handler for {}", precompile.program_id);
386 assert!(Arc::ptr_eq(
387 &handler.unwrap(),
388 &PrecompileProvider::<BaseSvmTypes>::get(&native, &precompile.program_id).unwrap()
389 ));
390 }
391 assert!(!native.program_ids().is_empty());
392 }
393}