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