hpsvm/svm_types.rs
1//! The SVM type family: one trait whose associated types describe every
2//! swappable axis of the VM.
3//!
4//! The design intent is that a fork changes one trait impl and inherits the
5//! whole machinery. Extension slots default to the unit type, so an unused
6//! extension point is zero-sized and monomorphizes away; nothing that does not
7//! read a slot pays for it.
8//!
9//! This mirrors the `EvmTypesHost` family of the reference project: the
10//! associated types are the spec identifier, the transaction envelope, the
11//! six extension slots, and the host itself.
12//!
13//! # Deferred bounds
14//!
15//! None remain: `type SpecId: Copy + Into<ClusterSpecId>` is expressed now that
16//! `Version` (Task 2.1) provides [`ClusterSpecId`].
17
18use solana_transaction::versioned::VersionedTransaction;
19
20use crate::{ClusterSpecId, Svm, host::Host};
21
22/// One associated-type family describing every swappable axis of the VM.
23///
24/// A type implementing this trait is a *type family*: it selects the spec
25/// identifier, the transaction envelope, the six extension slots and the host
26/// the rest of the workspace talks to. Everything downstream is written once
27/// against the trait and instantiated per family.
28pub trait SvmTypesHost: Sized + 'static {
29 /// The spec identifier describing the active protocol profile.
30 type SpecId: Copy + Into<ClusterSpecId>;
31 /// The transaction envelope the VM accepts.
32 type Tx;
33 /// VM-wide extension state, shared by every transaction the host runs.
34 type SvmExt;
35 /// Per-message extension state.
36 type MessageExt;
37 /// Per-message result extension state.
38 type MessageResultExt;
39 /// Transaction-environment extension state.
40 type TxEnvExt;
41 /// Transaction-result extension state.
42 type TxResultExt;
43 /// Block-environment extension state.
44 ///
45 /// The VM stores this value inside [`BlockEnv`](crate::BlockEnv), hands it
46 /// out by value on every host access, and shares the VM across Rayon batch
47 /// workers, so it must stay small, copyable, debuggable,
48 /// default-constructible and thread-shareable. The base family sets it to
49 /// `()`.
50 type BlockEnvExt: Clone + Copy + std::fmt::Debug + Default + Send + Sync;
51 /// The host this family runs against.
52 type Host<'a>: Host<Self> + ?Sized;
53}
54
55/// Blanket marker for a family whose host is the VM itself.
56///
57/// Requiring `for<'a> SvmTypesHost<Host<'a> = Svm<'a, Self>>` is what makes "the
58/// host is this VM" a type-system fact rather than a runtime convention: a type
59/// can only satisfy this marker by pointing its host associated type at the VM
60/// parameterized by itself.
61pub trait SvmTypes: for<'a> SvmTypesHost<Host<'a> = Svm<'a, Self>> {}
62
63/// The base type family: every extension slot is the unit type, except the
64/// spec identifier, which is the [`ClusterSpecId`][crate::ClusterSpecId] the
65/// VM's [`Version`][crate::Version] tracks.
66///
67/// This is the family the rest of the workspace instantiates through the
68/// [`Hpsvm`](crate::Hpsvm) alias, and the one a fork starts from before
69/// replacing the slots it cares about.
70#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
71pub struct BaseSvmTypes;
72
73impl SvmTypesHost for BaseSvmTypes {
74 type SpecId = ClusterSpecId;
75 type Tx = VersionedTransaction;
76 type SvmExt = ();
77 type MessageExt = ();
78 type MessageResultExt = ();
79 type TxEnvExt = ();
80 type TxResultExt = ();
81 type BlockEnvExt = ();
82 // Deferred: `Host<Self> + ?Sized` once the typed host boundary exists.
83 type Host<'a> = Svm<'a, Self>;
84}
85
86impl SvmTypes for BaseSvmTypes {}
87
88#[cfg(test)]
89mod tests {
90 use std::mem::size_of;
91
92 use solana_transaction::versioned::VersionedTransaction;
93
94 use super::*;
95
96 /// Every extension slot of the base family must be zero-sized, so an
97 /// unused extension point disappears from the generated code. The spec-id
98 /// slot is the exception: it names a [`ClusterSpecId`][crate::ClusterSpecId]
99 /// (`repr(u32)`, 4 bytes), not the unit type.
100 #[test]
101 fn base_svm_types_extension_slots_are_zero_sized() {
102 use crate::ClusterSpecId;
103 assert_eq!(size_of::<BaseSvmTypes>(), 0);
104 assert_eq!(size_of::<<BaseSvmTypes as SvmTypesHost>::SpecId>(), size_of::<ClusterSpecId>());
105 assert_eq!(size_of::<<BaseSvmTypes as SvmTypesHost>::SpecId>(), 4);
106 assert_eq!(size_of::<<BaseSvmTypes as SvmTypesHost>::SvmExt>(), 0);
107 assert_eq!(size_of::<<BaseSvmTypes as SvmTypesHost>::MessageExt>(), 0);
108 assert_eq!(size_of::<<BaseSvmTypes as SvmTypesHost>::MessageResultExt>(), 0);
109 assert_eq!(size_of::<<BaseSvmTypes as SvmTypesHost>::TxEnvExt>(), 0);
110 assert_eq!(size_of::<<BaseSvmTypes as SvmTypesHost>::TxResultExt>(), 0);
111 assert_eq!(size_of::<<BaseSvmTypes as SvmTypesHost>::BlockEnvExt>(), 0);
112 }
113
114 /// The envelope slot is the one non-unit associated type: it must stay the
115 /// transaction type the rest of the workspace already threads through.
116 #[test]
117 fn base_svm_types_selects_the_versioned_transaction_envelope() {
118 let envelope: Option<<BaseSvmTypes as SvmTypesHost>::Tx> = None::<VersionedTransaction>;
119 assert!(envelope.is_none());
120 }
121
122 /// The marker can only be satisfied by a family that points its host at the
123 /// VM itself, which is what keeps `Host<'a>` from drifting to a look-alike.
124 #[test]
125 fn base_svm_types_satisfies_the_blanket_marker() {
126 fn assert_marker<T: SvmTypes>() {}
127 assert_marker::<BaseSvmTypes>();
128 }
129
130 /// The base host is exactly the type the `Hpsvm` alias names, so the
131 /// pre-existing public-behaviour suite keeps compiling unchanged.
132 #[test]
133 fn the_base_host_is_the_compatibility_alias() {
134 fn same_host(host: Svm<'static, BaseSvmTypes>) -> crate::Hpsvm {
135 host
136 }
137
138 let aliased: crate::Hpsvm = same_host(Svm::<BaseSvmTypes>::new());
139 assert_eq!(aliased.latest_blockhash(), crate::Hpsvm::new().latest_blockhash());
140 }
141}