Line data Source code
1 : #ifndef HEADER_fd_src_ballet_txn_fd_txn_h
2 : #define HEADER_fd_src_ballet_txn_fd_txn_h
3 :
4 : /* The main structure this header defines is fd_txn_t, which represents a
5 : Solana transaction. A transaction, like a SQL database transaction, is the
6 : unit of execution atomicity in Solana, i.e. intermediate state is never
7 : visible to other transactions, and a failure at any point in the transaction
8 : causes the entire transaction to be rolled back (other than charging the
9 : transaction fee).
10 :
11 : A transaction primarily consists of a list of instructions to execute in
12 : sequence. The struct fd_txn_instr_t describes one instruction. An instruction
13 : specifies the invocation of a smart contract with some specified data and
14 : accounts. The name 'instruction' was a poor choice, (since on-chain code is
15 : composed of eBPF instructions and using the same word to refer to very
16 : different concepts is confusing) but it's too late to change. Thinking of a
17 : transaction-level instruction as a 'command' might be more useful.
18 :
19 : The other major component of a transaction is a list of account addresses.
20 : The address of any account that is referenced by any instruction in the
21 : transaction must appear in the list. The address of any signer (including
22 : the fee payer) must appear in the list. An account address is sometimes
23 : called a pubkey since it has the same format as one, though it is not always
24 : a public key strictly speaking (i.e. a corresponding private key may not
25 : exist). Each account address in the list has associated permissions flags:
26 : signer/not signer and writable/readonly. All 4 combinations are possible.
27 : These flags declare the transaction's intention in accessing the account,
28 : similar to the `mode` field of fopen( ). */
29 :
30 : #include "../fd_ballet_base.h"
31 :
32 : #include "../ed25519/fd_ed25519.h"
33 :
34 : /* FD_TXN_VLEGACY: the initial, pre-V0 transaction format. */
35 30956724 : #define FD_TXN_VLEGACY ((uchar)0xFF)
36 : /* FD_TXN_V0: The second transaction format. Includes a version number and
37 : potentially some address lookup tables */
38 572904 : #define FD_TXN_V0 ((uchar)0x00)
39 : /* FD_TXN_V1: The third transaction format ("Transaction V1").
40 : See https://github.com/solana-foundation/solana-improvement-documents/pull/385
41 :
42 : This format removes support for address lookup tables and is the
43 : only transaction format that supports an MTU greater than 1232
44 : bytes. */
45 94683 : #define FD_TXN_V1 ((uchar)0x01)
46 :
47 : /* FD_TXN_SIGNATURE_SZ: The size (in bytes) of an Ed25519 signature. */
48 61504597 : #define FD_TXN_SIGNATURE_SZ (64UL)
49 : /* FD_TXN_PUBKEY_SZ: The size (in bytes) of an Ed25519 public key. */
50 : #define FD_TXN_PUBKEY_SZ (32UL)
51 : /* FD_TXN_ACCT_ADDR_SZ: The size (in bytes) of a Solana account address.
52 : Account addresses are sometimes Ed25519 public keys, but they can also be
53 : the output of a SHA256 hash (program derived addresses and seeded accounts),
54 : or just hardcoded values (sysvars accounts). It's important that all types
55 : of account addresses have this same size. */
56 785334076 : #define FD_TXN_ACCT_ADDR_SZ (32UL)
57 : /* FD_TXN_BLOCKHASH_SZ: The size (in bytes) of a blockhash. A blockhash is a
58 : SHA256 hash, giving a size of 256 bits = 32 bytes. */
59 61345470 : #define FD_TXN_BLOCKHASH_SZ (32UL)
60 :
61 :
62 : /* FD_TXN_SIG_MAX: The (inclusive) maximum number of signatures a transaction
63 : can have.
64 :
65 : For V0 transactions, which have an MTU of 1232 B, the maximum signatures
66 : that can fit in the MTU is 12.
67 :
68 : For V1 transactions, the spec explicitly limits the maximum number of
69 : signatures to 12. */
70 6675 : #define FD_TXN_SIG_MAX (12UL)
71 :
72 : /* FD_TXN_ACCT_ADDR_MAX: The (inclusive) maximum number of account addresses
73 : that a transaction can have.
74 :
75 : The account lock limit currently limits this to 64. */
76 255525 : #define FD_TXN_ACCT_ADDR_MAX (64UL)
77 :
78 : /* FD_TXN_ADDR_TABLE_LOOKUP_MAX: The (inclusive) maximum number of address
79 : tables that this transaction references. The spec is pretty sloppy about
80 : the maximum number allowed. Since there's a maximum of 128 total accounts
81 : (including the fee payer) that the transaction can reference, if you have
82 : more than 127 table lookups, then you must have some from which you are not
83 : using any account. Realistically, the current MTU of 1232 B restricts this
84 : to 33. FIXME: We should petition to limit this to approx 8. */
85 25155 : #define FD_TXN_ADDR_TABLE_LOOKUP_MAX (127UL)
86 :
87 : /* FD_TXN_INSTR_MAX: The (inclusive) maximum number of instructions a transaction
88 : can have. */
89 6531 : #define FD_TXN_INSTR_MAX (64UL)
90 :
91 : /* FD_TXN_INSTR_ACCT_MAX: The (inclusive) maximum number of accounts a
92 : single instruction can reference.
93 : https://github.com/anza-xyz/agave/blob/v4.2.0-beta.1/transaction-context/src/lib.rs#L17 */
94 6 : #define FD_TXN_INSTR_ACCT_MAX (255UL)
95 :
96 :
97 : /* FD_TXN_MAX_SZ: The maximum amount of memory (in bytes) that a fd_txn can
98 : take up, including the instruction array and any address tables. The
99 : worst-case transaction is a V0 transaction with only two account
100 : addresses (a program and a fee payer), and tons of empty instructions (no
101 : accounts, no data) and as many address table lookups loading a single
102 : account as possible.
103 :
104 : Worst-case V0 transaction: only 2 accounts, 64 empty instructions
105 : (no accounts, no data), 24 address lookup table lookups loading
106 : a single account:
107 : sizeof(fd_txn_t) = 22
108 : 64 × sizeof(fd_txn_instr_t) = 640 +
109 : 24 × sizeof(fd_txn_acct_addr_lut_t) = 192 +
110 : = 854
111 :
112 : Worst-case V1 transaction: 64 empty instructions, no ALTs as
113 : ALTs are not allowed in V1 transactions:
114 : sizeof(fd_txn_t) = 22
115 : 64 × sizeof(fd_txn_instr_t) = 640 +
116 : = 662
117 :
118 : So the worst-case parsed transaction size is the V0 transaction
119 : case. */
120 21 : #define FD_TXN_MAX_SZ (854UL)
121 :
122 :
123 : /* FD_TXN_MTU: The maximum size (in bytes, inclusive) of a serialized
124 : transaction, across all transaction version formats. */
125 18774 : #define FD_TXN_MTU (4096UL)
126 :
127 : /* FD_TXN_MTU_V0: The maximum size (in bytes, inclusive) of a serialized
128 : legacy or V0 transaction.
129 :
130 : Any transaction that has an MTU larger than this is required to
131 : use the transaction V1 format. */
132 61340838 : #define FD_TXN_MTU_V0 (1232UL)
133 :
134 : /* FD_TXN_MIN_SERIALIZED_SZ: The minimum size (in bytes) of a serialized
135 : transaction, using fd_txn_parse() verification rules.
136 :
137 : Minimum legacy transaction size: 134
138 : Minimum v0 transaction size: 136
139 : Minimum v1 transaction size: 138 */
140 6639 : #define FD_TXN_MIN_SERIALIZED_SZ (134UL)
141 :
142 : /* BEGIN Agave limits */
143 :
144 : /* Maximum number of accounts that a transaction may lock. */
145 252597 : #define MAX_TX_ACCOUNT_LOCKS (64UL)
146 :
147 : /* In the FD runtime, we've sized things assuming up to
148 : MAX_TX_ACCOUNT_LOCKS accounts per transaction. We rely on the txn
149 : parser to enforce this limit, up till the point of
150 : validate_account_locks(). If the txn parser bumps the account limit,
151 : then we might overflow in the runtime. */
152 : FD_STATIC_ASSERT( MAX_TX_ACCOUNT_LOCKS==FD_TXN_ACCT_ADDR_MAX, num_accounts_per_txn );
153 :
154 : /* END Agave limits */
155 :
156 :
157 : /* A Solana transaction instruction, i.e. one command or step to execute in a
158 : transaction.
159 :
160 : An instruction tells the runtime to execute one on-chain program (smart
161 : contract) with some arguments (think argc, argv). The arguments come in the
162 : form of binary data and/or accounts, each of which is variable-sized and
163 : optional.
164 :
165 : Note that instructions specify accounts by giving an index into the
166 : transaction-level list of account addresses. This means there are
167 : essentially two layers of indirection: a 1 B index to a 32 B address which
168 : specifies an account. */
169 : struct fd_txn_instr {
170 : /* program_id: The on-chain program that this instruction invokes,
171 : represented as the index of the program's account address in the
172 : containing transaction's list of account addresses. */
173 : uchar program_id;
174 : uchar _padding_reserved_1; /* explicitly declare what the compiler would
175 : insert anyways */
176 :
177 : /* acct_cnt: The number of accounts this instruction references.
178 : N.B. It is possible to pass > 256 accounts to an instruction, but not more
179 : than 256 unique accounts. */
180 : ushort acct_cnt;
181 :
182 : /* data_sz: The size (in bytes) of the data passed to this instruction. The
183 : data itself is included in the transaction, so is limited to the overall
184 : transaction size. */
185 : ushort data_sz;
186 :
187 : /* acct_off: The offset (relative to the start of the transaction) in bytes
188 : where the account address index array starts. This array has size
189 : acct_cnt.
190 :
191 : Specifically, if uchar const * payload is a pointer to the first byte of
192 : the transaction data in the packet, then the array (payload+acct_off)[i]
193 : for i in [0, acct_cnt) gives all of the accounts passed to this
194 : instruction. As with the program_id, these accounts are represented as
195 : indices into the transaction's list of account addresses.
196 : */
197 : ushort acct_off;
198 :
199 : /* data_off: The offset (relative to the start of the transaction) in bytes
200 : where the instruction data array starts. This array has size data_sz.
201 :
202 : Specifically, if uchar const * payload is a pointer to the first byte of
203 : the transaction data in the packet, then the array (payload+data_off)[i]
204 : for i in [0, data_sz) gives the binary data passed to this instruction. */
205 : ushort data_off;
206 : };
207 :
208 : typedef struct fd_txn_instr fd_txn_instr_t;
209 :
210 :
211 : /* fd_txn_t: A Solana transaction. As explained above, a transaction is mostly
212 : a list of instructions, but there are a few other major components:
213 : - a list of account addresses,
214 : - the hash of a recent block (used as a nonce and TTL), and
215 : - potentially (if it's a V2 transaction) some address lookup tables. */
216 : struct fd_txn {
217 : /* transaction_version: The version number of this transaction. Currently
218 : must be one of { FD_TXN_VLEGACY, FD_TXN_V0, FD_TXN_V1 }. */
219 : uchar transaction_version;
220 :
221 : /* signature_cnt: The number of signatures in this transaction. signature_cnt
222 : in [1, FD_TXN_SIG_MAX]. */
223 : uchar signature_cnt;
224 :
225 : /* signature_off: The offset (relative to the start of the transaction) in
226 : bytes where the signatures start.
227 :
228 : Specifically, if uchar const * payload is a pointer to the first byte of
229 : the transaction data in the packet, then signature i starts at
230 : (payload+signature_off)[ FD_TXN_SIGNATURE_SZ*i ] for i in
231 : [0, signature_cnt). */
232 :
233 : ushort signature_off;
234 :
235 : /* message_off: The offset (relative to the start of the transaction)
236 : in bytes where the 'message' starts.
237 :
238 : The message is the part of the transaction covered by the
239 : signatures.
240 :
241 : For legacy/V0 transactions, the signatures are at the front of the
242 : packet and the message is at the end, so the message spans from
243 : message_off to the end of the packet.
244 :
245 : For V1 transactions, the message is at the front of the packet and
246 : the signatures are at the end, so the message spans from
247 : message_off to signature_off.
248 :
249 : Use fd_txn_msg_sz( txn, payload_sz ) to determine the length of
250 : the message. */
251 : ushort message_off;
252 :
253 : /* readonly_signed_cnt: Of the signature_cnt signatures, readonly_signed_cnt
254 : of them are read only. Since there must be a fee payer,
255 : readonly_signed_cnt in [0, signature_cnt) */
256 : uchar readonly_signed_cnt;
257 :
258 : /* readonly_unsigned_cnt: Of the account addresses that don't have an
259 : accompanying signature, readonly_unsigned_cnt of them are read only.
260 : readonly_unsigned_cnt in [0, acct_addr_cnt-signature_cnt]. Excludes any
261 : accounts from address table lookups. */
262 : uchar readonly_unsigned_cnt;
263 :
264 : /* acct_addr_cnt: The number of account addresses in this transaction.
265 : acct_addr_cnt in [1, FD_TXN_ACCT_ADDR_MAX]. Excludes any accounts from
266 : address table lookups. */
267 : ushort acct_addr_cnt;
268 :
269 : /* acct_addr_off: The offset (relative to the start of the transaction) in
270 : bytes where the account addresses start.
271 :
272 : Specifically, if uchar const * payload is a pointer to the first byte of
273 : the transaction data in the packet, then the array
274 : (payload+acct_addr_off)[ FD_TXN_ACCT_ADDR_SZ*i ] for i in [0, account_cnt)
275 : gives all of the account addresses in this transaction. Since
276 : (payload+acct_addr_off) points inside the packet, it should be treated as
277 : pointing to unaligned data.
278 :
279 : The order of these addresses is important, because it determines the
280 : "permission flags" for the account in this transaction.
281 : Accounts ordered:
282 : Index Range | Signer? | Writeable?
283 : ---------------------------------------------------------------------------------|--------------|-------------
284 : [0, signature_cnt - readonly_signed_cnt) | signer | writable
285 : [signature_cnt - readonly_signed_cnt, signature_cnt) | signer | readonly
286 : [signature_cnt, acct_addr_cnt - readonly_unsigned_cnt) | not signer | writable
287 : [acct_addr_cnt - readonly_unsigned_cnt, acct_addr_cnt) | not signer | readonly
288 : */
289 : ushort acct_addr_off;
290 :
291 : /* recent_blockhash_off: The offset (relative to the start of the
292 : transaction) in bytes where the recent blockhash starts.
293 :
294 : Specifically, if uchar const * payload is a pointer to the first byte of
295 : the transaction data in the packet, then (payload+recent_blockhash_off) is
296 : a pointer to the blockhash. Since the resulting pointer points inside the
297 : packet, it should be treated as pointing to unaligned data. In practice,
298 : recent_blockhash_off is 5 or 6 (mod 32). */
299 : ushort recent_blockhash_off;
300 :
301 : /* addr_table_lookup_cnt: The number of address lookup tables this
302 : transaction contains. Must be 0 if transaction_version==FD_TXN_VLEGACY.
303 : addr_table_lookup_cnt in [0, FD_TXN_ADDR_TABLE_LOOKUP_MAX]. */
304 : uchar addr_table_lookup_cnt;
305 :
306 : /* addr_table_adtl_writable_cnt: The total number of writable account
307 : addresses across all of the address table lookups.
308 : addr_table_adtl_writable_cnt in [0, addr_table_adtl_cnt]. */
309 : uchar addr_table_adtl_writable_cnt;
310 :
311 : /* addr_table_adtl_cnt: The total number of account addresses summed across
312 : all the address lookup tables. addr_table_adtl_cnt in
313 : [0, FD_TXN_ACCT_ADDR_MAX - acct_addr_cnt]. Since acct_addr_cnt > 0,
314 : addr_table_adtl_cnt < 64. */
315 : uchar addr_table_adtl_cnt;
316 :
317 : uchar _padding_reserved_1; /* explicit padding the compiler would have
318 : inserted anyways */
319 :
320 : /* v1_txn_config_values_off: The offset relative to the start of the
321 : transaction of the config values region. The config values region
322 : contains the fields which the V1 config mask indicates are
323 : present, packed together. Fields which are not present are not
324 : included, not set to zero.
325 :
326 : Legacy/V0 transactions have no config mask, so this field is 0
327 : for legacy/v0 transactions. */
328 : ushort v1_txn_config_values_off;
329 :
330 : /* From the address table lookups, we can add the following to the above table
331 : Index Range | Signer? | Writeable?
332 : -----------------------------------------------------------------------------------------------|--------------|-------------
333 : ...
334 : [acct_addr_cnt, acct_addr_cnt + addr_table_adtl_writable_cnt) | not signer | writable
335 : [acct_addr_cnt + addr_table_adtl_writable_cnt, acct_addr_cnt + addr_table_adtl_cnt) | not signer | readonly
336 : */
337 :
338 : /* instr_cnt: The number of instructions in this transaction.
339 : instr_cnt in [0, FD_TXN_INSTR_MAX]. */
340 : ushort instr_cnt;
341 :
342 : /* instr: The array of instructions in this transaction. It's a "flexible array
343 : member" since C does not allow the pretty typical 0-len array at the end
344 : of the struct trick.
345 : Indexed [0, instr_cnt). */
346 : fd_txn_instr_t instr[ ];
347 :
348 : /* Logically, there's another field here:
349 : address_tables: The address tables this transaction imports and which
350 : accounts from them are selected for inclusion in this transaction's
351 : overall list of accounts. Indexed [0, addr_table_lookup_cnt).
352 : fd_txn_acct_addr_lut_t address_tables[ ];
353 : To access it, call fd_txn_get_address_tables( ). */
354 :
355 : };
356 :
357 : typedef struct fd_txn fd_txn_t;
358 :
359 : /* fd_txn_acct_addr_lut: An on-chain address lookup table. Solana added this to
360 : the Transaction v2 spec in order to allow a transaction to reference more
361 : accounts. This struct specifies which account addresses from an on-chain
362 : list should be selected to include in the list of account addresses
363 : available to instructions in this transaction */
364 : struct fd_txn_acct_addr_lut {
365 : /* addr_off: The offset (relative to the start of the transaction) in bytes
366 : where the address of the account containing the list of to load is stored.
367 :
368 : Specifically, if uchar const * payload is a pointer to the first byte of
369 : the transaction data in the packet, then
370 : (fd_txn_acct_addr_t*)(payload+addr_off) is a pointer to the account
371 : address. Since (payload+acct_addr_off) points inside the packet, it
372 : should be treated as pointing to unaligned data. */
373 : ushort addr_off;
374 :
375 : /* writable_cnt: The number of account addresses this LUT selects as writable
376 : from the on-chain list. */
377 : uchar writable_cnt;
378 : /* readonly_cnt: The number of account addresses this LUT selects as read
379 : only from the on-chain list. */
380 : uchar readonly_cnt;
381 :
382 : /* writable_off: The offset (relative to the start of the transaction) in
383 : bytes where the writable account indices begins.
384 :
385 : Specifically, if uchar const * payload is a pointer to the first byte of
386 : the transaction data in the packet, then (payload+writable_off)[i] for i
387 : in [0, writable_cnt) gives the indices into the on-chain list that are
388 : selected for inclusion in this transaction's list of account addresses as
389 : writable accounts. */
390 : ushort writable_off;
391 :
392 : /* readonly_off: The offset (relative to the start of the transaction) in
393 : bytes where the read only account indices begins.
394 :
395 : Specifically, if uchar const * payload is a pointer to the first byte of
396 : the transaction data in the packet, then (payload+readonly_off)[i] for i
397 : in [0, readonly_cnt) gives the indices into the on-chain list that are
398 : selected for inclusion in this transaction's list of account addresses as
399 : read only accounts. */
400 : ushort readonly_off;
401 : };
402 :
403 : typedef struct fd_txn_acct_addr_lut fd_txn_acct_addr_lut_t;
404 :
405 87825 : #define FD_TXN_PARSE_COUNTERS_RING_SZ (32UL)
406 :
407 : /* Counters for collecting some metrics about the outcome of parsing
408 : transactions */
409 : struct fd_txn_parse_counters {
410 : /* success_cnt: the number of times a transaction parsed successfully */
411 : ulong success_cnt;
412 : /* failure_cnt: the number of times a transaction was ill-formed and failed
413 : to parse for any reason */
414 : ulong failure_cnt;
415 : /* failure_ring: some information about the causes of recent transaction
416 : parsing failures. Specifically, the line of code which detected that the
417 : ith malformed transaction was malformed maps to
418 : failure_ring[ i%FD_TXN_PARSE_COUNTERS_RING_SZ ] (where i starts at 0), and the
419 : last instance mapping to each element of the array is the one that is
420 : actually present. If fewer than FD_TXN_PARSE_COUNTERS_RING_SZ failures have
421 : occurred, the contents of some entries in this array are undefined. */
422 : ulong failure_ring[ FD_TXN_PARSE_COUNTERS_RING_SZ ];
423 : };
424 : typedef struct fd_txn_parse_counters fd_txn_parse_counters_t;
425 :
426 : FD_PROTOTYPES_BEGIN
427 : /* fd_txn_get_address_tables: Returns the array of address tables in this
428 : transaction. This depends on the value of txn->instr_cnt being correct. The
429 : lifetime of the returned pointer is the same as the fd_txn_t pointer passed
430 : as an argument, so it's not necessary to free the returned pointer
431 : separately. Treat it as if this function returned a pointer to a member of
432 : the struct. Suppose x=fd_txn_get_address_tables( txn ), then x[ i ] is valid
433 : for i in [0, txn->addr_table_lookup_cnt ). */
434 : static inline fd_txn_acct_addr_lut_t *
435 61282257 : fd_txn_get_address_tables( fd_txn_t * txn ) {
436 61282257 : return (fd_txn_acct_addr_lut_t *)(txn->instr + txn->instr_cnt);
437 61282257 : }
438 :
439 : static inline fd_txn_acct_addr_lut_t const *
440 474 : fd_txn_get_address_tables_const( fd_txn_t const * txn ) {
441 474 : return (fd_txn_acct_addr_lut_t const *)(txn->instr + txn->instr_cnt);
442 474 : }
443 :
444 : /* fd_acct_addr_t: An Solana account address, which may be an Ed25519
445 : public key, a SHA256 hash from a program derived address, a hardcoded
446 : sysvar, etc. This type does not imply any alignment. */
447 : union fd_acct_addr {
448 : uchar b[FD_TXN_ACCT_ADDR_SZ];
449 : };
450 : typedef union fd_acct_addr fd_acct_addr_t;
451 :
452 : /* fd_txn_get_{signatures, acct_addrs}: Returns the array of Ed25519
453 : signatures or account addresses (commonly, yet imprecisely called
454 : pubkeys), respectively, in `payload`, the serialization of the
455 : transaction described by `txn`. The number of signatures is seen in
456 : `txn->signature_cnt` and the number of account addresses is in
457 : `txn->acct_addr_cnt`.
458 :
459 : The lifetime of the returned signature is the lifetime of `payload`.
460 : Expect the returned pointer to point to memory with no particular
461 : alignment. U.B. If `payload` and `txn` were not arguments to a valid
462 : `fd_txn_parse` call or if either was modified after the parse call.
463 : */
464 : FD_FN_PURE static inline fd_ed25519_sig_t const *
465 : fd_txn_get_signatures( fd_txn_t const * txn,
466 153 : void const * payload ) {
467 153 : return (fd_ed25519_sig_t const *)((ulong)payload + (ulong)txn->signature_off);
468 153 : }
469 :
470 : FD_FN_PURE static inline fd_acct_addr_t const *
471 : fd_txn_get_acct_addrs( fd_txn_t const * txn,
472 856877 : void const * payload ) {
473 856877 : return (fd_acct_addr_t const *)((ulong)payload + (ulong)txn->acct_addr_off);
474 856877 : }
475 :
476 : FD_FN_PURE static inline uchar const *
477 : fd_txn_get_recent_blockhash( fd_txn_t const * txn,
478 3382 : void const * payload ) {
479 3382 : return (uchar const *)((ulong)payload + (ulong)txn->recent_blockhash_off);
480 3382 : }
481 :
482 : FD_FN_PURE static inline uchar const *
483 : fd_txn_get_instr_accts( fd_txn_instr_t const * instr,
484 15 : void const * payload ) {
485 15 : return (uchar const *)((ulong)payload + (ulong)instr->acct_off);
486 15 : }
487 :
488 : FD_FN_PURE static inline uchar const *
489 : fd_txn_get_instr_data( fd_txn_instr_t const * instr,
490 705 : void const * payload ) {
491 705 : return (uchar const *)((ulong)payload + (ulong)instr->data_off);
492 705 : }
493 :
494 : /* fd_txn_is_simple_vote_transaction: Returns 1 if `txn` is a simple
495 : vote and 0 otherwise. `txn` is a non-null pointer to a Solana
496 : transaction parsed by fd_txn_parse_core. `payload` is a non-null
497 : pointer to serialization of `txn`, which is coupled with `txn` as
498 : both `txn` and `payload` are different representations of the same
499 : data.
500 :
501 : A simple vote is a transaction that meets the following criteria:
502 : 1. has 1 or 2 signatures
503 : 2. is legacy transaction
504 : 3. has exactly one instruction
505 : 4. ...which must be a Vote instruction
506 : */
507 : static inline int
508 : fd_txn_is_simple_vote_transaction( fd_txn_t const * txn,
509 68433 : void const * payload ) {
510 : /* base58 decode of Vote111111111111111111111111111111111111111 */
511 68433 : static const uchar vote_program_id[FD_TXN_ACCT_ADDR_SZ] = {
512 68433 : 0x07U,0x61U,0x48U,0x1dU,0x35U,0x74U,0x74U,0xbbU,0x7cU,0x4dU,0x76U,0x24U,0xebU,0xd3U,0xbdU,0xb3U,
513 68433 : 0xd8U,0x35U,0x5eU,0x73U,0xd1U,0x10U,0x43U,0xfcU,0x0dU,0xa3U,0x53U,0x80U,0x00U,0x00U,0x00U,0x00U };
514 :
515 68433 : fd_acct_addr_t const * addr_base = fd_txn_get_acct_addrs( txn, payload );
516 68433 : if( FD_UNLIKELY( txn->instr_cnt!=1UL ) ) return 0;
517 7176 : if( FD_UNLIKELY( txn->transaction_version!=FD_TXN_VLEGACY ) ) return 0;
518 7161 : if( FD_UNLIKELY( txn->signature_cnt>2UL ) ) return 0;
519 7137 : ulong prog_id_idx = (ulong)txn->instr[0].program_id;
520 7137 : fd_acct_addr_t const * prog_id = addr_base + prog_id_idx;
521 7137 : return fd_memeq( prog_id->b, vote_program_id, FD_TXN_ACCT_ADDR_SZ );
522 7161 : }
523 :
524 : /* fd_txn_align returns the alignment in bytes required of a region of
525 : memory to be used as a fd_txn_t. It is the same as
526 : alignof(fd_txn_t). */
527 : static inline ulong
528 0 : fd_txn_align( void ) {
529 0 : return alignof(fd_txn_t);
530 0 : }
531 :
532 : /* fd_txn_footprint: Returns the total size of txn, including the
533 : instructions and the address tables (if any). */
534 : static inline ulong
535 : fd_txn_footprint( ulong instr_cnt,
536 61265664 : ulong addr_table_lookup_cnt ) {
537 61265664 : return sizeof(fd_txn_t) + instr_cnt*sizeof(fd_txn_instr_t) + addr_table_lookup_cnt*sizeof(fd_txn_acct_addr_lut_t);
538 61265664 : }
539 :
540 :
541 : /* Each account address in a transaction has 3 independent binary
542 : properties:
543 : - readonly/writable: this is enforced in the runtime, but a
544 : transaction fails if it tries to modify the contents of an
545 : account it marks as readonly
546 : - signer/nonsigner: the sigverify tile ensures that the transaction
547 : has been validly signed by the key associated to each account
548 : address marked as a signer
549 : - immediate/address lookup table: account addresses can come from
550 : the transaction itself ("immediate"), which is the only option
551 : for legacy transactions, or from an address lookup table
552 :
553 : For example, the fee payer must be writable, a signer, and immediate.
554 :
555 : From these properties, we can make categories of account addresses
556 : for counting and iterating over account addresses. Since these
557 : properties can be set independently, it would seem to give us 2*2*2=8
558 : categories of accounts based on the properties, but account addresses
559 : that come from an address lookup table cannot be signers, giving 6
560 : raw categories instead of 8.
561 :
562 : The individual types of accounts are defined as bitflags so that
563 : combination categories can be created easily, e.g. all readonly
564 : accounts or all signers. */
565 :
566 : /* Signer? Writable? Source? */
567 8696346 : #define FD_TXN_ACCT_CAT_WRITABLE_SIGNER 0x01 /* Yes Yes imm */
568 8143074 : #define FD_TXN_ACCT_CAT_READONLY_SIGNER 0x02 /* Yes No imm */
569 8726484 : #define FD_TXN_ACCT_CAT_WRITABLE_NONSIGNER_IMM 0x04 /* No Yes imm */
570 8173386 : #define FD_TXN_ACCT_CAT_READONLY_NONSIGNER_IMM 0x08 /* No No imm */
571 8696187 : #define FD_TXN_ACCT_CAT_WRITABLE_ALT 0x10 /* No Yes lookup */
572 8143041 : #define FD_TXN_ACCT_CAT_READONLY_ALT 0x20 /* No No lookup */
573 :
574 : /* Define some groupings for convenience. In the
575 : table below, "Any" means "don't care" */
576 243522 : #define FD_TXN_ACCT_CAT_WRITABLE 0x15 /* Any Yes Any */
577 135615 : #define FD_TXN_ACCT_CAT_READONLY 0x2A /* Any No Any */
578 1175487 : #define FD_TXN_ACCT_CAT_SIGNER 0x03 /* Yes Any Any/imm*/
579 384 : #define FD_TXN_ACCT_CAT_NONSIGNER 0x3C /* No Any Any */
580 173273 : #define FD_TXN_ACCT_CAT_IMM 0x0F /* Any Any imm */
581 38178 : #define FD_TXN_ACCT_CAT_ALT 0x30 /* No Any lookup */
582 489 : #define FD_TXN_ACCT_CAT_NONE 0x00 /* --- Empty set --- */
583 42927 : #define FD_TXN_ACCT_CAT_ALL 0x3F /* Any Any Any */
584 :
585 : /* fd_txn_account_cnt: Returns the number of accounts referenced by this
586 : transaction that have the property specified by include_category.
587 : txn must be a pointer to a valid transaction. include_cat must be
588 : one of the previously defined FD_TXN_ACCT_CAT_* values. Ideally,
589 : include_cat should be a compile-time constant, in which case this
590 : function typically compiles to about 3 instructions. */
591 : static inline ulong
592 : fd_txn_account_cnt( fd_txn_t const * txn,
593 7589511 : int include_cat ) {
594 7589511 : ulong cnt = 0UL;
595 7589511 : if( include_cat & FD_TXN_ACCT_CAT_WRITABLE_SIGNER ) cnt += (ulong)txn->signature_cnt - (ulong)txn->readonly_signed_cnt;
596 7589511 : if( include_cat & FD_TXN_ACCT_CAT_READONLY_SIGNER ) cnt += (ulong)txn->readonly_signed_cnt;
597 7589511 : if( include_cat & FD_TXN_ACCT_CAT_READONLY_NONSIGNER_IMM ) cnt += (ulong)txn->readonly_unsigned_cnt;
598 7589511 : if( include_cat & FD_TXN_ACCT_CAT_WRITABLE_ALT ) cnt += (ulong)txn->addr_table_adtl_writable_cnt;
599 7589511 : if( include_cat & FD_TXN_ACCT_CAT_WRITABLE_NONSIGNER_IMM )
600 3054993 : cnt += (ulong)txn->acct_addr_cnt - (ulong)txn->signature_cnt - (ulong)txn->readonly_unsigned_cnt;
601 7589511 : if( include_cat & FD_TXN_ACCT_CAT_READONLY_ALT )
602 630666 : cnt += (ulong)txn->addr_table_adtl_cnt - (ulong)txn->addr_table_adtl_writable_cnt;
603 :
604 7589511 : return cnt;
605 7589511 : }
606 :
607 : /* fd_txn_acct_iter_{init, next, end, idx}: These functions are used for
608 : iterating over the accounts in a transaction that have the property
609 : specified by include_cat.
610 :
611 : Example usage:
612 :
613 : fd_txn_acct_addr_t const * acct = fd_txn_get_acct_addrs( txn, payload );
614 : for( fd_txn_acct_iter_t i=fd_txn_acct_iter_init( txn, FD_TXN_ACCT_CAT_WRITABLE );
615 : i!=fd_txn_acct_iter_end(); i=fd_txn_acct_iter_next( i ) ) {
616 : // Do something with acct[ fd_txn_acct_iter_idx( i ) ]
617 : }
618 :
619 : For fd_txn_acct_iter_init, txn must be a pointer to a valid
620 : transaction and include_cat must be one of the FD_TXN_ACCT_CAT_*
621 : values defined above (or a bitwise combination of them). On
622 : completion, returns a value i such that fd_txn_acct_iter_idx( i ) is
623 : the index of the first account address meeting the specified
624 : criteria, or i==fd_txn_acct_iter_end() if there aren't any account
625 : addresses that meet the criteria.
626 :
627 : For fd_acct_iter_next, cur should be the current value of the
628 : iteration variable. Advances the iteration variable such that
629 : fd_txn_acct_iter_idx( i ) is the index of the next account meeting
630 : the initially specified criteria, or i==fd_txn_acct_iter_end() if
631 : there aren't any more account addresses meeting the criteria. It is
632 : undefined behavior to call fd_acct_iter_next with a value of cur not
633 : returned by a call to either fd_acct_iter_init or fd_acct_iter_next.
634 : It's also U.B. to call fd_acct_iter_next after fd_acct_iter_end has
635 : been returned.
636 :
637 : fd_txn_acct_iter_t should be treated as an opaque handle and not
638 : modified other than by using fd_txn_acc_iter_next. You can peek and
639 : see that it's a ulong, so it fits in a register and doesn't need to
640 : be destroyed or cleaned up. It's safe to save a fd_txn_acct_iter_t
641 : value to resume iteration later with the same transaction. */
642 :
643 : typedef ulong fd_txn_acct_iter_t;
644 :
645 : /* Account addresses are categorized into 6 categories, and all the
646 : account addresses for each category are stored contiguously. This
647 : means that for any subset of the 6 categories that the user wants to
648 : iterate over, there are at most 3 disjoint ranges.
649 :
650 : For any iteration space I, we can choose 6 integers
651 : {start,count}_{0,1,2} so that
652 : I = [start0, start0+count0) U [start1, start1+count1)
653 : U [start2, start2+count2)
654 : Any empty intervals are represented as [0, 0).
655 : We store the control word as a single ulong with start0 in the low
656 : order bits. Then the current account index can be retrieved by
657 : taking the low order byte, and the count remaining in the current
658 : interval is the second lowest byte. We can update both in one
659 : instruction by subtracting 255. */
660 :
661 : static inline fd_txn_acct_iter_t FD_FN_PURE
662 : fd_txn_acct_iter_init( fd_txn_t const * txn,
663 380409 : int include_cat ) {
664 : /* Our goal is to output something that looks like [start0, count0,
665 : start1, count1, start2, count2, 0, 0] from lowest order to highest.
666 : We construct the potentially 3 (start, count) pairs and then
667 : branchlessly get rid of any empty ones. */
668 380409 : ulong control[3] = { 0 }; /* High 6 bytes of each stay element not touched */
669 380409 : ulong i = (ulong)(-1L); /* So that it is 0 post increment */
670 :
671 : /* Make references more convenient. Dead code elimination seems to
672 : take care of the unneeded ones. */
673 380409 : ulong s = txn->signature_cnt;
674 380409 : ulong q = txn->readonly_signed_cnt;
675 380409 : ulong r = txn->readonly_unsigned_cnt;
676 380409 : ulong a = txn->acct_addr_cnt;
677 380409 : ulong t = txn->addr_table_adtl_cnt;
678 380409 : ulong u = txn->addr_table_adtl_writable_cnt;
679 :
680 : /* All the branches here should be known at compile time. */
681 : /* If WRITABLE_SIGNER is included, then f>>1 is 0, so the second
682 : branch will always be true, setting control[0]=0. */
683 380409 : # define INCLUDE_RANGE(f, start, cnt) \
684 2282454 : if( include_cat & (f) ) { \
685 1248849 : if( !(include_cat & ((f)>>1) ) ) control[ ++i ]=(start); \
686 1248849 : control[ i ] += (cnt)<<8; \
687 1248849 : }
688 :
689 380409 : INCLUDE_RANGE( FD_TXN_ACCT_CAT_WRITABLE_SIGNER, 0, s-q );
690 380409 : INCLUDE_RANGE( FD_TXN_ACCT_CAT_READONLY_SIGNER, s-q, q );
691 380409 : INCLUDE_RANGE( FD_TXN_ACCT_CAT_WRITABLE_NONSIGNER_IMM, s, a-r-s );
692 380409 : INCLUDE_RANGE( FD_TXN_ACCT_CAT_READONLY_NONSIGNER_IMM, a-r, r );
693 380409 : INCLUDE_RANGE( FD_TXN_ACCT_CAT_WRITABLE_ALT, a, u );
694 380409 : INCLUDE_RANGE( FD_TXN_ACCT_CAT_READONLY_ALT, a+u, t-u );
695 380409 : # undef INCLUDE_RANGE
696 :
697 : /* We now need to delete the empty intervals (if any). */
698 380409 : ulong control0 = control[0];
699 380409 : ulong control1 = control[1];
700 380409 : ulong control2 = control[2];
701 :
702 380409 : int control2_empty = !(control2&0xFF00UL);
703 380409 : control2 = fd_ulong_if( control2_empty, 0UL, control2 );
704 :
705 380409 : int control1_empty = !(control1&0xFF00UL);
706 380409 : control1 = fd_ulong_if( control1_empty, control2, control1 );
707 380409 : control2 = fd_ulong_if( control1_empty, 0UL, control2 );
708 :
709 380409 : int control0_empty = !(control0&0xFF00UL);
710 380409 : control0 = fd_ulong_if( control0_empty, control1, control0 );
711 380409 : control1 = fd_ulong_if( control0_empty, control2, control1 );
712 380409 : control2 = fd_ulong_if( control0_empty, 0UL, control2 );
713 :
714 380409 : return control0 | (control1<<16) | (control2<<32);
715 380409 : }
716 :
717 : static inline fd_txn_acct_iter_t FD_FN_CONST
718 1419213 : fd_txn_acct_iter_next( fd_txn_acct_iter_t cur ) {
719 1419213 : cur = cur + 0x0001UL - 0x0100UL; /* Increment low byte, decrement count */
720 : /* Move to the next interval if we're done with this one. */
721 1419213 : return fd_ulong_if( cur&0xFF00UL, cur, cur>>16 );
722 1419213 : }
723 :
724 1799622 : static inline fd_txn_acct_iter_t FD_FN_CONST fd_txn_acct_iter_end( void ) { return 0UL; }
725 1925532 : static inline ulong FD_FN_CONST fd_txn_acct_iter_idx( fd_txn_acct_iter_t cur ) { return cur & 0xFFUL; }
726 :
727 : /* fd_txn_parse_core: Parses a transaction from the canonical encoding, i.e.
728 : the format used on the wire.
729 :
730 : Payload points to the first byte of encoded transaction, e.g. the
731 : first byte of the UDP/Quic payload if the transaction comes from the
732 : network. The encoded transaction must occupy exactly [payload,
733 : payload+payload_sz), i.e. this method will read no more than
734 : payload_sz bytes from payload, but it will reject the transaction if
735 : it contains extra padding at the end or continues past
736 : payload+payload_sz.
737 :
738 : out_buf is the memory where the parsed transaction will be stored.
739 : out_buf must be non-NULL and have room for at least FD_TXN_MAX_SZ
740 : bytes.
741 :
742 : Returns the total size of the resulting fd_txn struct on success and
743 : 0 on failure. On failure, the contents of out_buf are undefined,
744 : although nothing will be written beyond FD_TXN_MAX_SZ bytes.
745 :
746 : If counters_opt is non-NULL, some counters about the result of the
747 : parsing process will be accumulated into the struct pointed to by
748 : counters_opt. Note: The returned txn object is not self-contained
749 : since it refers to byte ranges inside the payload.
750 :
751 : payload_sz_opt, if supplied, gets filled with the total bytes this txn
752 : uses (allowing for walking of an entry/microblock). If it is not supplied, the
753 : parse will return an error if the payload_sz does not exactly match. */
754 :
755 : ulong
756 : fd_txn_parse_core( uchar const * payload,
757 : ulong payload_sz,
758 : void * out_buf,
759 : fd_txn_parse_counters_t * counters_opt,
760 : ulong * payload_sz_opt );
761 :
762 :
763 : /* fd_txn_parse: Convenient wrapper around fd_txn_parse_core that eliminates some optional arguments */
764 : static inline ulong
765 61334061 : fd_txn_parse( uchar const * payload, ulong payload_sz, void * out_buf, fd_txn_parse_counters_t * counters_opt ) {
766 61334061 : return fd_txn_parse_core( payload, payload_sz, out_buf, counters_opt, NULL );
767 61334061 : }
768 :
769 : /* fd_txn_msg_sz returns the size in bytes of the signed message region
770 : of a transaction - not including the transaction signatures.
771 :
772 : The message bytes begin at message_off, relative to the start of
773 : the payload.
774 :
775 : Where these bytes are located in the transaction payload is
776 : dependent on the transaction format: legacy/V0 transactions have
777 : signatures at the front and then the message, whereas V1
778 : transactions have the message at the front and then the signatures.
779 :
780 : fd_txn_msg_sz and message_off should always be used together. */
781 : FD_FN_PURE static inline ulong
782 : fd_txn_msg_sz( fd_txn_t const * txn,
783 6753 : ulong payload_sz ) {
784 6753 : ulong msg_end = ( txn->transaction_version==FD_TXN_V1 ) ? (ulong)txn->signature_off
785 6753 : : payload_sz;
786 6753 : return msg_end - (ulong)txn->message_off;
787 6753 : }
788 :
789 : /* fd_txn_is_writable: Is the account at the supplied index writable
790 :
791 : Accounts ordered:
792 : Index Range | Signer? | Writeable?
793 : ---------------------------------------------------------------------------------|--------------|-------------
794 : [0, signature_cnt - readonly_signed_cnt) | signer | writable
795 : [signature_cnt, acct_addr_cnt - readonly_unsigned_cnt) | not signer | writable
796 : */
797 :
798 : static inline int
799 3114 : fd_txn_is_writable( fd_txn_t const * txn, ushort idx ) {
800 3114 : if (txn->transaction_version == FD_TXN_V0 && idx >= txn->acct_addr_cnt) {
801 420 : if (idx < (txn->acct_addr_cnt + txn->addr_table_adtl_writable_cnt)) {
802 0 : return 1;
803 0 : }
804 420 : return 0;
805 420 : }
806 :
807 2694 : if (idx < (txn->signature_cnt - txn->readonly_signed_cnt))
808 1176 : return 1;
809 1518 : if ((idx >= txn->signature_cnt) & (idx < (txn->acct_addr_cnt - txn->readonly_unsigned_cnt)))
810 735 : return 1;
811 :
812 783 : return 0;
813 1518 : }
814 :
815 : /* fd_txn_is_signer: Is the account at the supplied index a signer
816 :
817 : Accounts ordered:
818 : Index Range | Signer? | Writeable?
819 : ---------------------------------------------------------------------------------|--------------|-------------
820 : [0, signature_cnt - readonly_signed_cnt) | signer | writable
821 : [signature_cnt - readonly_signed_cnt, signature_cnt) | signer | readonly
822 : */
823 : static inline int
824 2028 : fd_txn_is_signer( fd_txn_t const * txn, int idx ) {
825 2028 : return idx < txn->signature_cnt;
826 2028 : }
827 :
828 : FD_PROTOTYPES_END
829 :
830 : #endif /* HEADER_fd_src_ballet_txn_fd_txn_h */
|