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 30975699 : #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 310236 : #define FD_TXN_V0 ((uchar)0x00)
39 :
40 : /* FD_TXN_SIGNATURE_SZ: The size (in bytes) of an Ed25519 signature. */
41 369555276 : #define FD_TXN_SIGNATURE_SZ (64UL)
42 : /* FD_TXN_PUBKEY_SZ: The size (in bytes) of an Ed25519 public key. */
43 : #define FD_TXN_PUBKEY_SZ (32UL)
44 : /* FD_TXN_ACCT_ADDR_SZ: The size (in bytes) of a Solana account address.
45 : Account addresses are sometimes Ed25519 public keys, but they can also be
46 : the output of a SHA256 hash (program derived addresses and seeded accounts),
47 : or just hardcoded values (sysvars accounts). It's important that all types
48 : of account addresses have this same size. */
49 5454401559 : #define FD_TXN_ACCT_ADDR_SZ (32UL)
50 : /* FD_TXN_BLOCKHASH_SZ: The size (in bytes) of a blockhash. A blockhash is a
51 : SHA256 hash, giving a size of 256 bits = 32 bytes. */
52 61370514 : #define FD_TXN_BLOCKHASH_SZ (32UL)
53 :
54 :
55 : /* FD_TXN_SIG_MAX: The (inclusive) maximum number of signatures a transaction
56 : can have. Note: for the current MTU size of 1232 B, the maximum that a
57 : valid transaction can have is 12 signatures. The most I've seen in practice
58 : is about 7.
59 :
60 : FD_TXN_ACTUAL_SIG_MAX: used to allocate arrays of signatures, pubkeys, etc.
61 :
62 : From the spec: "The Solana runtime verifies that the number of signatures
63 : [stored as a compact-u16] matches the number in the first 8 bits of the
64 : message header."
65 : Thus this value must live in the range where compact-u16 and uint8
66 : representations are identical, hence a max of 127. */
67 221319 : #define FD_TXN_SIG_MAX (127UL)
68 324 : #define FD_TXN_ACTUAL_SIG_MAX (12UL)
69 :
70 : /* FD_TXN_ACCT_ADDR_MAX: The (inclusive) maximum number of account addresses
71 : that a transaction can have. The spec only guarantees <= 256, but the
72 : current MTU of 1232 B restricts this to 35 account addresses. An artificial
73 : limit of 64 is currently in place, but this is being changed to 128 in the
74 : near future (https://github.com/solana-labs/solana/issues/27241), so we'll
75 : use 128. */
76 : /* https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/sdk/program/src/message/versions/v0/mod.rs#L139 */
77 10308 : #define FD_TXN_ACCT_ADDR_MAX (128UL)
78 :
79 : /* FD_TXN_ADDR_TABLE_LOOKUP_MAX: The (inclusive) maximum number of address
80 : tables that this transaction references. The spec is pretty sloppy about
81 : the maximum number allowed. Since there's a maximum of 128 total accounts
82 : (including the fee payer) that the transaction can reference, if you have
83 : more than 127 table lookups, then you must have some from which you are not
84 : using any account. Realistically, the current MTU of 1232 B restricts this
85 : to 33. FIXME: We should petition to limit this to approx 8. */
86 25155 : #define FD_TXN_ADDR_TABLE_LOOKUP_MAX (127UL)
87 :
88 : /* FD_TXN_INSTR_MAX: The (inclusive) maximum number of instructions a transaction
89 : can have. As of Solana 1.15.0, this is limited to 64. */
90 198 : #define FD_TXN_INSTR_MAX (64UL)
91 :
92 :
93 : /* FD_TXN_MAX_SZ: The maximum amount of memory (in bytes) that a fd_txn can
94 : take up, including the instruction array and any address tables. The
95 : worst-case transaction is a V0 transaction with only two account
96 : addresses (a program and a fee payer), and tons of empty instructions (no
97 : accounts, no data) and as many address table lookups loading a single
98 : account as possible. */
99 42 : #define FD_TXN_MAX_SZ (852UL)
100 :
101 :
102 : /* FD_TXN_MTU: The maximum size (in bytes, inclusive) of a serialized
103 : transaction. */
104 92955 : #define FD_TXN_MTU (1232UL)
105 :
106 : /* FD_TXN_MIN_SERIALIZED_SZ: The minimum size (in bytes) of a serialized
107 : transaction, using fd_txn_parse() verification rules. */
108 0 : #define FD_TXN_MIN_SERIALIZED_SZ (134UL)
109 :
110 : /* BEGIN Agave limits */
111 :
112 : /* "Maximum number of accounts that a transaction may lock.
113 : 128 was chosen because it is the minimum number of accounts
114 : needed for the Neon EVM implementation."
115 : https://github.com/anza-xyz/agave/blob/838c1952595809a31520ff1603a13f2c9123aa51/sdk/src/transaction/sanitized.rs#L30 */
116 226785 : #define MAX_TX_ACCOUNT_LOCKS (128UL)
117 : /* In the FD runtime, we've sized things assuming up to
118 : MAX_TX_ACCOUNT_LOCKS accounts per transaction. We rely on the txn
119 : parser to enforce this limit, up till the point of
120 : validate_account_locks(). If the txn parser bumps the account limit,
121 : then we might overflow in the runtime. */
122 : FD_STATIC_ASSERT( MAX_TX_ACCOUNT_LOCKS==FD_TXN_ACCT_ADDR_MAX, num_accounts_per_txn );
123 :
124 : /* END Agave limits */
125 :
126 :
127 : /* A Solana transaction instruction, i.e. one command or step to execute in a
128 : transaction.
129 :
130 : An instruction tells the runtime to execute one on-chain program (smart
131 : contract) with some arguments (think argc, argv). The arguments come in the
132 : form of binary data and/or accounts, each of which is variable-sized and
133 : optional.
134 :
135 : Note that instructions specify accounts by giving an index into the
136 : transaction-level list of account addresses. This means there are
137 : essentially two layers of indirection: a 1 B index to a 32 B address which
138 : specifies an account. */
139 : struct fd_txn_instr {
140 : /* program_id: The on-chain program that this instruction invokes,
141 : represented as the index of the program's account address in the
142 : containing transaction's list of account addresses. */
143 : uchar program_id;
144 : uchar _padding_reserved_1; /* explicitly declare what the compiler would
145 : insert anyways */
146 :
147 : /* acct_cnt: The number of accounts this instruction references.
148 : N.B. It is possible to pass > 256 accounts to an instruction, but not more
149 : than 256 unique accounts. */
150 : ushort acct_cnt;
151 :
152 : /* data_sz: The size (in bytes) of the data passed to this instruction. The
153 : data itself is included in the transaction, so is limited to the overall
154 : transaction size. */
155 : ushort data_sz;
156 :
157 : /* acct_off: The offset (relative to the start of the transaction) in bytes
158 : where the account address index array starts. This array has size
159 : acct_cnt.
160 :
161 : Specifically, if uchar const * payload is a pointer to the first byte of
162 : the transaction data in the packet, then the array (payload+acct_off)[i]
163 : for i in [0, acct_cnt) gives all of the accounts passed to this
164 : instruction. As with the program_id, these accounts are represented as
165 : indices into the transaction's list of account addresses.
166 : */
167 : ushort acct_off;
168 :
169 : /* data_off: The offset (relative to the start of the transaction) in bytes
170 : where the instruction data array starts. This array has size data_sz.
171 :
172 : Specifically, if uchar const * payload is a pointer to the first byte of
173 : the transaction data in the packet, then the array (payload+data_off)[i]
174 : for i in [0, data_sz) gives the binary data passed to this instruction. */
175 : ushort data_off;
176 : };
177 :
178 : typedef struct fd_txn_instr fd_txn_instr_t;
179 :
180 :
181 : /* fd_txn_t: A Solana transaction. As explained above, a transaction is mostly
182 : a list of instructions, but there are a few other major components:
183 : - a list of account addresses,
184 : - the hash of a recent block (used as a nonce and TTL), and
185 : - potentially (if it's a V2 transaction) some address lookup tables. */
186 : struct fd_txn {
187 : /* transaction_version: The version number of this transaction. Currently
188 : must be one of { FD_TXN_VLEGACY, FD_TXN_V0 }. */
189 : uchar transaction_version;
190 :
191 : /* signature_cnt: The number of signatures in this transaction. signature_cnt
192 : in [1, FD_TXN_SIG_MAX]. */
193 : uchar signature_cnt;
194 :
195 : /* signature_off: The offset (relative to the start of the transaction) in
196 : bytes where the signatures start.
197 :
198 : Specifically, if uchar const * payload is a pointer to the first byte of
199 : the transaction data in the packet, then signature i starts at
200 : (payload+signature_off)[ FD_TXN_SIGNATURE_SZ*i ] for i in
201 : [0, signature_cnt).
202 :
203 : Note that signature_off is always 1 in current transaction versions. */
204 : ushort signature_off;
205 :
206 : /* message_off: The offset (relative to the start of the transaction) in
207 : bytes where the 'message' starts.
208 :
209 : The message, which is the part of the packet covered by the signatures,
210 : spans from this offset to the end of the packet. */
211 : ushort message_off;
212 :
213 : /* readonly_signed_cnt: Of the signature_cnt signatures, readonly_signed_cnt
214 : of them are read only. Since there must be a fee payer,
215 : readonly_signed_cnt in [0, signature_cnt) */
216 : uchar readonly_signed_cnt;
217 :
218 : /* readonly_unsigned_cnt: Of the account addresses that don't have an
219 : accompanying signature, readonly_unsigned_cnt of them are read only.
220 : readonly_unsigned_cnt in [0, acct_addr_cnt-signature_cnt]. Excludes any
221 : accounts from address table lookups. */
222 : uchar readonly_unsigned_cnt;
223 :
224 : /* acct_addr_cnt: The number of account addresses in this transaction.
225 : acct_addr_cnt in [1, FD_TXN_ACCT_ADDR_MAX]. Excludes any accounts from
226 : address table lookups. */
227 : ushort acct_addr_cnt;
228 :
229 : /* acct_addr_off: The offset (relative to the start of the transaction) in
230 : bytes where the account addresses start.
231 :
232 : Specifically, if uchar const * payload is a pointer to the first byte of
233 : the transaction data in the packet, then the array
234 : (payload+acct_addr_off)[ FD_TXN_ACCT_ADDR_SZ*i ] for i in [0, account_cnt)
235 : gives all of the account addresses in this transaction. Since
236 : (payload+acct_addr_off) points inside the packet, it should be treated as
237 : pointing to unaligned data.
238 :
239 : The order of these addresses is important, because it determines the
240 : "permission flags" for the account in this transaction.
241 : Accounts ordered:
242 : Index Range | Signer? | Writeable?
243 : ---------------------------------------------------------------------------------|--------------|-------------
244 : [0, signature_cnt - readonly_signed_cnt) | signer | writable
245 : [signature_cnt - readonly_signed_cnt, signature_cnt) | signer | readonly
246 : [signature_cnt, acct_addr_cnt - readonly_unsigned_cnt) | not signer | writable
247 : [acct_addr_cnt - readonly_unsigned_cnt, acct_addr_cnt) | not signer | readonly
248 : */
249 : ushort acct_addr_off;
250 :
251 : /* recent_blockhash_off: The offset (relative to the start of the
252 : transaction) in bytes where the recent blockhash starts.
253 :
254 : Specifically, if uchar const * payload is a pointer to the first byte of
255 : the transaction data in the packet, then (payload+recent_blockhash_off) is
256 : a pointer to the blockhash. Since the resulting pointer points inside the
257 : packet, it should be treated as pointing to unaligned data. In practice,
258 : recent_blockhash_off is 5 or 6 (mod 32). */
259 : ushort recent_blockhash_off;
260 :
261 : /* addr_table_lookup_cnt: The number of address lookup tables this
262 : transaction contains. Must be 0 if transaction_version==FD_TXN_VLEGACY.
263 : addr_table_lookup_cnt in [0, FD_TXN_ADDR_TABLE_LOOKUP_MAX]. */
264 : uchar addr_table_lookup_cnt;
265 :
266 : /* addr_table_adtl_writable_cnt: The total number of writable account
267 : addresses across all of the address table lookups.
268 : addr_table_adtl_writable_cnt in [0, addr_table_adtl_cnt]. */
269 : uchar addr_table_adtl_writable_cnt;
270 :
271 : /* addr_table_adtl_cnt: The total number of account addresses summed across
272 : all the address lookup tables. addr_table_adtl_cnt in
273 : [0, FD_TXN_ADDT_ADDR_MAX - acct_addr_cnt]. Since acct_addr_cnt > 0,
274 : addr_table_adtl_cnt < 256. */
275 : uchar addr_table_adtl_cnt;
276 : uchar _padding_reserved_1; /* explicit padding the compiler would have
277 : inserted anyways */
278 :
279 : /* From the address table lookups, we can add the following to the above table
280 : Index Range | Signer? | Writeable?
281 : -----------------------------------------------------------------------------------------------|--------------|-------------
282 : ...
283 : [acct_addr_cnt, acct_addr_cnt + addr_table_adtl_writable_cnt) | not signer | writable
284 : [acct_addr_cnt + addr_table_adtl_writable_cnt, acct_addr_cnt + addr_table_adtl_cnt) | not signer | readonly
285 : */
286 :
287 : /* instr_cnt: The number of instructions in this transaction.
288 : instr_cnt in [0, FD_TXN_INSTR_MAX]. */
289 : ushort instr_cnt;
290 :
291 : /* instr: The array of instructions in this transaction. It's a "flexible array
292 : member" since C does not allow the pretty typical 0-len array at the end
293 : of the struct trick.
294 : Indexed [0, instr_cnt). */
295 : fd_txn_instr_t instr[ ];
296 :
297 : /* Logically, there's another field here:
298 : address_tables: The address tables this transaction imports and which
299 : accounts from them are selected for inclusion in this transaction's
300 : overall list of accounts. Indexed [0, addr_table_lookup_cnt).
301 : fd_txn_acct_addr_lut_t address_tables[ ];
302 : To access it, call fd_txn_get_address_tables( ). */
303 :
304 : };
305 :
306 : typedef struct fd_txn fd_txn_t;
307 :
308 : /* fd_txn_acct_addr_lut: An on-chain address lookup table. Solana added this to
309 : the Transaction v2 spec in order to allow a transaction to reference more
310 : accounts. This struct specifies which account addresses from an on-chain
311 : list should be selected to include in the list of account addresses
312 : available to instructions in this transaction */
313 : struct fd_txn_acct_addr_lut {
314 : /* addr_off: The offset (relative to the start of the transaction) in bytes
315 : where the address of the account containing the list of to load is stored.
316 :
317 : Specifically, if uchar const * payload is a pointer to the first byte of
318 : the transaction data in the packet, then
319 : (fd_txn_acct_addr_t*)(payload+addr_off) is a pointer to the account
320 : address. Since (payload+acct_addr_off) points inside the packet, it
321 : should be treated as pointing to unaligned data. */
322 : ushort addr_off;
323 :
324 : /* writable_cnt: The number of account addresses this LUT selects as writable
325 : from the on-chain list. */
326 : uchar writable_cnt;
327 : /* readonly_cnt: The number of account addresses this LUT selects as read
328 : only from the on-chain list. */
329 : uchar readonly_cnt;
330 :
331 : /* writable_off: The offset (relative to the start of the transaction) in
332 : bytes where the writable account indices begins.
333 :
334 : Specifically, if uchar const * payload is a pointer to the first byte of
335 : the transaction data in the packet, then (payload+writable_off)[i] for i
336 : in [0, writable_cnt) gives the indices into the on-chain list that are
337 : selected for inclusion in this transaction's list of account addresses as
338 : writable accounts. */
339 : ushort writable_off;
340 :
341 : /* readonly_off: The offset (relative to the start of the transaction) in
342 : bytes where the read only account indices begins.
343 :
344 : Specifically, if uchar const * payload is a pointer to the first byte of
345 : the transaction data in the packet, then (payload+readonly_off)[i] for i
346 : in [0, readonly_cnt) gives the indices into the on-chain list that are
347 : selected for inclusion in this transaction's list of account addresses as
348 : read only accounts. */
349 : ushort readonly_off;
350 : };
351 :
352 : typedef struct fd_txn_acct_addr_lut fd_txn_acct_addr_lut_t;
353 :
354 87783 : #define FD_TXN_PARSE_COUNTERS_RING_SZ (32UL)
355 :
356 : /* Counters for collecting some metrics about the outcome of parsing
357 : transactions */
358 : struct fd_txn_parse_counters {
359 : /* success_cnt: the number of times a transaction parsed successfully */
360 : ulong success_cnt;
361 : /* failure_cnt: the number of times a transaction was ill-formed and failed
362 : to parse for any reason */
363 : ulong failure_cnt;
364 : /* failure_ring: some information about the causes of recent transaction
365 : parsing failures. Specifically, the line of code which detected that the
366 : ith malformed transaction was malformed maps to
367 : failure_ring[ i%FD_TXN_PARSE_COUNTERS_RING_SZ ] (where i starts at 0), and the
368 : last instance mapping to each element of the array is the one that is
369 : actually present. If fewer than FD_TXN_PARSE_COUNTERS_RING_SZ failures have
370 : occurred, the contents of some entries in this array are undefined. */
371 : ulong failure_ring[ FD_TXN_PARSE_COUNTERS_RING_SZ ];
372 : };
373 : typedef struct fd_txn_parse_counters fd_txn_parse_counters_t;
374 :
375 : FD_PROTOTYPES_BEGIN
376 : /* fd_txn_get_address_tables: Returns the array of address tables in this
377 : transaction. This depends on the value of txn->instr_cnt being correct. The
378 : lifetime of the returned pointer is the same as the fd_txn_t pointer passed
379 : as an argument, so it's not necessary to free the returned pointer
380 : separately. Treat it as if this function returned a pointer to a member of
381 : the struct. Suppose x=fd_txn_get_address_tables( txn ), then x[ i ] is valid
382 : for i in [0, txn->addr_table_lookup_cnt ). */
383 : static inline fd_txn_acct_addr_lut_t *
384 61323720 : fd_txn_get_address_tables( fd_txn_t * txn ) {
385 61323720 : return (fd_txn_acct_addr_lut_t *)(txn->instr + txn->instr_cnt);
386 61323720 : }
387 :
388 : static inline fd_txn_acct_addr_lut_t const *
389 10893 : fd_txn_get_address_tables_const( fd_txn_t const * txn ) {
390 10893 : return (fd_txn_acct_addr_lut_t const *)(txn->instr + txn->instr_cnt);
391 10893 : }
392 :
393 : /* fd_acct_addr_t: An Solana account address, which may be an Ed25519
394 : public key, a SHA256 hash from a program derived address, a hardcoded
395 : sysvar, etc. This type does not imply any alignment. */
396 : union fd_acct_addr {
397 : uchar b[FD_TXN_ACCT_ADDR_SZ];
398 : };
399 : typedef union fd_acct_addr fd_acct_addr_t;
400 :
401 : /* fd_txn_get_{signatures, acct_addrs}: Returns the array of Ed25519
402 : signatures or account addresses (commonly, yet imprecisely called
403 : pubkeys), respectively, in `payload`, the serialization of the
404 : transaction described by `txn`. The number of signatures is seen in
405 : `txn->signature_cnt` and the number of account addresses is in
406 : `txn->acct_addr_cnt`.
407 :
408 : The lifetime of the returned signature is the lifetime of `payload`.
409 : Expect the returned pointer to point to memory with no particular
410 : alignment. U.B. If `payload` and `txn` were not arguments to a valid
411 : `fd_txn_parse` call or if either was modified after the parse call.
412 : */
413 : static inline fd_ed25519_sig_t const *
414 : fd_txn_get_signatures( fd_txn_t const * txn,
415 39734235 : void const * payload ) {
416 39734235 : return (fd_ed25519_sig_t const *)((ulong)payload + (ulong)txn->signature_off);
417 39734235 : }
418 :
419 : static inline fd_acct_addr_t const *
420 : fd_txn_get_acct_addrs( fd_txn_t const * txn,
421 40292547 : void const * payload ) {
422 40292547 : return (fd_acct_addr_t const *)((ulong)payload + (ulong)txn->acct_addr_off);
423 40292547 : }
424 :
425 : static inline uchar const *
426 : fd_txn_get_recent_blockhash( fd_txn_t const * txn,
427 0 : void const * payload ) {
428 0 : return (uchar const *)((ulong)payload + (ulong)txn->recent_blockhash_off);
429 0 : }
430 :
431 : static inline uchar const *
432 : fd_txn_get_instr_accts( fd_txn_instr_t const * instr,
433 1905 : void const * payload ) {
434 1905 : return (uchar const *)((ulong)payload + (ulong)instr->acct_off);
435 1905 : }
436 :
437 : static inline uchar const *
438 : fd_txn_get_instr_data( fd_txn_instr_t const * instr,
439 22845 : void const * payload ) {
440 22845 : return (uchar const *)((ulong)payload + (ulong)instr->data_off);
441 22845 : }
442 :
443 : /* fd_txn_align returns the alignment in bytes required of a region of
444 : memory to be used as a fd_txn_t. It is the same as
445 : alignof(fd_txn_t). */
446 : static inline ulong
447 108786 : fd_txn_align( void ) {
448 108786 : return alignof(fd_txn_t);
449 108786 : }
450 :
451 : /* fd_txn_footprint: Returns the total size of txn, including the
452 : instructions and the address tables (if any). */
453 : static inline ulong
454 : fd_txn_footprint( ulong instr_cnt,
455 88260219 : ulong addr_table_lookup_cnt ) {
456 88260219 : return sizeof(fd_txn_t) + instr_cnt*sizeof(fd_txn_instr_t) + addr_table_lookup_cnt*sizeof(fd_txn_acct_addr_lut_t);
457 88260219 : }
458 :
459 :
460 : /* Each account address in a transaction has 3 independent binary
461 : properties:
462 : - readonly/writable: this is enforced in the runtime, but a
463 : transaction fails if it tries to modify the contents of an
464 : account it marks as readonly
465 : - signer/nonsigner: the sigverify tile ensures that the transaction
466 : has been validly signed by the key associated to each account
467 : address marked as a signer
468 : - immediate/address lookup table: account addresses can come from
469 : the transaction itself ("immediate"), which is the only option
470 : for legacy transactions, or from an address lookup table
471 :
472 : For example, the fee payer must be writable, a signer, and immediate.
473 :
474 : From these properties, we can make categories of account addresses
475 : for counting and iterating over account addresses. Since these
476 : properties can be set independently, it would seem to give us 2*2*2=8
477 : categories of accounts based on the properties, but account addresses
478 : that come from an address lookup table cannot be signers, giving 6
479 : raw categories instead of 8.
480 :
481 : The individual types of accounts are defined as bitflags so that
482 : combination categories can be created easily, e.g. all readonly
483 : accounts or all signers. */
484 :
485 : /* Signer? Writable? Source? */
486 162696846 : #define FD_TXN_ACCT_CAT_WRITABLE_SIGNER 0x01 /* Yes Yes imm */
487 162696846 : #define FD_TXN_ACCT_CAT_READONLY_SIGNER 0x02 /* Yes No imm */
488 162750399 : #define FD_TXN_ACCT_CAT_WRITABLE_NONSIGNER_IMM 0x04 /* No Yes imm */
489 162750399 : #define FD_TXN_ACCT_CAT_READONLY_NONSIGNER_IMM 0x08 /* No No imm */
490 162696846 : #define FD_TXN_ACCT_CAT_WRITABLE_ALT 0x10 /* No Yes lookup */
491 162696846 : #define FD_TXN_ACCT_CAT_READONLY_ALT 0x20 /* No No lookup */
492 :
493 : /* Define some groupings for convenience. In the
494 : table below, "Any" means "don't care" */
495 66391152 : #define FD_TXN_ACCT_CAT_WRITABLE 0x15 /* Any Yes Any */
496 39239676 : #define FD_TXN_ACCT_CAT_READONLY 0x2A /* Any No Any */
497 13573050 : #define FD_TXN_ACCT_CAT_SIGNER 0x03 /* Yes Any Any/imm*/
498 384 : #define FD_TXN_ACCT_CAT_NONSIGNER 0x3C /* No Any Any */
499 40227036 : #define FD_TXN_ACCT_CAT_IMM 0x0F /* Any Any imm */
500 13573050 : #define FD_TXN_ACCT_CAT_ALT 0x30 /* No Any lookup */
501 384 : #define FD_TXN_ACCT_CAT_NONE 0x00 /* --- Empty set --- */
502 384 : #define FD_TXN_ACCT_CAT_ALL 0x3F /* Any Any Any */
503 :
504 : /* fd_txn_account_cnt: Returns the number of accounts referenced by this
505 : transaction that have the property specified by include_category.
506 : txn must be a pointer to a valid transaction. include_cat must be
507 : one of the previously defined FD_TXN_ACCT_CAT_* values. Ideally,
508 : include_cat should be a compile-time constant, in which case this
509 : function typically compiles to about 3 instructions. */
510 : static inline ulong
511 : fd_txn_account_cnt( fd_txn_t const * txn,
512 162696462 : int include_cat ) {
513 162696462 : ulong cnt = 0UL;
514 162696462 : if( include_cat & FD_TXN_ACCT_CAT_WRITABLE_SIGNER ) cnt += (ulong)txn->signature_cnt - (ulong)txn->readonly_signed_cnt;
515 162696462 : if( include_cat & FD_TXN_ACCT_CAT_READONLY_SIGNER ) cnt += (ulong)txn->readonly_signed_cnt;
516 162696462 : if( include_cat & FD_TXN_ACCT_CAT_READONLY_NONSIGNER_IMM ) cnt += (ulong)txn->readonly_unsigned_cnt;
517 162696462 : if( include_cat & FD_TXN_ACCT_CAT_WRITABLE_ALT ) cnt += (ulong)txn->addr_table_adtl_writable_cnt;
518 162696462 : if( include_cat & FD_TXN_ACCT_CAT_WRITABLE_NONSIGNER_IMM )
519 135547674 : cnt += (ulong)txn->acct_addr_cnt - (ulong)txn->signature_cnt - (ulong)txn->readonly_unsigned_cnt;
520 162696462 : if( include_cat & FD_TXN_ACCT_CAT_READONLY_ALT )
521 27147249 : cnt += (ulong)txn->addr_table_adtl_cnt - (ulong)txn->addr_table_adtl_writable_cnt;
522 :
523 162696462 : return cnt;
524 162696462 : }
525 :
526 : /* fd_txn_acct_iter_{init, next, end, idx}: These functions are used for
527 : iterating over the accounts in a transaction that have the property
528 : specified by include_cat.
529 :
530 : Example usage:
531 :
532 : fd_txn_acct_addr_t const * acct = fd_txn_get_acct_addrs( txn, payload );
533 : for( fd_txn_acct_iter_t i=fd_txn_acct_iter_init( txn, FD_TXN_ACCT_CAT_WRITABLE );
534 : i!=fd_txn_acct_iter_end(); i=fd_txn_acct_iter_next( i ) ) {
535 : // Do something with acct[ fd_txn_acct_iter_idx( i ) ]
536 : }
537 :
538 : For fd_txn_acct_iter_init, txn must be a pointer to a valid
539 : transaction and include_cat must be one of the FD_TXN_ACCT_CAT_*
540 : values defined above (or a bitwise combination of them). On
541 : completion, returns a value i such that fd_txn_acct_iter_idx( i ) is
542 : the index of the first account address meeting the specified
543 : criteria, or i==fd_txn_acct_iter_end() if there aren't any account
544 : addresses that meet the criteria.
545 :
546 : For fd_acct_iter_next, cur should be the current value of the
547 : iteration variable. Advances the iteration variable such that
548 : fd_txn_acct_iter_idx( i ) is the index of the next account meeting
549 : the initially specified criteria, or i==fd_txn_acct_iter_end() if
550 : there aren't any more account addresses meeting the criteria. It is
551 : undefined behavior to call fd_acct_iter_next with a value of cur not
552 : returned by a call to either fd_acct_iter_init or fd_acct_iter_next.
553 : It's also U.B. to call fd_acct_iter_next after fd_acct_iter_end has
554 : been returned.
555 :
556 : fd_txn_acct_iter_t should be treated as an opaque handle and not
557 : modified other than by using fd_txn_acc_iter_next. You can peek and
558 : see that it's a ulong, so it fits in a register and doesn't need to
559 : be destroyed or cleaned up. It's safe to save a fd_txn_acct_iter_t
560 : value to resume iteration later with the same transaction. */
561 :
562 : typedef ulong fd_txn_acct_iter_t;
563 :
564 : /* Account addresses are categorized into 6 categories, and all the
565 : account addresses for each category are stored contiguously. This
566 : means that for any subset of the 6 categories that the user wants to
567 : iterate over, there are at most 3 disjoint ranges.
568 :
569 : For any iteration space I, we can choose 6 integers
570 : {start,count}_{0,1,2} so that
571 : I = [start0, start0+count0) U [start1, start1+count1)
572 : U [start2, start2+count2)
573 : Any empty intervals are represented as [0, 0).
574 : We store the control word as a single ulong with start0 in the low
575 : order bits. Then the current account index can be retrieved by
576 : taking the low order byte, and the count remaining in the current
577 : interval is the second lowest byte. We can update both in one
578 : instruction by subtracting 255. */
579 :
580 : static inline fd_txn_acct_iter_t FD_FN_PURE
581 : fd_txn_acct_iter_init( fd_txn_t const * txn,
582 92164149 : int include_cat ) {
583 : /* Our goal is to output something that looks like [start0, count0,
584 : start1, count1, start2, count2, 0, 0] from lowest order to highest.
585 : We construct the potentially 3 (start, count) pairs and then
586 : branchlessly get rid of any empty ones. */
587 92164149 : ulong control[3] = { 0 }; /* High 6 bytes of each stay element not touched */
588 92164149 : ulong i = (ulong)(-1L); /* So that it is 0 post increment */
589 :
590 : /* Make references more convenient. Dead code elimination seems to
591 : take care of the unneeded ones. */
592 92164149 : ulong s = txn->signature_cnt;
593 92164149 : ulong q = txn->readonly_signed_cnt;
594 92164149 : ulong r = txn->readonly_unsigned_cnt;
595 92164149 : ulong a = txn->acct_addr_cnt;
596 92164149 : ulong t = txn->addr_table_adtl_cnt;
597 92164149 : ulong u = txn->addr_table_adtl_writable_cnt;
598 :
599 : /* All the branches here should be known at compile time. */
600 : /* If WRITABLE_SIGNER is included, then f>>1 is 0, so the second
601 : branch will always be true, setting control[0]=0. */
602 92164149 : # define INCLUDE_RANGE(f, start, cnt) \
603 552984894 : if( include_cat & (f) ) { \
604 276271695 : if( !(include_cat & ((f)>>1) ) ) control[ ++i ]=(start); \
605 276271695 : control[ i ] += (cnt)<<8; \
606 276271695 : }
607 :
608 92164149 : INCLUDE_RANGE( FD_TXN_ACCT_CAT_WRITABLE_SIGNER, 0, s-q );
609 92164149 : INCLUDE_RANGE( FD_TXN_ACCT_CAT_READONLY_SIGNER, s-q, q );
610 92164149 : INCLUDE_RANGE( FD_TXN_ACCT_CAT_WRITABLE_NONSIGNER_IMM, s, a-r-s );
611 92164149 : INCLUDE_RANGE( FD_TXN_ACCT_CAT_READONLY_NONSIGNER_IMM, a-r, r );
612 92164149 : INCLUDE_RANGE( FD_TXN_ACCT_CAT_WRITABLE_ALT, a, u );
613 92164149 : INCLUDE_RANGE( FD_TXN_ACCT_CAT_READONLY_ALT, a+u, t-u );
614 92164149 : # undef INCLUDE_RANGE
615 :
616 : /* We now need to delete the empty intervals (if any). */
617 92164149 : ulong control0 = control[0];
618 92164149 : ulong control1 = control[1];
619 92164149 : ulong control2 = control[2];
620 :
621 92164149 : int control2_empty = !(control2&0xFF00UL);
622 92164149 : control2 = fd_ulong_if( control2_empty, 0UL, control2 );
623 :
624 92164149 : int control1_empty = !(control1&0xFF00UL);
625 92164149 : control1 = fd_ulong_if( control1_empty, control2, control1 );
626 92164149 : control2 = fd_ulong_if( control1_empty, 0UL, control2 );
627 :
628 92164149 : int control0_empty = !(control0&0xFF00UL);
629 92164149 : control0 = fd_ulong_if( control0_empty, control1, control0 );
630 92164149 : control1 = fd_ulong_if( control0_empty, control2, control1 );
631 92164149 : control2 = fd_ulong_if( control0_empty, 0UL, control2 );
632 :
633 92164149 : return control0 | (control1<<16) | (control2<<32);
634 92164149 : }
635 :
636 : static inline fd_txn_acct_iter_t FD_FN_CONST
637 68519679 : fd_txn_acct_iter_next( fd_txn_acct_iter_t cur ) {
638 68519679 : cur = cur + 0x0001UL - 0x0100UL; /* Increment low byte, decrement count */
639 : /* Move to the next interval if we're done with this one. */
640 68519679 : return fd_ulong_if( cur&0xFF00UL, cur, cur>>16 );
641 68519679 : }
642 :
643 0 : static inline fd_txn_acct_iter_t FD_FN_CONST fd_txn_acct_iter_end( void ) { return 0UL; }
644 68519691 : static inline ulong FD_FN_CONST fd_txn_acct_iter_idx( fd_txn_acct_iter_t cur ) { return cur & 0xFFUL; }
645 :
646 : /* fd_txn_parse_core: Parses a transaction from the canonical encoding, i.e.
647 : the format used on the wire.
648 :
649 : Payload points to the first byte of encoded transaction, e.g. the
650 : first byte of the UDP/Quic payload if the transaction comes from the
651 : network. The encoded transaction must occupy exactly [payload,
652 : payload+payload_sz), i.e. this method will read no more than
653 : payload_sz bytes from payload, but it will reject the transaction if
654 : it contains extra padding at the end or continues past
655 : payload+payload_sz. payload_sz <= FD_TXN_MTU.
656 :
657 : out_buf is the memory where the parsed transaction will be stored.
658 : out_buf must have room for at least FD_TXN_MAX_SZ bytes.
659 :
660 : Returns the total size of the resulting fd_txn struct on success and
661 : 0 on failure. On failure, the contents of out_buf are undefined,
662 : although nothing will be written beyond FD_TXN_MAX_SZ bytes.
663 :
664 : If counters_opt is non-NULL, some counters about the result of the
665 : parsing process will be accumulated into the struct pointed to by
666 : counters_opt. Note: The returned txn object is not self-contained
667 : since it refers to byte ranges inside the payload.
668 :
669 : payload_sz_opt, if supplied, gets filled with the total bytes this txn
670 : uses (allowing for walking of an entry/microblock). If it is not supplied, the
671 : parse will return an error if the payload_sz does not exactly match. */
672 :
673 : ulong
674 : fd_txn_parse_core( uchar const * payload,
675 : ulong payload_sz,
676 : void * out_buf,
677 : fd_txn_parse_counters_t * counters_opt,
678 : ulong * payload_sz_opt );
679 :
680 :
681 : /* fd_txn_parse: Convenient wrapper around fd_txn_parse_core that eliminates some optional arguments */
682 : static inline ulong
683 61357776 : fd_txn_parse( uchar const * payload, ulong payload_sz, void * out_buf, fd_txn_parse_counters_t * counters_opt ) {
684 61357776 : return fd_txn_parse_core( payload, payload_sz, out_buf, counters_opt, NULL );
685 61357776 : }
686 :
687 : /* fd_txn_is_writable: Is the account at the supplied index writable
688 :
689 : Accounts ordered:
690 : Index Range | Signer? | Writeable?
691 : ---------------------------------------------------------------------------------|--------------|-------------
692 : [0, signature_cnt - readonly_signed_cnt) | signer | writable
693 : [signature_cnt, acct_addr_cnt - readonly_unsigned_cnt) | not signer | writable
694 : */
695 :
696 : static inline int
697 82524 : fd_txn_is_writable( fd_txn_t const * txn, int idx ) {
698 82524 : if (txn->transaction_version == FD_TXN_V0 && idx >= txn->acct_addr_cnt) {
699 25017 : if (idx < (txn->acct_addr_cnt + txn->addr_table_adtl_writable_cnt)) {
700 16731 : return 1;
701 16731 : }
702 8286 : return 0;
703 25017 : }
704 :
705 57507 : if (idx < (txn->signature_cnt - txn->readonly_signed_cnt))
706 41088 : return 1;
707 16419 : if ((idx >= txn->signature_cnt) & (idx < (txn->acct_addr_cnt - txn->readonly_unsigned_cnt)))
708 5763 : return 1;
709 :
710 10656 : return 0;
711 16419 : }
712 :
713 : /* fd_txn_is_signer: Is the account at the supplied index a signer
714 :
715 : Accounts ordered:
716 : Index Range | Signer? | Writeable?
717 : ---------------------------------------------------------------------------------|--------------|-------------
718 : [0, signature_cnt - readonly_signed_cnt) | signer | writable
719 : [signature_cnt - readonly_signed_cnt, signature_cnt) | signer | readonly
720 : */
721 : static inline int
722 36387 : fd_txn_is_signer( fd_txn_t const * txn, int idx ) {
723 36387 : return idx < txn->signature_cnt;
724 36387 : }
725 :
726 : FD_PROTOTYPES_END
727 :
728 : #endif /* HEADER_fd_src_ballet_txn_fd_txn_h */
|