Skip to main content

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}