Line data Source code
1 : #ifndef HEADER_fd_src_flamenco_runtime_program_fd_system_program_h
2 : #define HEADER_fd_src_flamenco_runtime_program_fd_system_program_h
3 :
4 : /* fd_system_program.h provides hand-written types and zero-copy
5 : parsers for Solana's SystemInstruction enum, plus entrypoints for
6 : the system program.
7 :
8 : The decoder produces pointers into the caller-owned instruction
9 : buffer for variable-length fields (seeds). Callers must keep the
10 : instruction buffer alive for the lifetime of the decoded struct.
11 :
12 : https://github.com/anza-xyz/solana-sdk/blob/system-interface%40v3.0.0/system-interface/src/instruction.rs#L92-L299 */
13 :
14 : #include "../../fd_flamenco_base.h"
15 : #include "../../../ballet/utf8/fd_utf8.h"
16 :
17 : /* Custom error types */
18 :
19 12 : #define FD_SYSTEM_PROGRAM_ERR_ACCT_ALREADY_IN_USE (0) /* SystemError::AccountAlreadyInUse */
20 3 : #define FD_SYSTEM_PROGRAM_ERR_RESULT_WITH_NEGATIVE_LAMPORTS (1) /* SystemError::ResultWithNegativeLamports */
21 : #define FD_SYSTEM_PROGRAM_ERR_INVALID_PROGRAM_ID (2) /* SystemError::InvalidProgramId */
22 3 : #define FD_SYSTEM_PROGRAM_ERR_INVALID_ACCT_DATA_LEN (3) /* SystemError::InvalidAccountDataLength */
23 : #define FD_SYSTEM_PROGRAM_ERR_MAX_SEED_LEN_EXCEEDED (4) /* SystemError::MaxSeedLengthExceeded */
24 0 : #define FD_SYSTEM_PROGRAM_ERR_ADDR_WITH_SEED_MISMATCH (5) /* SystemError::AddressWithSeedMismatch */
25 0 : #define FD_SYSTEM_PROGRAM_ERR_NONCE_NO_RECENT_BLOCKHASHES (6) /* SystemError::NonceNoRecentBlockhashes */
26 0 : #define FD_SYSTEM_PROGRAM_ERR_NONCE_BLOCKHASH_NOT_EXPIRED (7) /* SystemError::NonceBlockhashNotExpired */
27 : #define FD_SYSTEM_PROGRAM_ERR_NONCE_UNEXPECTED_BLOCKHASH_VALUE (8) /* SystemError::NonceUnexpectedBlockhashValue */
28 :
29 12 : #define FD_SYSTEM_PROGRAM_NONCE_DLEN (80UL)
30 :
31 : /* Nonce account state bincode wire format (must match Agave byte-for-byte).
32 :
33 : https://github.com/anza-xyz/solana-sdk/blob/nonce%40v3.0.0/nonce/src/versions.rs
34 : https://github.com/anza-xyz/solana-sdk/blob/nonce%40v3.0.0/nonce/src/state.rs
35 :
36 : Outer enum `Versions` (4-byte u32 LE discriminant):
37 : 0 = Legacy(Box<State>)
38 : 1 = Current(Box<State>)
39 : Inner enum `State` (4-byte u32 LE discriminant):
40 : 0 = Uninitialized (no payload; total wire size = 8)
41 : 1 = Initialized(Data) (payload = 72 bytes; total wire size = 80)
42 : Data struct (72 bytes fixed):
43 : pubkey authority[32] | hash durable_nonce[32] | u64 lamports_per_signature
44 :
45 : Agave uses bincode 1.3.3 `DefaultOptions::with_fixint_encoding().
46 : allow_trailing_bytes()` via the top-level `bincode::deserialize` helper
47 : (TransactionContext::get_state). Decoders must therefore accept trailing
48 : bytes beyond the parsed region. */
49 :
50 : #define FD_NONCE_VERSION_LEGACY (0U)
51 30 : #define FD_NONCE_VERSION_CURRENT (1U)
52 :
53 9 : #define FD_NONCE_STATE_UNINITIALIZED (0U)
54 102 : #define FD_NONCE_STATE_INITIALIZED (1U)
55 :
56 0 : #define FD_NONCE_STATE_UNINITIALIZED_SZ (8UL)
57 21 : #define FD_NONCE_STATE_INITIALIZED_SZ (80UL)
58 :
59 : /* https://github.com/anza-xyz/solana-sdk/blob/nonce-account%40v2.2.1/nonce-account/src/lib.rs#L49-L53 */
60 0 : #define FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_UNKNOWN (-1)
61 183 : #define FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_SYSTEM (0)
62 183 : #define FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_NONCE (1)
63 :
64 : /* SystemInstruction discriminants (wire-format u32 values). */
65 :
66 198 : #define FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT (0U)
67 0 : #define FD_SYSTEM_PROGRAM_INSTR_ASSIGN (1U)
68 57 : #define FD_SYSTEM_PROGRAM_INSTR_TRANSFER (2U)
69 0 : #define FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_WITH_SEED (3U)
70 18 : #define FD_SYSTEM_PROGRAM_INSTR_ADVANCE_NONCE_ACCOUNT (4U)
71 0 : #define FD_SYSTEM_PROGRAM_INSTR_WITHDRAW_NONCE_ACCOUNT (5U)
72 9 : #define FD_SYSTEM_PROGRAM_INSTR_INITIALIZE_NONCE_ACCOUNT (6U)
73 0 : #define FD_SYSTEM_PROGRAM_INSTR_AUTHORIZE_NONCE_ACCOUNT (7U)
74 156 : #define FD_SYSTEM_PROGRAM_INSTR_ALLOCATE (8U)
75 0 : #define FD_SYSTEM_PROGRAM_INSTR_ALLOCATE_WITH_SEED (9U)
76 0 : #define FD_SYSTEM_PROGRAM_INSTR_ASSIGN_WITH_SEED (10U)
77 0 : #define FD_SYSTEM_PROGRAM_INSTR_TRANSFER_WITH_SEED (11U)
78 0 : #define FD_SYSTEM_PROGRAM_INSTR_UPGRADE_NONCE_ACCOUNT (12U)
79 246 : #define FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_ALLOW_PREFUND (13U)
80 :
81 : /* Per-variant structs. */
82 :
83 : struct create_account {
84 : ulong lamports;
85 : ulong space;
86 : fd_pubkey_t owner;
87 : };
88 : typedef struct create_account create_account_t;
89 :
90 : struct create_account_with_seed {
91 : fd_pubkey_t base;
92 : uchar const * seed; /* points into caller-owned instr_data */
93 : ulong seed_len;
94 : ulong lamports;
95 : ulong space;
96 : fd_pubkey_t owner;
97 : };
98 : typedef struct create_account_with_seed create_account_with_seed_t;
99 :
100 : struct allocate_with_seed {
101 : fd_pubkey_t base;
102 : uchar const * seed;
103 : ulong seed_len;
104 : ulong space;
105 : fd_pubkey_t owner;
106 : };
107 : typedef struct allocate_with_seed allocate_with_seed_t;
108 :
109 : struct assign_with_seed {
110 : fd_pubkey_t base;
111 : uchar const * seed;
112 : ulong seed_len;
113 : fd_pubkey_t owner;
114 : };
115 : typedef struct assign_with_seed assign_with_seed_t;
116 :
117 : struct transfer_with_seed {
118 : ulong lamports;
119 : uchar const * from_seed;
120 : ulong from_seed_len;
121 : fd_pubkey_t from_owner;
122 : };
123 : typedef struct transfer_with_seed transfer_with_seed_t;
124 :
125 : /* Discriminated union. */
126 :
127 : union fd_system_program_instruction_inner {
128 : create_account_t create_account;
129 : fd_pubkey_t assign;
130 : ulong transfer;
131 : create_account_with_seed_t create_account_with_seed;
132 : ulong withdraw_nonce_account;
133 : fd_pubkey_t initialize_nonce_account;
134 : fd_pubkey_t authorize_nonce_account;
135 : ulong allocate;
136 : allocate_with_seed_t allocate_with_seed;
137 : assign_with_seed_t assign_with_seed;
138 : transfer_with_seed_t transfer_with_seed;
139 : create_account_t create_account_allow_prefund;
140 : };
141 : typedef union fd_system_program_instruction_inner fd_system_program_instruction_inner_t;
142 :
143 : struct fd_system_program_instruction {
144 : uint discriminant;
145 : fd_system_program_instruction_inner_t inner;
146 : };
147 : typedef struct fd_system_program_instruction fd_system_program_instruction_t;
148 :
149 : /* fd_system_program_instruction_decode reads a bincode-encoded
150 : SystemInstruction from [data, data+data_sz). Variable-length seed
151 : fields in the output point directly into `data`, so the caller must
152 : keep that buffer alive. Returns 0 on success, -1 on decode error. */
153 :
154 : static inline int
155 : fd_system_program_instruction_decode( fd_system_program_instruction_t * out,
156 : uchar const * data,
157 240 : ulong data_sz ) {
158 240 : uchar const * _payload = data;
159 240 : ulong const _payload_sz = data_sz;
160 240 : ulong _i = 0UL;
161 :
162 465 : # define CHECK( cond ) { if( FD_UNLIKELY( !(cond) ) ) { return -1; } }
163 465 : # define CHECK_LEFT( n ) CHECK( (n)<=(_payload_sz-_i) )
164 708 : # define INC( n ) (_i += (ulong)(n))
165 240 : # define CURSOR (_payload+_i)
166 :
167 240 : CHECK_LEFT( 4UL );
168 237 : uint disc = FD_LOAD( uint, CURSOR ); INC( 4UL );
169 237 : out->discriminant = disc;
170 :
171 237 : switch( disc ) {
172 :
173 15 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT:
174 123 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_ALLOW_PREFUND: {
175 123 : create_account_t * ca =
176 123 : (disc==FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT)
177 123 : ? &out->inner.create_account
178 123 : : &out->inner.create_account_allow_prefund;
179 123 : CHECK_LEFT( 48UL );
180 123 : ca->lamports = FD_LOAD( ulong, CURSOR ); INC( 8UL );
181 123 : ca->space = FD_LOAD( ulong, CURSOR ); INC( 8UL );
182 123 : fd_memcpy( ca->owner.key, CURSOR, 32UL ); INC( 32UL );
183 123 : return 0;
184 123 : }
185 :
186 0 : case FD_SYSTEM_PROGRAM_INSTR_ASSIGN: {
187 0 : CHECK_LEFT( 32UL );
188 0 : fd_memcpy( out->inner.assign.key, CURSOR, 32UL ); INC( 32UL );
189 0 : return 0;
190 0 : }
191 :
192 27 : case FD_SYSTEM_PROGRAM_INSTR_TRANSFER: {
193 27 : CHECK_LEFT( 8UL );
194 27 : out->inner.transfer = FD_LOAD( ulong, CURSOR ); INC( 8UL );
195 27 : return 0;
196 27 : }
197 :
198 0 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_WITH_SEED: {
199 0 : create_account_with_seed_t * cs = &out->inner.create_account_with_seed;
200 0 : CHECK_LEFT( 32UL ); fd_memcpy( cs->base.key, CURSOR, 32UL ); INC( 32UL );
201 0 : CHECK_LEFT( 8UL ); cs->seed_len = FD_LOAD( ulong, CURSOR ); INC( 8UL );
202 0 : CHECK_LEFT( cs->seed_len );
203 0 : if( cs->seed_len ) {
204 0 : CHECK( fd_utf8_verify( (char const *)CURSOR, cs->seed_len ) );
205 0 : }
206 0 : cs->seed = CURSOR; INC( cs->seed_len );
207 0 : CHECK_LEFT( 48UL );
208 0 : cs->lamports = FD_LOAD( ulong, CURSOR ); INC( 8UL );
209 0 : cs->space = FD_LOAD( ulong, CURSOR ); INC( 8UL );
210 0 : fd_memcpy( cs->owner.key, CURSOR, 32UL ); INC( 32UL );
211 0 : return 0;
212 0 : }
213 :
214 12 : case FD_SYSTEM_PROGRAM_INSTR_ADVANCE_NONCE_ACCOUNT: {
215 12 : return 0;
216 0 : }
217 :
218 0 : case FD_SYSTEM_PROGRAM_INSTR_WITHDRAW_NONCE_ACCOUNT: {
219 0 : CHECK_LEFT( 8UL );
220 0 : out->inner.withdraw_nonce_account = FD_LOAD( ulong, CURSOR ); INC( 8UL );
221 0 : return 0;
222 0 : }
223 :
224 6 : case FD_SYSTEM_PROGRAM_INSTR_INITIALIZE_NONCE_ACCOUNT: {
225 6 : CHECK_LEFT( 32UL );
226 6 : fd_memcpy( out->inner.initialize_nonce_account.key, CURSOR, 32UL ); INC( 32UL );
227 6 : return 0;
228 6 : }
229 :
230 0 : case FD_SYSTEM_PROGRAM_INSTR_AUTHORIZE_NONCE_ACCOUNT: {
231 0 : CHECK_LEFT( 32UL );
232 0 : fd_memcpy( out->inner.authorize_nonce_account.key, CURSOR, 32UL ); INC( 32UL );
233 0 : return 0;
234 0 : }
235 :
236 69 : case FD_SYSTEM_PROGRAM_INSTR_ALLOCATE: {
237 69 : CHECK_LEFT( 8UL );
238 69 : out->inner.allocate = FD_LOAD( ulong, CURSOR ); INC( 8UL );
239 69 : return 0;
240 69 : }
241 :
242 0 : case FD_SYSTEM_PROGRAM_INSTR_ALLOCATE_WITH_SEED: {
243 0 : allocate_with_seed_t * as_ = &out->inner.allocate_with_seed;
244 0 : CHECK_LEFT( 32UL ); fd_memcpy( as_->base.key, CURSOR, 32UL ); INC( 32UL );
245 0 : CHECK_LEFT( 8UL ); as_->seed_len = FD_LOAD( ulong, CURSOR ); INC( 8UL );
246 0 : CHECK_LEFT( as_->seed_len );
247 0 : if( as_->seed_len ) {
248 0 : CHECK( fd_utf8_verify( (char const *)CURSOR, as_->seed_len ) );
249 0 : }
250 0 : as_->seed = CURSOR; INC( as_->seed_len );
251 0 : CHECK_LEFT( 40UL );
252 0 : as_->space = FD_LOAD( ulong, CURSOR ); INC( 8UL );
253 0 : fd_memcpy( as_->owner.key, CURSOR, 32UL ); INC( 32UL );
254 0 : return 0;
255 0 : }
256 :
257 0 : case FD_SYSTEM_PROGRAM_INSTR_ASSIGN_WITH_SEED: {
258 0 : assign_with_seed_t * aw = &out->inner.assign_with_seed;
259 0 : CHECK_LEFT( 32UL ); fd_memcpy( aw->base.key, CURSOR, 32UL ); INC( 32UL );
260 0 : CHECK_LEFT( 8UL ); aw->seed_len = FD_LOAD( ulong, CURSOR ); INC( 8UL );
261 0 : CHECK_LEFT( aw->seed_len );
262 0 : if( aw->seed_len ) {
263 0 : CHECK( fd_utf8_verify( (char const *)CURSOR, aw->seed_len ) );
264 0 : }
265 0 : aw->seed = CURSOR; INC( aw->seed_len );
266 0 : CHECK_LEFT( 32UL );
267 0 : fd_memcpy( aw->owner.key, CURSOR, 32UL ); INC( 32UL );
268 0 : return 0;
269 0 : }
270 :
271 0 : case FD_SYSTEM_PROGRAM_INSTR_TRANSFER_WITH_SEED: {
272 0 : transfer_with_seed_t * tw = &out->inner.transfer_with_seed;
273 0 : CHECK_LEFT( 8UL ); tw->lamports = FD_LOAD( ulong, CURSOR ); INC( 8UL );
274 0 : CHECK_LEFT( 8UL ); tw->from_seed_len = FD_LOAD( ulong, CURSOR ); INC( 8UL );
275 0 : CHECK_LEFT( tw->from_seed_len );
276 0 : if( tw->from_seed_len ) {
277 0 : CHECK( fd_utf8_verify( (char const *)CURSOR, tw->from_seed_len ) );
278 0 : }
279 0 : tw->from_seed = CURSOR; INC( tw->from_seed_len );
280 0 : CHECK_LEFT( 32UL );
281 0 : fd_memcpy( tw->from_owner.key, CURSOR, 32UL ); INC( 32UL );
282 0 : return 0;
283 0 : }
284 :
285 0 : case FD_SYSTEM_PROGRAM_INSTR_UPGRADE_NONCE_ACCOUNT: {
286 0 : return 0;
287 0 : }
288 :
289 0 : default: return -1;
290 237 : }
291 :
292 237 : # undef CHECK
293 237 : # undef CHECK_LEFT
294 237 : # undef INC
295 237 : # undef CURSOR
296 237 : }
297 :
298 : /* fd_system_program_instruction_encode writes a bincode-encoded
299 : SystemInstruction into [buf, buf+bufsz). On success stores the
300 : number of bytes written to *out_sz and returns 0. Returns -1 on
301 : short buffer. */
302 :
303 : static inline int
304 : fd_system_program_instruction_encode( fd_system_program_instruction_t const * in,
305 : uchar * buf,
306 : ulong bufsz,
307 27 : ulong * out_sz ) {
308 27 : uchar * const _payload = buf;
309 27 : ulong const _payload_sz = bufsz;
310 27 : ulong _i = 0UL;
311 :
312 54 : # define CHECK_LEFT( n ) { if( FD_UNLIKELY( (n)>(_payload_sz-_i) ) ) { return -1; } }
313 84 : # define INC( n ) (_i += (ulong)(n))
314 27 : # define CURSOR (_payload+_i)
315 :
316 27 : CHECK_LEFT( 4UL ); FD_STORE( uint, CURSOR, in->discriminant ); INC( 4UL );
317 :
318 27 : switch( in->discriminant ) {
319 :
320 15 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT:
321 15 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_ALLOW_PREFUND: {
322 15 : create_account_t const * ca =
323 15 : (in->discriminant==FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT)
324 15 : ? &in->inner.create_account
325 15 : : &in->inner.create_account_allow_prefund;
326 15 : CHECK_LEFT( 48UL );
327 15 : FD_STORE( ulong, CURSOR, ca->lamports ); INC( 8UL );
328 15 : FD_STORE( ulong, CURSOR, ca->space ); INC( 8UL );
329 15 : fd_memcpy( CURSOR, ca->owner.key, 32UL ); INC( 32UL );
330 15 : break;
331 15 : }
332 :
333 0 : case FD_SYSTEM_PROGRAM_INSTR_ASSIGN: {
334 0 : CHECK_LEFT( 32UL );
335 0 : fd_memcpy( CURSOR, in->inner.assign.key, 32UL ); INC( 32UL );
336 0 : break;
337 0 : }
338 :
339 3 : case FD_SYSTEM_PROGRAM_INSTR_TRANSFER: {
340 3 : CHECK_LEFT( 8UL );
341 3 : FD_STORE( ulong, CURSOR, in->inner.transfer ); INC( 8UL );
342 3 : break;
343 3 : }
344 :
345 0 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_WITH_SEED: {
346 0 : create_account_with_seed_t const * cs = &in->inner.create_account_with_seed;
347 0 : CHECK_LEFT( 32UL ); fd_memcpy( CURSOR, cs->base.key, 32UL ); INC( 32UL );
348 0 : CHECK_LEFT( 8UL ); FD_STORE( ulong, CURSOR, cs->seed_len ); INC( 8UL );
349 0 : if( cs->seed_len ) {
350 0 : CHECK_LEFT( cs->seed_len ); fd_memcpy( CURSOR, cs->seed, cs->seed_len ); INC( cs->seed_len );
351 0 : }
352 0 : CHECK_LEFT( 48UL );
353 0 : FD_STORE( ulong, CURSOR, cs->lamports ); INC( 8UL );
354 0 : FD_STORE( ulong, CURSOR, cs->space ); INC( 8UL );
355 0 : fd_memcpy( CURSOR, cs->owner.key, 32UL ); INC( 32UL );
356 0 : break;
357 0 : }
358 :
359 0 : case FD_SYSTEM_PROGRAM_INSTR_ADVANCE_NONCE_ACCOUNT: break;
360 :
361 0 : case FD_SYSTEM_PROGRAM_INSTR_WITHDRAW_NONCE_ACCOUNT: {
362 0 : CHECK_LEFT( 8UL );
363 0 : FD_STORE( ulong, CURSOR, in->inner.withdraw_nonce_account ); INC( 8UL );
364 0 : break;
365 0 : }
366 :
367 0 : case FD_SYSTEM_PROGRAM_INSTR_INITIALIZE_NONCE_ACCOUNT: {
368 0 : CHECK_LEFT( 32UL );
369 0 : fd_memcpy( CURSOR, in->inner.initialize_nonce_account.key, 32UL ); INC( 32UL );
370 0 : break;
371 0 : }
372 :
373 0 : case FD_SYSTEM_PROGRAM_INSTR_AUTHORIZE_NONCE_ACCOUNT: {
374 0 : CHECK_LEFT( 32UL );
375 0 : fd_memcpy( CURSOR, in->inner.authorize_nonce_account.key, 32UL ); INC( 32UL );
376 0 : break;
377 0 : }
378 :
379 9 : case FD_SYSTEM_PROGRAM_INSTR_ALLOCATE: {
380 9 : CHECK_LEFT( 8UL );
381 9 : FD_STORE( ulong, CURSOR, in->inner.allocate ); INC( 8UL );
382 9 : break;
383 9 : }
384 :
385 0 : case FD_SYSTEM_PROGRAM_INSTR_ALLOCATE_WITH_SEED: {
386 0 : allocate_with_seed_t const * as_ = &in->inner.allocate_with_seed;
387 0 : CHECK_LEFT( 32UL ); fd_memcpy( CURSOR, as_->base.key, 32UL ); INC( 32UL );
388 0 : CHECK_LEFT( 8UL ); FD_STORE( ulong, CURSOR, as_->seed_len ); INC( 8UL );
389 0 : if( as_->seed_len ) {
390 0 : CHECK_LEFT( as_->seed_len ); fd_memcpy( CURSOR, as_->seed, as_->seed_len ); INC( as_->seed_len );
391 0 : }
392 0 : CHECK_LEFT( 40UL );
393 0 : FD_STORE( ulong, CURSOR, as_->space ); INC( 8UL );
394 0 : fd_memcpy( CURSOR, as_->owner.key, 32UL ); INC( 32UL );
395 0 : break;
396 0 : }
397 :
398 0 : case FD_SYSTEM_PROGRAM_INSTR_ASSIGN_WITH_SEED: {
399 0 : assign_with_seed_t const * aw = &in->inner.assign_with_seed;
400 0 : CHECK_LEFT( 32UL ); fd_memcpy( CURSOR, aw->base.key, 32UL ); INC( 32UL );
401 0 : CHECK_LEFT( 8UL ); FD_STORE( ulong, CURSOR, aw->seed_len ); INC( 8UL );
402 0 : if( aw->seed_len ) {
403 0 : CHECK_LEFT( aw->seed_len ); fd_memcpy( CURSOR, aw->seed, aw->seed_len ); INC( aw->seed_len );
404 0 : }
405 0 : CHECK_LEFT( 32UL );
406 0 : fd_memcpy( CURSOR, aw->owner.key, 32UL ); INC( 32UL );
407 0 : break;
408 0 : }
409 :
410 0 : case FD_SYSTEM_PROGRAM_INSTR_TRANSFER_WITH_SEED: {
411 0 : transfer_with_seed_t const * tw = &in->inner.transfer_with_seed;
412 0 : CHECK_LEFT( 8UL ); FD_STORE( ulong, CURSOR, tw->lamports ); INC( 8UL );
413 0 : CHECK_LEFT( 8UL ); FD_STORE( ulong, CURSOR, tw->from_seed_len ); INC( 8UL );
414 0 : if( tw->from_seed_len ) {
415 0 : CHECK_LEFT( tw->from_seed_len ); fd_memcpy( CURSOR, tw->from_seed, tw->from_seed_len ); INC( tw->from_seed_len );
416 0 : }
417 0 : CHECK_LEFT( 32UL );
418 0 : fd_memcpy( CURSOR, tw->from_owner.key, 32UL ); INC( 32UL );
419 0 : break;
420 0 : }
421 :
422 0 : case FD_SYSTEM_PROGRAM_INSTR_UPGRADE_NONCE_ACCOUNT: break;
423 :
424 0 : default: return -1;
425 27 : }
426 :
427 27 : *out_sz = _i;
428 :
429 27 : # undef CHECK_LEFT
430 27 : # undef INC
431 27 : # undef CURSOR
432 :
433 27 : return 0;
434 27 : }
435 :
436 : /* fd_nonce_state_versions_t is the in-memory representation of a decoded
437 : nonce account state. Mirrors Agave's `Versions(Box<State>)` wrapper
438 : flattened into a single struct with an explicit `version`/`kind`
439 : discriminant pair. When `kind == FD_NONCE_STATE_UNINITIALIZED` the
440 : `authority`, `durable_nonce`, and `lamports_per_signature` fields are
441 : ignored on both encode and decode. */
442 :
443 : struct fd_nonce_state_versions {
444 : uint version;
445 : uint kind;
446 : fd_pubkey_t authority;
447 : fd_hash_t durable_nonce;
448 : ulong lamports_per_signature;
449 : };
450 : typedef struct fd_nonce_state_versions fd_nonce_state_versions_t;
451 :
452 : /* fd_nonce_state_versions_size returns the bincode-exact wire size of the
453 : given nonce state. Matches Agave's `bincode::serialized_size` for
454 : `nonce::versions::Versions`. */
455 :
456 : static inline ulong
457 21 : fd_nonce_state_versions_size( fd_nonce_state_versions_t const * in ) {
458 21 : return ( in->kind==FD_NONCE_STATE_INITIALIZED )
459 21 : ? FD_NONCE_STATE_INITIALIZED_SZ
460 21 : : FD_NONCE_STATE_UNINITIALIZED_SZ;
461 21 : }
462 :
463 : /* fd_nonce_state_versions_decode reads a bincode-encoded
464 : `nonce::versions::Versions` from [data, data+data_sz). Trailing bytes
465 : beyond the parsed region are accepted (matches Agave's top-level
466 : `bincode::deserialize` which uses `allow_trailing_bytes()`). Returns 0
467 : on success, -1 on any decode failure (short buffer, unknown
468 : discriminant). Callers should map -1 to
469 : `FD_EXECUTOR_INSTR_ERR_INVALID_ACC_DATA`. */
470 :
471 : static inline int
472 : fd_nonce_state_versions_decode( fd_nonce_state_versions_t * out,
473 : uchar const * data,
474 21 : ulong data_sz ) {
475 21 : uchar const * _payload = data;
476 21 : ulong const _payload_sz = data_sz;
477 21 : ulong _i = 0UL;
478 :
479 102 : # define CHECK( cond ) { if( FD_UNLIKELY( !(cond) ) ) { return -1; } }
480 60 : # define CHECK_LEFT( n ) CHECK( (n)<=(_payload_sz-_i) )
481 96 : # define INC( n ) (_i += (ulong)(n))
482 36 : # define CURSOR (_payload+_i)
483 :
484 21 : CHECK_LEFT( 4UL ); uint version = FD_LOAD( uint, CURSOR ); INC( 4UL );
485 21 : CHECK( version<=FD_NONCE_VERSION_CURRENT );
486 21 : out->version = version;
487 :
488 21 : CHECK_LEFT( 4UL ); uint kind = FD_LOAD( uint, CURSOR ); INC( 4UL );
489 21 : CHECK( kind<=FD_NONCE_STATE_INITIALIZED );
490 21 : out->kind = kind;
491 :
492 21 : if( kind==FD_NONCE_STATE_INITIALIZED ) {
493 18 : CHECK_LEFT( 72UL );
494 18 : fd_memcpy( out->authority.key, CURSOR, 32UL ); INC( 32UL );
495 18 : fd_memcpy( out->durable_nonce.hash, CURSOR, 32UL ); INC( 32UL );
496 18 : out->lamports_per_signature = FD_LOAD( ulong, CURSOR ); INC( 8UL );
497 18 : }
498 :
499 21 : # undef CHECK
500 21 : # undef CHECK_LEFT
501 21 : # undef INC
502 21 : # undef CURSOR
503 :
504 21 : return 0;
505 21 : }
506 :
507 : /* fd_nonce_state_versions_encode writes a bincode-encoded
508 : `nonce::versions::Versions` into [buf, buf+bufsz). On success stores
509 : the number of bytes written to *out_sz and returns 0. Returns -1 on
510 : short buffer or invalid discriminant. */
511 :
512 : static inline int
513 : fd_nonce_state_versions_encode( fd_nonce_state_versions_t const * in,
514 : uchar * buf,
515 : ulong bufsz,
516 30 : ulong * out_sz ) {
517 30 : uchar * const _payload = buf;
518 30 : ulong const _payload_sz = bufsz;
519 30 : ulong _i = 0UL;
520 :
521 147 : # define CHECK( cond ) { if( FD_UNLIKELY( !(cond) ) ) { return -1; } }
522 87 : # define CHECK_LEFT( n ) CHECK( (n)<=(_payload_sz-_i) )
523 141 : # define INC( n ) (_i += (ulong)(n))
524 54 : # define CURSOR (_payload+_i)
525 :
526 30 : CHECK( in->version<=FD_NONCE_VERSION_CURRENT );
527 30 : CHECK( in->kind <=FD_NONCE_STATE_INITIALIZED );
528 :
529 30 : CHECK_LEFT( 4UL ); FD_STORE( uint, CURSOR, in->version ); INC( 4UL );
530 30 : CHECK_LEFT( 4UL ); FD_STORE( uint, CURSOR, in->kind ); INC( 4UL );
531 :
532 30 : if( in->kind==FD_NONCE_STATE_INITIALIZED ) {
533 27 : CHECK_LEFT( 72UL );
534 27 : fd_memcpy( CURSOR, in->authority.key, 32UL ); INC( 32UL );
535 27 : fd_memcpy( CURSOR, in->durable_nonce.hash, 32UL ); INC( 32UL );
536 27 : FD_STORE( ulong, CURSOR, in->lamports_per_signature ); INC( 8UL );
537 27 : }
538 :
539 30 : *out_sz = _i;
540 :
541 30 : # undef CHECK
542 30 : # undef CHECK_LEFT
543 30 : # undef INC
544 30 : # undef CURSOR
545 :
546 30 : return 0;
547 30 : }
548 :
549 : FD_PROTOTYPES_BEGIN
550 :
551 : /* fd_system_program_execute is the entrypoint for the system program */
552 :
553 : int fd_system_program_execute( fd_exec_instr_ctx_t * ctx );
554 :
555 : /* System program instruction handlers */
556 :
557 : int fd_system_program_exec_create_account ( fd_exec_instr_ctx_t * ctx, create_account_t const * data );
558 : int fd_system_program_exec_assign ( fd_exec_instr_ctx_t * ctx, fd_pubkey_t const * owner );
559 : int fd_system_program_exec_transfer ( fd_exec_instr_ctx_t * ctx, ulong lamports );
560 : int fd_system_program_exec_create_account_with_seed ( fd_exec_instr_ctx_t * ctx, create_account_with_seed_t const * data );
561 : int fd_system_program_exec_advance_nonce_account ( fd_exec_instr_ctx_t * ctx );
562 : int fd_system_program_exec_withdraw_nonce_account ( fd_exec_instr_ctx_t * ctx, ulong lamports );
563 : int fd_system_program_exec_initialize_nonce_account ( fd_exec_instr_ctx_t * ctx, fd_pubkey_t const * pubkey );
564 : int fd_system_program_exec_authorize_nonce_account ( fd_exec_instr_ctx_t * ctx, fd_pubkey_t const * pubkey );
565 : int fd_system_program_exec_allocate ( fd_exec_instr_ctx_t * ctx, ulong space );
566 : int fd_system_program_exec_allocate_with_seed ( fd_exec_instr_ctx_t * ctx, allocate_with_seed_t const * data );
567 : int fd_system_program_exec_assign_with_seed ( fd_exec_instr_ctx_t * ctx, assign_with_seed_t const * data );
568 : int fd_system_program_exec_transfer_with_seed ( fd_exec_instr_ctx_t * ctx, transfer_with_seed_t const * data );
569 : int fd_system_program_exec_upgrade_nonce_account ( fd_exec_instr_ctx_t * ctx );
570 : int fd_system_program_exec_create_account_allow_prefund( fd_exec_instr_ctx_t * ctx, create_account_t const * data );
571 :
572 : /* User APIs */
573 :
574 : /* fd_check_transaction_age returns 0 if the transactions age is valid,
575 : returns non-zero otherwise. This is determined by the age of the
576 : blockhash provided in the transaction message or by the validity of
577 : the nonce provided in the transaction. */
578 :
579 : int
580 : fd_check_transaction_age( fd_bank_t * bank,
581 : fd_txn_in_t const * txn_in,
582 : fd_txn_out_t * txn_out );
583 :
584 : /* `fd_get_system_account_kind()` determines whether an account is
585 : a normal system program account or a nonce account. Returns:
586 : - FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_SYSTEM if the account is a
587 : normal system program account
588 : - FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_NONCE if the account is a
589 : nonce account
590 : - FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_UNKNOWN otherwise
591 : https://github.com/anza-xyz/solana-sdk/blob/nonce-account%40v2.2.1/nonce-account/src/lib.rs#L55-L71 */
592 :
593 : int
594 : fd_get_system_account_kind( fd_account_meta_t const * meta );
595 :
596 : FD_PROTOTYPES_END
597 :
598 : #endif /* HEADER_fd_src_flamenco_runtime_program_fd_system_program_h */
|