1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
//! Unified zero-copy trait family.
//!
//! The Hopper Safety Audit's "structural" recommendation was to
//! consolidate `Pod`, `FixedLayout`, `Projectable`, `SafeProjectable`,
//! `LayoutContract`, header metadata, and schema export into one
//! coherent trait stack. This module delivers the foundation:
//!
//! - [`ZeroCopy`], the canonical "safe to overlay on raw bytes"
//! marker. Equivalent-in-contract to [`Pod`](crate::pod::Pod), which
//! (under the default `hopper-native-backend` + `bytemuck` features)
//! is a sub-trait of `bytemuck::Pod + bytemuck::Zeroable`.
//! `ZeroCopy` is implemented for every `Pod` type via a blanket
//! impl, so existing layouts participate automatically.
//!
//! - [`WireLayout`], a `ZeroCopy` type with a fixed wire size.
//! Declared once via `const WIRE_SIZE = size_of::<Self>()` by
//! default; macros may override if the in-memory and on-wire sizes
//! diverge (none do today, but the hook is there for future
//! compressed / tagged encodings).
//!
//! - [`AccountLayout`], a `WireLayout` that also carries Hopper's
//! account header identity (disc, version, wire fingerprint, schema
//! epoch, type offset). This is the audit's proposed top-level
//! trait, matching its exact member list so the contract is
//! frozen-in-place for migrations and client generation.
//!
//! ## Why three traits, not one
//!
//! The layering mirrors a real capability hierarchy. Every account
//! layout is a wire layout; every wire layout is zero-copy; but not
//! every zero-copy type is a full account layout (`u64`, `WireBool`,
//! `TypedAddress<T>` are zero-copy but carry no header). Splitting
//! the traits lets generic helpers demand just what they need.
//!
//! ## Relation to `LayoutContract`
//!
//! The existing [`crate::layout::LayoutContract`] trait predates this
//! module. `LayoutContract` and `AccountLayout` intentionally overlap:
//! both describe "a Hopper layout with disc/version/layout_id".
//! `AccountLayout` is the audit-blessed name with the richer member
//! list; `LayoutContract` is kept for backward compatibility and gets
//! a blanket impl so any type deriving the latter automatically
//! satisfies the former. New authoring surfaces (the proposed
//! `#[hopper::state]` v2 expansion) should reach for `AccountLayout`.
use crateLayoutContract;
use cratePod;
// ══════════════════════════════════════════════════════════════════════
// Seal (audit final-API Step 5)
// ══════════════════════════════════════════════════════════════════════
/// Internal marker every Hopper-authored zero-copy type stamps itself
/// with. Sealed by convention: it lives in a doc-hidden module so
/// downstream code cannot name it except through the canonical
/// Hopper entry points (`#[hopper::pod]`, `#[hopper::state]`,
/// `hopper_layout!`, and the framework's own primitive wire types).
///
/// This closes the Hopper Safety Audit's final-API-design Step 5:
/// a user bypassing the macro system with a hand-rolled
/// `unsafe impl Pod for Foo {}` cannot accidentally pick up
/// [`ZeroCopy`] for free. The `ZeroCopy` blanket below additionally
/// requires `HopperZeroCopySealed`, which only Hopper-authored
/// surfaces implement.
///
/// Users who legitimately need to extend `ZeroCopy` for a custom
/// primitive can declare `unsafe impl ::hopper_runtime::__sealed::HopperZeroCopySealed for MyType {}`
/// manually, but the path-through-doc-hidden-module signals clearly
/// that they are opting out of the macro's field-level proof.
// ══════════════════════════════════════════════════════════════════════
// ZeroCopy
// ══════════════════════════════════════════════════════════════════════
/// Canonical marker for types that may be overlaid on raw bytes.
///
/// # Safety
///
/// The contract is the same four-point obligation as [`Pod`]:
///
/// 1. Every `[u8; size_of::<T>()]` bit pattern decodes to a valid `T`.
/// 2. `align_of::<T>() == 1`.
/// 3. `T` contains no padding.
/// 4. `T` contains no internal pointers or references.
///
/// # Sealing
///
/// `ZeroCopy` is gated behind the doc-hidden
/// [`__sealed::HopperZeroCopySealed`] marker. Types authored through
/// `#[hopper::pod]`, `#[hopper::state]`, `hopper_layout!`, or one of
/// the framework's own primitive wire types (`WireU64`, `WireBool`,
/// `TypedAddress<T>`, etc.) stamp themselves with the seal
/// automatically. A user bypassing the macros with a bare
/// `unsafe impl Pod` does **not** get `ZeroCopy` for free, which
/// closes the Hopper Safety Audit's Step 5 ("you cannot implement
/// `ZeroCopy` manually, only via macro").
pub unsafe
// Blanket: any `Pod + 'static` type that also carries the seal gets
// `ZeroCopy`. Every Hopper-authored surface carries the seal; the
// blanket plus the seal together mean the trait is free for
// framework users and opaque to bypassing code.
unsafe
// ══════════════════════════════════════════════════════════════════════
// WireLayout
// ══════════════════════════════════════════════════════════════════════
/// A `ZeroCopy` type with a compile-time-known wire size.
///
/// The default associated-const body returns `size_of::<Self>()`,
/// which matches every Hopper layout today. Macros may override it
/// in a future revision if the in-memory and on-wire representations
/// ever diverge (e.g. compact trailing tags for optional fields).
// Blanket: every `ZeroCopy` type gets `WireLayout` with the default
// `WIRE_SIZE`. Keeps the trait free for user code.
// ══════════════════════════════════════════════════════════════════════
// AccountLayout
// ══════════════════════════════════════════════════════════════════════
/// Hopper account layout identity, the top of the unified trait stack.
///
/// This is the audit-blessed trait: its member list matches the PDF's
/// "proposed trait model" section exactly, so Hopper's long-term ABI
/// story is anchored in the vocabulary the audit uses.
///
/// `WIRE_FINGERPRINT` is the first 8 bytes of the canonical SHA-256
/// wire descriptor (see `hopper_macros_proc::state::layout_id_bytes`)
/// reinterpreted as a little-endian `u64`, so the runtime can compare
/// against the on-account header byte-for-byte.
///
/// `SCHEMA_EPOCH` defaults to `1`; programs that publish later epochs
/// via their on-chain manifest bump it to signal a version transition.
// Blanket: every `LayoutContract` type automatically is an
// `AccountLayout`. This makes the transition source-compatible -
// `#[hopper::state]` emits `LayoutContract` today; downstream can
// reach for either trait interchangeably.
//
// Fingerprint translation: `LayoutContract::LAYOUT_ID` is already a
// `[u8; 8]` produced by the canonical wire-descriptor hash. We reinterpret
// it as a little-endian `u64` for the `WIRE_FINGERPRINT` slot.