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