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 12 : #define FD_SYSTEM_PROGRAM_ERR_ACCT_ALREADY_IN_USE (0) /* SystemError::AccountAlreadyInUse */
22 3 : #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 3 : #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 0 : #define FD_SYSTEM_PROGRAM_ERR_ADDR_WITH_SEED_MISMATCH (5) /* SystemError::AddressWithSeedMismatch */
27 0 : #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 36 : #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 48 : #define FD_NONCE_VERSION_CURRENT (1U)
54 :
55 9 : #define FD_NONCE_STATE_UNINITIALIZED (0U)
56 153 : #define FD_NONCE_STATE_INITIALIZED (1U)
57 :
58 0 : #define FD_NONCE_STATE_UNINITIALIZED_SZ (8UL)
59 24 : #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 0 : #define FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_UNKNOWN (-1)
63 396 : #define FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_SYSTEM (0)
64 3 : #define FD_SYSTEM_PROGRAM_NONCE_ACCOUNT_KIND_NONCE (1)
65 :
66 : /* SystemInstruction discriminants (wire-format u32 values). */
67 :
68 234 : #define FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT (0U)
69 0 : #define FD_SYSTEM_PROGRAM_INSTR_ASSIGN (1U)
70 66 : #define FD_SYSTEM_PROGRAM_INSTR_TRANSFER (2U)
71 0 : #define FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_WITH_SEED (3U)
72 27 : #define FD_SYSTEM_PROGRAM_INSTR_ADVANCE_NONCE_ACCOUNT (4U)
73 0 : #define FD_SYSTEM_PROGRAM_INSTR_WITHDRAW_NONCE_ACCOUNT (5U)
74 9 : #define FD_SYSTEM_PROGRAM_INSTR_INITIALIZE_NONCE_ACCOUNT (6U)
75 0 : #define FD_SYSTEM_PROGRAM_INSTR_AUTHORIZE_NONCE_ACCOUNT (7U)
76 156 : #define FD_SYSTEM_PROGRAM_INSTR_ALLOCATE (8U)
77 0 : #define FD_SYSTEM_PROGRAM_INSTR_ALLOCATE_WITH_SEED (9U)
78 0 : #define FD_SYSTEM_PROGRAM_INSTR_ASSIGN_WITH_SEED (10U)
79 0 : #define FD_SYSTEM_PROGRAM_INSTR_TRANSFER_WITH_SEED (11U)
80 0 : #define FD_SYSTEM_PROGRAM_INSTR_UPGRADE_NONCE_ACCOUNT (12U)
81 258 : #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 264 : ulong data_sz ) {
163 264 : 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 264 : ulong const _payload_sz = fd_ulong_min( data_sz, FD_TXN_MTU_V0 );
167 264 : ulong _i = 0UL;
168 :
169 504 : # define CHECK( cond ) { if( FD_UNLIKELY( !(cond) ) ) { return -1; } }
170 504 : # define CHECK_LEFT( n ) CHECK( (n)<=(_payload_sz-_i) )
171 759 : # define INC( n ) (_i += (ulong)(n))
172 264 : # define CURSOR (_payload+_i)
173 :
174 264 : CHECK_LEFT( 4UL );
175 261 : uint disc = FD_LOAD( uint, CURSOR ); INC( 4UL );
176 261 : out->discriminant = disc;
177 :
178 261 : switch( disc ) {
179 :
180 21 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT:
181 129 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_ALLOW_PREFUND: {
182 129 : create_account_t * ca =
183 129 : (disc==FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT)
184 129 : ? &out->inner.create_account
185 129 : : &out->inner.create_account_allow_prefund;
186 129 : CHECK_LEFT( 48UL );
187 129 : ca->lamports = FD_LOAD( ulong, CURSOR ); INC( 8UL );
188 129 : ca->space = FD_LOAD( ulong, CURSOR ); INC( 8UL );
189 129 : fd_memcpy( ca->owner.key, CURSOR, 32UL ); INC( 32UL );
190 129 : return 0;
191 129 : }
192 :
193 0 : case FD_SYSTEM_PROGRAM_INSTR_ASSIGN: {
194 0 : CHECK_LEFT( 32UL );
195 0 : fd_memcpy( out->inner.assign.key, CURSOR, 32UL ); INC( 32UL );
196 0 : return 0;
197 0 : }
198 :
199 36 : case FD_SYSTEM_PROGRAM_INSTR_TRANSFER: {
200 36 : CHECK_LEFT( 8UL );
201 36 : out->inner.transfer = FD_LOAD( ulong, CURSOR ); INC( 8UL );
202 36 : return 0;
203 36 : }
204 :
205 0 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_WITH_SEED: {
206 0 : create_account_with_seed_t * cs = &out->inner.create_account_with_seed;
207 0 : CHECK_LEFT( 32UL ); fd_memcpy( cs->base.key, CURSOR, 32UL ); INC( 32UL );
208 0 : CHECK_LEFT( 8UL ); cs->seed_len = FD_LOAD( ulong, CURSOR ); INC( 8UL );
209 0 : CHECK_LEFT( cs->seed_len );
210 0 : if( cs->seed_len ) {
211 0 : CHECK( fd_utf8_verify( (char const *)CURSOR, cs->seed_len ) );
212 0 : }
213 0 : cs->seed = CURSOR; INC( cs->seed_len );
214 0 : CHECK_LEFT( 48UL );
215 0 : cs->lamports = FD_LOAD( ulong, CURSOR ); INC( 8UL );
216 0 : cs->space = FD_LOAD( ulong, CURSOR ); INC( 8UL );
217 0 : fd_memcpy( cs->owner.key, CURSOR, 32UL ); INC( 32UL );
218 0 : return 0;
219 0 : }
220 :
221 21 : case FD_SYSTEM_PROGRAM_INSTR_ADVANCE_NONCE_ACCOUNT: {
222 21 : return 0;
223 0 : }
224 :
225 0 : case FD_SYSTEM_PROGRAM_INSTR_WITHDRAW_NONCE_ACCOUNT: {
226 0 : CHECK_LEFT( 8UL );
227 0 : out->inner.withdraw_nonce_account = FD_LOAD( ulong, CURSOR ); INC( 8UL );
228 0 : return 0;
229 0 : }
230 :
231 6 : case FD_SYSTEM_PROGRAM_INSTR_INITIALIZE_NONCE_ACCOUNT: {
232 6 : CHECK_LEFT( 32UL );
233 6 : fd_memcpy( out->inner.initialize_nonce_account.key, CURSOR, 32UL ); INC( 32UL );
234 6 : return 0;
235 6 : }
236 :
237 0 : case FD_SYSTEM_PROGRAM_INSTR_AUTHORIZE_NONCE_ACCOUNT: {
238 0 : CHECK_LEFT( 32UL );
239 0 : fd_memcpy( out->inner.authorize_nonce_account.key, CURSOR, 32UL ); INC( 32UL );
240 0 : return 0;
241 0 : }
242 :
243 69 : case FD_SYSTEM_PROGRAM_INSTR_ALLOCATE: {
244 69 : CHECK_LEFT( 8UL );
245 69 : out->inner.allocate = FD_LOAD( ulong, CURSOR ); INC( 8UL );
246 69 : return 0;
247 69 : }
248 :
249 0 : case FD_SYSTEM_PROGRAM_INSTR_ALLOCATE_WITH_SEED: {
250 0 : allocate_with_seed_t * as_ = &out->inner.allocate_with_seed;
251 0 : CHECK_LEFT( 32UL ); fd_memcpy( as_->base.key, CURSOR, 32UL ); INC( 32UL );
252 0 : CHECK_LEFT( 8UL ); as_->seed_len = FD_LOAD( ulong, CURSOR ); INC( 8UL );
253 0 : CHECK_LEFT( as_->seed_len );
254 0 : if( as_->seed_len ) {
255 0 : CHECK( fd_utf8_verify( (char const *)CURSOR, as_->seed_len ) );
256 0 : }
257 0 : as_->seed = CURSOR; INC( as_->seed_len );
258 0 : CHECK_LEFT( 40UL );
259 0 : as_->space = FD_LOAD( ulong, CURSOR ); INC( 8UL );
260 0 : fd_memcpy( as_->owner.key, CURSOR, 32UL ); INC( 32UL );
261 0 : return 0;
262 0 : }
263 :
264 0 : case FD_SYSTEM_PROGRAM_INSTR_ASSIGN_WITH_SEED: {
265 0 : assign_with_seed_t * aw = &out->inner.assign_with_seed;
266 0 : CHECK_LEFT( 32UL ); fd_memcpy( aw->base.key, CURSOR, 32UL ); INC( 32UL );
267 0 : CHECK_LEFT( 8UL ); aw->seed_len = FD_LOAD( ulong, CURSOR ); INC( 8UL );
268 0 : CHECK_LEFT( aw->seed_len );
269 0 : if( aw->seed_len ) {
270 0 : CHECK( fd_utf8_verify( (char const *)CURSOR, aw->seed_len ) );
271 0 : }
272 0 : aw->seed = CURSOR; INC( aw->seed_len );
273 0 : CHECK_LEFT( 32UL );
274 0 : fd_memcpy( aw->owner.key, CURSOR, 32UL ); INC( 32UL );
275 0 : return 0;
276 0 : }
277 :
278 0 : case FD_SYSTEM_PROGRAM_INSTR_TRANSFER_WITH_SEED: {
279 0 : transfer_with_seed_t * tw = &out->inner.transfer_with_seed;
280 0 : CHECK_LEFT( 8UL ); tw->lamports = FD_LOAD( ulong, CURSOR ); INC( 8UL );
281 0 : CHECK_LEFT( 8UL ); tw->from_seed_len = FD_LOAD( ulong, CURSOR ); INC( 8UL );
282 0 : CHECK_LEFT( tw->from_seed_len );
283 0 : if( tw->from_seed_len ) {
284 0 : CHECK( fd_utf8_verify( (char const *)CURSOR, tw->from_seed_len ) );
285 0 : }
286 0 : tw->from_seed = CURSOR; INC( tw->from_seed_len );
287 0 : CHECK_LEFT( 32UL );
288 0 : fd_memcpy( tw->from_owner.key, CURSOR, 32UL ); INC( 32UL );
289 0 : return 0;
290 0 : }
291 :
292 0 : case FD_SYSTEM_PROGRAM_INSTR_UPGRADE_NONCE_ACCOUNT: {
293 0 : return 0;
294 0 : }
295 :
296 0 : default: return -1;
297 261 : }
298 :
299 261 : # undef CHECK
300 261 : # undef CHECK_LEFT
301 261 : # undef INC
302 261 : # undef CURSOR
303 261 : }
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 36 : ulong * out_sz ) {
315 36 : uchar * const _payload = buf;
316 36 : ulong const _payload_sz = bufsz;
317 36 : ulong _i = 0UL;
318 :
319 72 : # define CHECK_LEFT( n ) { if( FD_UNLIKELY( (n)>(_payload_sz-_i) ) ) { return -1; } }
320 114 : # define INC( n ) (_i += (ulong)(n))
321 36 : # define CURSOR (_payload+_i)
322 :
323 36 : CHECK_LEFT( 4UL ); FD_STORE( uint, CURSOR, in->discriminant ); INC( 4UL );
324 :
325 36 : switch( in->discriminant ) {
326 :
327 21 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT:
328 21 : case FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT_ALLOW_PREFUND: {
329 21 : create_account_t const * ca =
330 21 : (in->discriminant==FD_SYSTEM_PROGRAM_INSTR_CREATE_ACCOUNT)
331 21 : ? &in->inner.create_account
332 21 : : &in->inner.create_account_allow_prefund;
333 21 : CHECK_LEFT( 48UL );
334 21 : FD_STORE( ulong, CURSOR, ca->lamports ); INC( 8UL );
335 21 : FD_STORE( ulong, CURSOR, ca->space ); INC( 8UL );
336 21 : fd_memcpy( CURSOR, ca->owner.key, 32UL ); INC( 32UL );
337 21 : break;
338 21 : }
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 6 : case FD_SYSTEM_PROGRAM_INSTR_TRANSFER: {
347 6 : CHECK_LEFT( 8UL );
348 6 : FD_STORE( ulong, CURSOR, in->inner.transfer ); INC( 8UL );
349 6 : break;
350 6 : }
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 9 : case FD_SYSTEM_PROGRAM_INSTR_ALLOCATE: {
387 9 : CHECK_LEFT( 8UL );
388 9 : FD_STORE( ulong, CURSOR, in->inner.allocate ); INC( 8UL );
389 9 : break;
390 9 : }
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 36 : }
433 :
434 36 : *out_sz = _i;
435 :
436 36 : # undef CHECK_LEFT
437 36 : # undef INC
438 36 : # undef CURSOR
439 :
440 36 : return 0;
441 36 : }
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 24 : fd_nonce_state_versions_size( fd_nonce_state_versions_t const * in ) {
465 24 : return ( in->kind==FD_NONCE_STATE_INITIALIZED )
466 24 : ? FD_NONCE_STATE_INITIALIZED_SZ
467 24 : : FD_NONCE_STATE_UNINITIALIZED_SZ;
468 24 : }
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 33 : ulong data_sz ) {
482 33 : uchar const * _payload = data;
483 33 : ulong const _payload_sz = data_sz;
484 33 : ulong _i = 0UL;
485 :
486 162 : # define CHECK( cond ) { if( FD_UNLIKELY( !(cond) ) ) { return -1; } }
487 96 : # define CHECK_LEFT( n ) CHECK( (n)<=(_payload_sz-_i) )
488 156 : # define INC( n ) (_i += (ulong)(n))
489 60 : # define CURSOR (_payload+_i)
490 :
491 33 : CHECK_LEFT( 4UL ); uint version = FD_LOAD( uint, CURSOR ); INC( 4UL );
492 33 : CHECK( version<=FD_NONCE_VERSION_CURRENT );
493 33 : out->version = version;
494 :
495 33 : CHECK_LEFT( 4UL ); uint kind = FD_LOAD( uint, CURSOR ); INC( 4UL );
496 33 : CHECK( kind<=FD_NONCE_STATE_INITIALIZED );
497 33 : out->kind = kind;
498 :
499 33 : if( kind==FD_NONCE_STATE_INITIALIZED ) {
500 30 : CHECK_LEFT( 72UL );
501 30 : fd_memcpy( out->authority.key, CURSOR, 32UL ); INC( 32UL );
502 30 : fd_memcpy( out->durable_nonce.hash, CURSOR, 32UL ); INC( 32UL );
503 30 : out->lamports_per_signature = FD_LOAD( ulong, CURSOR ); INC( 8UL );
504 30 : }
505 :
506 33 : # undef CHECK
507 33 : # undef CHECK_LEFT
508 33 : # undef INC
509 33 : # undef CURSOR
510 :
511 33 : return 0;
512 33 : }
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 48 : ulong * out_sz ) {
524 48 : uchar * const _payload = buf;
525 48 : ulong const _payload_sz = bufsz;
526 48 : ulong _i = 0UL;
527 :
528 237 : # define CHECK( cond ) { if( FD_UNLIKELY( !(cond) ) ) { return -1; } }
529 141 : # define CHECK_LEFT( n ) CHECK( (n)<=(_payload_sz-_i) )
530 231 : # define INC( n ) (_i += (ulong)(n))
531 90 : # define CURSOR (_payload+_i)
532 :
533 48 : CHECK( in->version<=FD_NONCE_VERSION_CURRENT );
534 48 : CHECK( in->kind <=FD_NONCE_STATE_INITIALIZED );
535 :
536 48 : CHECK_LEFT( 4UL ); FD_STORE( uint, CURSOR, in->version ); INC( 4UL );
537 48 : CHECK_LEFT( 4UL ); FD_STORE( uint, CURSOR, in->kind ); INC( 4UL );
538 :
539 48 : if( in->kind==FD_NONCE_STATE_INITIALIZED ) {
540 45 : CHECK_LEFT( 72UL );
541 45 : fd_memcpy( CURSOR, in->authority.key, 32UL ); INC( 32UL );
542 45 : fd_memcpy( CURSOR, in->durable_nonce.hash, 32UL ); INC( 32UL );
543 45 : FD_STORE( ulong, CURSOR, in->lamports_per_signature ); INC( 8UL );
544 45 : }
545 :
546 48 : *out_sz = _i;
547 :
548 48 : # undef CHECK
549 48 : # undef CHECK_LEFT
550 48 : # undef INC
551 48 : # undef CURSOR
552 :
553 48 : return 0;
554 48 : }
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 */
|