Line data Source code
1 :
2 : #include "fd_bank_abi.h"
3 : #include "../../flamenco/runtime/fd_system_ids_pp.h"
4 : #include "../../flamenco/runtime/fd_system_ids.h"
5 : #include "../../flamenco/types/fd_types.h"
6 : #include "../../disco/pack/fd_pack_unwritable.h"
7 : #include "../../disco/pack/fd_compute_budget_program.h"
8 :
9 : #define ABI_ALIGN( x ) __attribute__((packed)) __attribute__((aligned(x)))
10 :
11 : /* Lots of these types contain Rust structs with vectors in them. The
12 : capacity field of Rust Vec<> objects is declared with
13 : type Cap = core::num::niche_types::UsizeNoHighBit;
14 : The compiler takes advantage of this to use that high bit in
15 : discriminating between members of Rust structs. */
16 0 : #define ABI_HIGH_BIT 9223372036854775808UL
17 : FD_STATIC_ASSERT( ABI_HIGH_BIT==0x8000000000000000UL, bank_abi_tag );
18 :
19 : typedef struct ABI_ALIGN(1UL) {
20 : uchar key[ 32UL ];
21 : } sanitized_txn_abi_pubkey_t;
22 :
23 : FD_STATIC_ASSERT( sizeof(sanitized_txn_abi_pubkey_t) == 32UL, "messed up size" );
24 : FD_STATIC_ASSERT( alignof(sanitized_txn_abi_pubkey_t) == 1UL, "messed up size" );
25 :
26 : typedef struct ABI_ALIGN(1UL) {
27 : uchar signature[ 64UL ];
28 : } sanitized_txn_abi_signature_t;
29 :
30 : FD_STATIC_ASSERT( sizeof(sanitized_txn_abi_signature_t) == 64UL, "messed up size" );
31 : FD_STATIC_ASSERT( alignof(sanitized_txn_abi_signature_t) == 1UL, "messed up size" );
32 :
33 : typedef struct ABI_ALIGN(8UL) {
34 : ulong accounts_cap;
35 : uchar * accounts;
36 : ulong accounts_cnt;
37 :
38 : ulong data_cap;
39 : uchar * data;
40 : ulong data_cnt;
41 :
42 : uchar program_id_index;
43 : } sanitized_txn_abi_compiled_instruction_t;
44 :
45 : FD_STATIC_ASSERT( sizeof(sanitized_txn_abi_compiled_instruction_t) == 56UL, bank_abi );
46 : FD_STATIC_ASSERT( alignof(sanitized_txn_abi_compiled_instruction_t) == 8UL, bank_abi );
47 : FD_STATIC_ASSERT( offsetof(sanitized_txn_abi_compiled_instruction_t, accounts_cap)==0, bank_abi );
48 : FD_STATIC_ASSERT( offsetof(sanitized_txn_abi_compiled_instruction_t, data_cap)==24, bank_abi );
49 : FD_STATIC_ASSERT( offsetof(sanitized_txn_abi_compiled_instruction_t, program_id_index)==48, bank_abi );
50 :
51 : typedef struct ABI_ALIGN(1UL) {
52 : uchar num_required_signatures;
53 : uchar num_readonly_signed_accounts;
54 : uchar num_readonly_unsigned_accounts;
55 : } sanitized_txn_abi_message_header_t;
56 :
57 : FD_STATIC_ASSERT( sizeof(sanitized_txn_abi_message_header_t) == 3UL, "messed up size" );
58 : FD_STATIC_ASSERT( alignof(sanitized_txn_abi_message_header_t) == 1UL, "messed up size" );
59 :
60 : typedef struct ABI_ALIGN(8UL) {
61 : ulong account_keys_cap;
62 : sanitized_txn_abi_pubkey_t * account_keys;
63 : ulong account_keys_cnt;
64 :
65 : ulong instructions_cap;
66 : sanitized_txn_abi_compiled_instruction_t * instructions;
67 : ulong instructions_cnt;
68 :
69 : uchar recent_blockhash[ 32 ];
70 :
71 : sanitized_txn_abi_message_header_t header;
72 : } sanitized_txn_abi_legacy_message0_t;
73 :
74 : FD_STATIC_ASSERT( offsetof(sanitized_txn_abi_legacy_message0_t, account_keys_cap)==0, bank_abi );
75 : FD_STATIC_ASSERT( offsetof(sanitized_txn_abi_legacy_message0_t, instructions_cap)==24, bank_abi );
76 : FD_STATIC_ASSERT( offsetof(sanitized_txn_abi_legacy_message0_t, recent_blockhash)==48, bank_abi );
77 : FD_STATIC_ASSERT( offsetof(sanitized_txn_abi_legacy_message0_t, header)==80, bank_abi );
78 : FD_STATIC_ASSERT( sizeof(sanitized_txn_abi_legacy_message0_t) == 88UL, bank_abi );
79 : FD_STATIC_ASSERT( alignof(sanitized_txn_abi_legacy_message0_t) == 8UL, bank_abi );
80 :
81 : typedef struct ABI_ALIGN(8UL) {
82 : ulong is_writable_account_cache_cap;
83 : uchar * is_writable_account_cache;
84 : ulong is_writable_account_cache_cnt;
85 :
86 : union ABI_ALIGN(8UL) {
87 : ulong discr;
88 :
89 : /* when discr==ABI_HIGH_BIT */
90 : struct ABI_ALIGN(8UL) {
91 : uchar _padding[8];
92 : sanitized_txn_abi_legacy_message0_t * borrowed;
93 : };
94 :
95 : /* else */
96 : sanitized_txn_abi_legacy_message0_t owned;
97 : } message;
98 : } sanitized_txn_abi_legacy_message1_t;
99 :
100 : FD_STATIC_ASSERT( offsetof(sanitized_txn_abi_legacy_message1_t, message)==24UL, bank_abi );
101 : FD_STATIC_ASSERT( sizeof(sanitized_txn_abi_legacy_message1_t) == 112UL, bank_abi );
102 : FD_STATIC_ASSERT( alignof(sanitized_txn_abi_legacy_message1_t) == 8UL, bank_abi );
103 :
104 : typedef struct ABI_ALIGN(8UL) {
105 : ulong writable_indexes_cap;
106 : uchar * writable_indexes;
107 : ulong writable_indexes_cnt;
108 :
109 : ulong readonly_indexes_cap;
110 : uchar * readonly_indexes;
111 : ulong readonly_indexes_cnt;
112 :
113 : uchar account_key[ 32 ];
114 : } sanitized_txn_abi_v0_message_address_table_lookup_t;
115 :
116 : FD_STATIC_ASSERT( sizeof(sanitized_txn_abi_v0_message_address_table_lookup_t) == 80UL, "messed up size" );
117 : FD_STATIC_ASSERT( alignof(sanitized_txn_abi_v0_message_address_table_lookup_t) == 8UL, "messed up size" );
118 :
119 : typedef struct ABI_ALIGN(8UL) {
120 : ulong account_keys_cap;
121 : sanitized_txn_abi_pubkey_t * account_keys;
122 : ulong account_keys_cnt;
123 :
124 : ulong instructions_cap;
125 : sanitized_txn_abi_compiled_instruction_t * instructions;
126 : ulong instructions_cnt;
127 :
128 : ulong address_table_lookups_cap;
129 : sanitized_txn_abi_v0_message_address_table_lookup_t * address_table_lookups;
130 : ulong address_table_lookups_cnt;
131 :
132 : uchar recent_blockhash[ 32 ];
133 :
134 : sanitized_txn_abi_message_header_t header;
135 : } sanitized_txn_abi_v0_message_t;
136 :
137 : FD_STATIC_ASSERT( sizeof(sanitized_txn_abi_v0_message_t) == 112UL, "messed up size" );
138 : FD_STATIC_ASSERT( alignof(sanitized_txn_abi_v0_message_t) == 8UL, "messed up size" );
139 :
140 : typedef struct ABI_ALIGN(8UL) {
141 : ulong writable_cap;
142 : sanitized_txn_abi_pubkey_t * writable;
143 : ulong writable_cnt;
144 :
145 : ulong readable_cap;
146 : sanitized_txn_abi_pubkey_t * readable;
147 : ulong readable_cnt;
148 : } sanitized_txn_abi_v0_loaded_addresses_t;
149 :
150 : FD_STATIC_ASSERT( sizeof(sanitized_txn_abi_v0_loaded_addresses_t) == 48UL, "messed up size" );
151 : FD_STATIC_ASSERT( alignof(sanitized_txn_abi_v0_loaded_addresses_t) == 8UL, "messed up size" );
152 :
153 : typedef struct ABI_ALIGN(8UL) {
154 : ulong is_writable_account_cache_cap;
155 : uchar * is_writable_account_cache;
156 : ulong is_writable_account_cache_cnt;
157 :
158 : union __attribute__((__packed__)) __attribute__((aligned(8UL))) {
159 : ulong discr;
160 :
161 : /* when discr==ABI_HIGH_BIT */
162 : struct __attribute__((__packed__)) __attribute__((aligned(8UL))) {
163 : uchar _padding[8];
164 : sanitized_txn_abi_v0_message_t * borrowed;
165 : };
166 :
167 : /* else */
168 : struct __attribute__((__packed__)) __attribute__((aligned(8UL))) {
169 : sanitized_txn_abi_v0_message_t owned;
170 : };
171 : } message;
172 :
173 : union __attribute__((__packed__)) __attribute__((aligned(8UL))) {
174 : ulong discr;
175 :
176 : /* when discr==ABI_HIGH_BIT */
177 : struct __attribute__((__packed__)) __attribute__((aligned(8UL))) {
178 : uchar _padding[8];
179 : sanitized_txn_abi_v0_loaded_addresses_t * borrowed;
180 : };
181 :
182 : /* else */
183 : struct __attribute__((__packed__)) __attribute__((aligned(8UL))) {
184 : sanitized_txn_abi_v0_loaded_addresses_t owned;
185 : };
186 : } loaded_addresses;
187 : } sanitized_txn_abi_v0_loaded_msg_t;
188 :
189 : FD_STATIC_ASSERT( offsetof(sanitized_txn_abi_v0_loaded_msg_t, is_writable_account_cache_cap)==0, bank_abi );
190 : FD_STATIC_ASSERT( offsetof(sanitized_txn_abi_v0_loaded_msg_t, message)==24, bank_abi );
191 : FD_STATIC_ASSERT( offsetof(sanitized_txn_abi_v0_loaded_msg_t, loaded_addresses)==136, bank_abi );
192 : FD_STATIC_ASSERT( sizeof(sanitized_txn_abi_v0_loaded_msg_t)==184UL, bank_abi );
193 : FD_STATIC_ASSERT( alignof(sanitized_txn_abi_v0_loaded_msg_t)==8UL, bank_abi );
194 :
195 : typedef union ABI_ALIGN(8UL) {
196 : ulong discr;
197 :
198 : /* when discr==ABI_HIGH_BIT */
199 : struct ABI_ALIGN(8UL) {
200 : uchar _padding[8];
201 : sanitized_txn_abi_legacy_message1_t legacy;
202 : };
203 :
204 : /* else */
205 : /* No tag. Rust Vec's cap field (the first field in v0) is
206 : core::num::niche_types::UsizeNoHighBit, so this is never ambiguous. */
207 : sanitized_txn_abi_v0_loaded_msg_t v0;
208 : } sanitized_txn_abi_message_t;
209 :
210 : FD_STATIC_ASSERT( sizeof (sanitized_txn_abi_message_t) == 184UL, bank_abi );
211 : FD_STATIC_ASSERT( alignof(sanitized_txn_abi_message_t) == 8UL, bank_abi );
212 :
213 :
214 : typedef union {
215 : ulong discr;
216 : /* When discr==1 */
217 : struct {
218 : uchar _padding[8];
219 : uchar _0;
220 : ulong _1;
221 : };
222 : /* when discr==0 */
223 : /* None */
224 : } option_u8_u64_t;
225 : FD_STATIC_ASSERT( sizeof (option_u8_u64_t)==24UL, bank_abi );
226 : FD_STATIC_ASSERT( alignof(option_u8_u64_t)==8UL, bank_abi );
227 :
228 :
229 : typedef union {
230 : uint discr;
231 : /* When discr==1 */
232 : struct {
233 : uchar _padding[4];
234 : uchar _0;
235 : uint _1;
236 : };
237 : /* when discr==0 */
238 : /* None */
239 : } option_u8_u32_t;
240 : FD_STATIC_ASSERT( sizeof (option_u8_u32_t)==12UL, bank_abi );
241 : FD_STATIC_ASSERT( alignof(option_u8_u32_t)==4UL, bank_abi );
242 :
243 :
244 :
245 : struct ABI_ALIGN(8UL) fd_bank_abi_txn_private {
246 : struct ABI_ALIGN(8UL) {
247 : struct ABI_ALIGN(8UL) {
248 : option_u8_u64_t requested_compute_unit_price;
249 : option_u8_u32_t requested_compute_unit_limit;
250 : option_u8_u32_t requested_heap_size;
251 : option_u8_u32_t requested_loaded_accounts_data_size_limit;
252 :
253 : ushort num_non_compute_budget_instructions;
254 : ushort num_non_migratable_builtin_instructions;
255 : ushort num_non_builtin_instructions;
256 : ushort migrating_builtin[1]; /* The stake program */
257 : } compute_budget_instruction_details;
258 :
259 : uchar _message_hash[ 32 ]; /* with the same value as message_hash */
260 :
261 : struct ABI_ALIGN(8UL) {
262 : ulong num_transaction_signatures;
263 : ulong num_secp256k1_instruction_signatures;
264 : ulong num_ed25519_instruction_signatures;
265 : ulong num_secp256r1_instruction_signatures;
266 : }; /* TransactionSignatureDetails */
267 :
268 : ushort instruction_data_len;
269 : uchar is_simple_vote_transaction; /* same as is_simple_vote_tx */
270 : }; /* parts of the TransactionMeta */
271 :
272 : struct ABI_ALIGN(8UL) {
273 : ulong signatures_cap;
274 : sanitized_txn_abi_signature_t * signatures;
275 : ulong signatures_cnt;
276 :
277 : sanitized_txn_abi_message_t message;
278 :
279 : uchar message_hash[ 32 ];
280 : uchar is_simple_vote_tx;
281 : }; /* parts of the SanitizedTransaction */
282 : };
283 :
284 : FD_STATIC_ASSERT( sizeof (struct fd_bank_abi_txn_private)==FD_BANK_ABI_TXN_FOOTPRINT, bank_abi );
285 : FD_STATIC_ASSERT( sizeof (struct fd_bank_abi_txn_private)==392UL, bank_abi );
286 : FD_STATIC_ASSERT( alignof(struct fd_bank_abi_txn_private)==8UL, bank_abi );
287 :
288 : FD_STATIC_ASSERT( offsetof(struct fd_bank_abi_txn_private, signatures_cap )==144UL, bank_abi );
289 :
290 : FD_STATIC_ASSERT( offsetof( struct fd_bank_abi_txn_private, compute_budget_instruction_details.requested_compute_unit_price)==0, bank_abi );
291 : FD_STATIC_ASSERT( offsetof( struct fd_bank_abi_txn_private, compute_budget_instruction_details.requested_compute_unit_limit)==24, bank_abi );
292 : FD_STATIC_ASSERT( offsetof( struct fd_bank_abi_txn_private, compute_budget_instruction_details.requested_heap_size)==36, bank_abi );
293 : FD_STATIC_ASSERT( offsetof( struct fd_bank_abi_txn_private, compute_budget_instruction_details.requested_loaded_accounts_data_size_limit)==48, bank_abi );
294 : FD_STATIC_ASSERT( offsetof( struct fd_bank_abi_txn_private, compute_budget_instruction_details.num_non_compute_budget_instructions)==60, bank_abi );
295 : FD_STATIC_ASSERT( offsetof( struct fd_bank_abi_txn_private, compute_budget_instruction_details.num_non_migratable_builtin_instructions)==62, bank_abi );
296 : FD_STATIC_ASSERT( offsetof( struct fd_bank_abi_txn_private, compute_budget_instruction_details.num_non_builtin_instructions)==64, bank_abi );
297 : FD_STATIC_ASSERT( offsetof( struct fd_bank_abi_txn_private, compute_budget_instruction_details.migrating_builtin)==66, bank_abi );
298 : FD_STATIC_ASSERT( offsetof( struct fd_bank_abi_txn_private, is_simple_vote_tx)==0x180, bank_abi );
299 : FD_STATIC_ASSERT( offsetof( struct fd_bank_abi_txn_private, is_simple_vote_transaction)==0x8a, bank_abi );
300 :
301 : FD_STATIC_ASSERT( offsetof( struct fd_bank_abi_txn_private, message) -offsetof(struct fd_bank_abi_txn_private, signatures_cap)==24, bank_abi );
302 : FD_STATIC_ASSERT( offsetof( struct fd_bank_abi_txn_private, message_hash) -offsetof(struct fd_bank_abi_txn_private, signatures_cap)==208, bank_abi );
303 : FD_STATIC_ASSERT( offsetof( struct fd_bank_abi_txn_private, is_simple_vote_tx)-offsetof(struct fd_bank_abi_txn_private, signatures_cap)==240, bank_abi );
304 :
305 : static int
306 : is_key_called_as_program( fd_txn_t const * txn,
307 0 : ushort key_index ) {
308 0 : for( ushort i=0; i<txn->instr_cnt; i++ ) {
309 0 : fd_txn_instr_t const * instr = &txn->instr[ i ];
310 0 : if( FD_UNLIKELY( instr->program_id==key_index ) ) return 1;
311 0 : }
312 0 : return 0;
313 0 : }
314 :
315 : static const uchar BPF_UPGRADEABLE_PROG_ID1[32] = { BPF_UPGRADEABLE_PROG_ID };
316 :
317 : static int
318 : is_upgradeable_loader_present( fd_txn_t const * txn,
319 : uchar const * payload,
320 0 : sanitized_txn_abi_pubkey_t const * loaded_addresses ) {
321 0 : for( ushort i=0; i<txn->acct_addr_cnt; i++ ) {
322 0 : if( FD_UNLIKELY( !memcmp( payload + txn->acct_addr_off + i*32UL, BPF_UPGRADEABLE_PROG_ID1, 32UL ) ) ) return 1;
323 0 : }
324 0 : for( ushort i=0; i<txn->addr_table_adtl_cnt; i++ ) {
325 0 : if( FD_UNLIKELY( !memcmp( loaded_addresses + i, BPF_UPGRADEABLE_PROG_ID1, 32UL ) ) ) return 1;
326 0 : }
327 0 : return 0;
328 0 : }
329 :
330 : extern int
331 : fd_ext_bank_load_account( void const * bank,
332 : int fixed_root,
333 : uchar const * addr,
334 : uchar * owner,
335 : uchar * data,
336 : ulong * data_sz );
337 :
338 : int
339 : fd_bank_abi_resolve_address_lookup_tables( void const * bank,
340 : int fixed_root,
341 : ulong slot,
342 : fd_txn_t const * txn,
343 : uchar const * payload,
344 0 : fd_acct_addr_t * out_lut_accts ) {
345 0 : ulong writable_idx = 0UL;
346 0 : ulong readable_idx = 0UL;
347 0 : for( ulong i=0UL; i<txn->addr_table_lookup_cnt; i++ ) {
348 0 : fd_txn_acct_addr_lut_t const * lut = &fd_txn_get_address_tables_const( txn )[ i ];
349 0 : uchar const * addr = payload + lut->addr_off;
350 :
351 0 : uchar owner[ 32UL ];
352 0 : uchar data[ 1UL+56UL+256UL*32UL ];
353 0 : ulong data_sz = sizeof(data);
354 0 : int result = fd_ext_bank_load_account( bank, fixed_root, addr, owner, data, &data_sz );
355 0 : if( FD_UNLIKELY( result ) ) return FD_BANK_ABI_TXN_INIT_ERR_ACCOUNT_NOT_FOUND;
356 :
357 0 : result = memcmp( owner, fd_solana_address_lookup_table_program_id.key, 32UL );
358 0 : if( FD_UNLIKELY( result ) ) return FD_BANK_ABI_TXN_INIT_ERR_INVALID_ACCOUNT_OWNER;
359 :
360 0 : if( FD_UNLIKELY( (data_sz<56UL) | (data_sz>(56UL+256UL*32UL)) ) ) return FD_BANK_ABI_TXN_INIT_ERR_INVALID_ACCOUNT_DATA;
361 :
362 0 : fd_bincode_decode_ctx_t bincode = {
363 0 : .data = data,
364 0 : .dataend = data+data_sz,
365 0 : };
366 :
367 0 : ulong total_sz = 0UL;
368 0 : result = fd_address_lookup_table_state_decode_footprint( &bincode, &total_sz );
369 0 : if( FD_UNLIKELY( result!=FD_BINCODE_SUCCESS ) ) return FD_BANK_ABI_TXN_INIT_ERR_INVALID_ACCOUNT_DATA;
370 :
371 0 : fd_address_lookup_table_state_t table[1];
372 0 : fd_address_lookup_table_state_decode( table, &bincode );
373 :
374 0 : result = fd_address_lookup_table_state_is_lookup_table( table );
375 0 : if( FD_UNLIKELY( !result ) ) return FD_BANK_ABI_TXN_INIT_ERR_ACCOUNT_UNINITIALIZED;
376 :
377 0 : if( FD_UNLIKELY( (data_sz-56UL)%32UL ) ) return FD_BANK_ABI_TXN_INIT_ERR_INVALID_ACCOUNT_DATA;
378 :
379 0 : ulong addresses_len = (data_sz-56UL)/32UL;
380 0 : fd_acct_addr_t const * addresses = fd_type_pun_const( data+56UL );
381 :
382 : /* This logic is not currently very precise... an ALUT is allowed if
383 : the deactivation slot is no longer present in the slot hashes
384 : sysvar, which means that the slot was more than 512 *unskipped*
385 : slots prior. In the current case, we are just throwing out a
386 : fraction of transactions that could actually still be valid
387 : (those deactivated between 512 and 512*(1+skip_rate) slots ago. */
388 :
389 0 : ulong deactivation_slot = table->inner.lookup_table.meta.deactivation_slot;
390 0 : if( FD_UNLIKELY( deactivation_slot!=ULONG_MAX && (deactivation_slot+512UL)<slot ) ) return FD_BANK_ABI_TXN_INIT_ERR_ACCOUNT_NOT_FOUND;
391 :
392 0 : ulong active_addresses_len = fd_ulong_if( slot>table->inner.lookup_table.meta.last_extended_slot,
393 0 : addresses_len,
394 0 : table->inner.lookup_table.meta.last_extended_slot_start_index );
395 0 : for( ulong j=0UL; j<lut->writable_cnt; j++ ) {
396 0 : uchar idx = payload[ lut->writable_off+j ];
397 0 : if( FD_UNLIKELY( idx>=active_addresses_len ) ) return FD_BANK_ABI_TXN_INIT_ERR_INVALID_LOOKUP_INDEX;
398 0 : out_lut_accts[ writable_idx++ ] = addresses[ idx ];
399 0 : }
400 0 : for( ulong j=0UL; j<lut->readonly_cnt; j++ ) {
401 0 : uchar idx = payload[ lut->readonly_off+j ];
402 0 : if( FD_UNLIKELY( idx>=active_addresses_len ) ) return FD_BANK_ABI_TXN_INIT_ERR_INVALID_LOOKUP_INDEX;
403 0 : out_lut_accts[ txn->addr_table_adtl_writable_cnt+readable_idx++ ] = addresses[ idx ];
404 0 : }
405 0 : }
406 :
407 0 : return FD_BANK_ABI_TXN_INIT_SUCCESS;
408 0 : }
409 :
410 0 : #define CATEGORY_NON_BUILTIN 0
411 0 : #define CATEGORY_NON_MIGRATABLE 1
412 0 : #define CATEGORY_MIGRATING(x) (2+(x))
413 : typedef struct {
414 : uchar b[FD_TXN_ACCT_ADDR_SZ];
415 : int category;
416 : } fd_bank_abi_prog_map_t;
417 :
418 : #define MAP_PERFECT_NAME prog_map
419 : #define MAP_PERFECT_LG_TBL_SZ 4
420 : #define MAP_PERFECT_T fd_bank_abi_prog_map_t
421 : #define MAP_PERFECT_KEY b
422 : #define MAP_PERFECT_KEY_T fd_acct_addr_t const *
423 : #define MAP_PERFECT_ZERO_KEY (0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0)
424 : #define MAP_PERFECT_COMPLEX_KEY 1
425 0 : #define MAP_PERFECT_KEYS_EQUAL(k1,k2) (!memcmp( (k1), (k2), 32UL ))
426 :
427 0 : #define PERFECT_HASH( u ) ((((3776U*(u))>>28)-1U)&0xFU)
428 :
429 : #define MAP_PERFECT_HASH_PP( a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15, \
430 : a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31) \
431 : PERFECT_HASH( (a08 | (a09<<8) | (a10<<16) | (a11<<24)) )
432 0 : #define MAP_PERFECT_HASH_R( ptr ) PERFECT_HASH( fd_uint_load_4( (uchar const *)ptr->b + 8UL ) )
433 :
434 : #define MAP_PERFECT_0 ( KECCAK_SECP_PROG_ID ), .category=CATEGORY_NON_MIGRATABLE
435 : #define MAP_PERFECT_1 ( ED25519_SV_PROG_ID ), .category=CATEGORY_NON_MIGRATABLE
436 : #define MAP_PERFECT_2 ( SECP256R1_PROG_ID ), .category=CATEGORY_NON_BUILTIN /* strange, but true */
437 : #define MAP_PERFECT_3 ( VOTE_PROG_ID ), .category=CATEGORY_NON_MIGRATABLE
438 : #define MAP_PERFECT_4 ( SYS_PROG_ID ), .category=CATEGORY_NON_MIGRATABLE
439 : #define MAP_PERFECT_5 ( COMPUTE_BUDGET_PROG_ID ), .category=CATEGORY_NON_MIGRATABLE
440 : #define MAP_PERFECT_6 ( BPF_UPGRADEABLE_PROG_ID ), .category=CATEGORY_NON_MIGRATABLE
441 : #define MAP_PERFECT_7 ( BPF_LOADER_1_PROG_ID ), .category=CATEGORY_NON_MIGRATABLE
442 : #define MAP_PERFECT_8 ( BPF_LOADER_2_PROG_ID ), .category=CATEGORY_NON_MIGRATABLE
443 : #define MAP_PERFECT_9 ( LOADER_V4_PROG_ID ), .category=CATEGORY_NON_MIGRATABLE
444 : #define MAP_PERFECT_10 ( STAKE_PROG_ID ), .category=CATEGORY_MIGRATING(0)
445 :
446 :
447 : #include "../../util/tmpl/fd_map_perfect.c"
448 :
449 : /* Redefine it so we can use it below */
450 : #define MAP_PERFECT_HASH_PP( a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15, \
451 : a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31) \
452 0 : PERFECT_HASH( ((uint)a08 | ((uint)a09<<8) | ((uint)a10<<16) | ((uint)a11<<24)) )
453 0 : #define HASH( x ) MAP_PERFECT_HASH_PP( x )
454 :
455 : FD_STATIC_ASSERT( HASH( KECCAK_SECP_PROG_ID )<3, precompile_table );
456 : FD_STATIC_ASSERT( HASH( ED25519_SV_PROG_ID )<3, precompile_table );
457 : FD_STATIC_ASSERT( HASH( SECP256R1_PROG_ID )<3, precompile_table );
458 :
459 : int
460 : fd_bank_abi_txn_init( fd_bank_abi_txn_t * out_txn,
461 : uchar * out_sidecar,
462 : void const * bank,
463 : ulong slot,
464 : fd_blake3_t * blake3,
465 : uchar * payload,
466 : ulong payload_sz,
467 : fd_txn_t * txn,
468 0 : int is_simple_vote ) {
469 0 : out_txn->signatures_cnt = txn->signature_cnt;
470 0 : out_txn->signatures_cap = txn->signature_cnt;
471 0 : out_txn->signatures = (void*)(payload + txn->signature_off);
472 :
473 0 : fd_blake3_init( blake3 );
474 0 : fd_blake3_append( blake3, "solana-tx-message-v1", 20UL );
475 0 : fd_blake3_append( blake3, payload + txn->message_off, payload_sz - txn->message_off );
476 0 : fd_blake3_fini( blake3, out_txn->message_hash );
477 0 : memcpy( out_txn->_message_hash, out_txn->message_hash, 32UL );
478 :
479 0 : out_txn->is_simple_vote_tx = !!is_simple_vote;
480 0 : out_txn->is_simple_vote_transaction = !!is_simple_vote;
481 :
482 :
483 0 : ulong sig_counters[4] = { 0UL };
484 0 : ulong instr_cnt[3] = { 0UL }; /* non-builtin, non-migrating, stake program */
485 :
486 0 : fd_compute_budget_program_state_t cbp_state[1];
487 0 : fd_compute_budget_program_init( cbp_state );
488 :
489 0 : ulong instr_data_sz = 0UL;
490 0 : fd_acct_addr_t const * addr_base = fd_txn_get_acct_addrs( txn, payload );
491 0 : const fd_bank_abi_prog_map_t non_builtin[1] = { { .category = CATEGORY_NON_BUILTIN } };
492 0 : for( ulong i=0UL; i<txn->instr_cnt; i++ ) {
493 0 : ulong prog_id_idx = (ulong)txn->instr[i].program_id;
494 0 : fd_acct_addr_t const * prog_id = addr_base + prog_id_idx;
495 :
496 : /* Lookup prog_id in hash table. If it's a miss, it'll return
497 : UINT_MAX which gets clamped to 3. Otherwise, it'll be 0, 1, or
498 : 2. */
499 0 : uint hash_or_def = prog_map_hash_or_default( prog_id );
500 0 : sig_counters[ fd_uint_min( 3UL, hash_or_def ) ] +=
501 0 : (txn->instr[i].data_sz>0) ? (ulong)payload[ txn->instr[i].data_off ] : 0UL;
502 :
503 0 : instr_cnt[ prog_map_query( prog_id, non_builtin )->category ]++;
504 0 : instr_data_sz += txn->instr[i].data_sz;
505 :
506 0 : if( FD_UNLIKELY( hash_or_def==HASH( COMPUTE_BUDGET_PROG_ID ) ) ) {
507 0 : fd_compute_budget_program_parse( payload+txn->instr[i].data_off, txn->instr[i].data_sz, cbp_state );
508 0 : }
509 0 : }
510 0 : out_txn->instruction_data_len = (ushort)instr_data_sz; /* fd_txn_parse ensures this is less than MTU, so the cast is safe */
511 0 : out_txn->num_transaction_signatures = fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_SIGNER );
512 0 : out_txn->num_secp256k1_instruction_signatures = sig_counters[ HASH( KECCAK_SECP_PROG_ID ) ];
513 0 : out_txn->num_ed25519_instruction_signatures = sig_counters[ HASH( ED25519_SV_PROG_ID ) ];
514 0 : out_txn->num_secp256r1_instruction_signatures = sig_counters[ HASH( SECP256R1_PROG_ID ) ];
515 :
516 0 : out_txn->compute_budget_instruction_details.num_non_compute_budget_instructions = (ushort)(txn->instr_cnt - cbp_state->compute_budget_instr_cnt);
517 0 : out_txn->compute_budget_instruction_details.num_non_migratable_builtin_instructions = (ushort)instr_cnt[ CATEGORY_NON_MIGRATABLE ];
518 0 : out_txn->compute_budget_instruction_details.num_non_builtin_instructions = (ushort)instr_cnt[ CATEGORY_NON_BUILTIN ];
519 0 : out_txn->compute_budget_instruction_details.migrating_builtin[0] = (ushort)instr_cnt[ CATEGORY_MIGRATING(0) ];
520 : /* The instruction index doesn't matter */
521 0 : #define CBP_TO_TUPLE_OPTION( out, flag, val0, val1 ) \
522 0 : do { \
523 0 : out_txn->compute_budget_instruction_details.out.discr = !!(cbp_state->flags & FD_COMPUTE_BUDGET_PROGRAM_FLAG_ ## flag); \
524 0 : out_txn->compute_budget_instruction_details.out._0 = (val0); \
525 0 : out_txn->compute_budget_instruction_details.out._1 = (val1); \
526 0 : } while( 0 )
527 :
528 0 : CBP_TO_TUPLE_OPTION( requested_compute_unit_price, SET_FEE, 0, cbp_state->micro_lamports_per_cu );
529 0 : CBP_TO_TUPLE_OPTION( requested_compute_unit_limit, SET_CU, 0, cbp_state->compute_units );
530 0 : CBP_TO_TUPLE_OPTION( requested_heap_size, SET_HEAP, 0, cbp_state->heap_size );
531 0 : CBP_TO_TUPLE_OPTION( requested_loaded_accounts_data_size_limit, SET_LOADED_DATA_SZ, 0, cbp_state->loaded_acct_data_sz );
532 0 : #undef CBP_TO_TUPLE_OPTION
533 :
534 0 : if( FD_LIKELY( txn->transaction_version==FD_TXN_VLEGACY ) ) {
535 0 : sanitized_txn_abi_legacy_message1_t * legacy = &out_txn->message.legacy;
536 0 : sanitized_txn_abi_legacy_message0_t * message = &legacy->message.owned;
537 :
538 0 : out_txn->message.discr = ABI_HIGH_BIT;
539 :
540 0 : legacy->is_writable_account_cache_cnt = txn->acct_addr_cnt;
541 0 : legacy->is_writable_account_cache_cap = txn->acct_addr_cnt;
542 0 : legacy->is_writable_account_cache = out_sidecar;
543 0 : int _is_upgradeable_loader_present = is_upgradeable_loader_present( txn, payload, NULL );
544 0 : for( ushort i=0; i<txn->acct_addr_cnt; i++ ) {
545 0 : int is_writable = fd_txn_is_writable( txn, i ) &&
546 : /* Agave does this check, but we don't need to here because pack
547 : rejects these transactions before they make it to the bank.
548 :
549 : !fd_pack_unwritable_contains( (const fd_acct_addr_t*)(payload + txn->acct_addr_off + i*32UL) ) */
550 0 : (!is_key_called_as_program( txn, i ) || _is_upgradeable_loader_present);
551 0 : legacy->is_writable_account_cache[ i ] = !!is_writable;
552 0 : }
553 0 : out_sidecar += txn->acct_addr_cnt;
554 0 : out_sidecar = (void*)fd_ulong_align_up( (ulong)out_sidecar, 8UL );
555 :
556 0 : message->account_keys_cnt = txn->acct_addr_cnt;
557 0 : message->account_keys_cap = txn->acct_addr_cnt;
558 0 : message->account_keys = (void*)(payload + txn->acct_addr_off);
559 :
560 0 : message->instructions_cnt = txn->instr_cnt;
561 0 : message->instructions_cap = txn->instr_cnt;
562 0 : message->instructions = (void*)out_sidecar;
563 0 : for( ulong i=0; i<txn->instr_cnt; i++ ) {
564 0 : fd_txn_instr_t * instr = &txn->instr[ i ];
565 0 : sanitized_txn_abi_compiled_instruction_t * out_instr = &message->instructions[ i ];
566 :
567 0 : out_instr->accounts_cnt = instr->acct_cnt;
568 0 : out_instr->accounts_cap = instr->acct_cnt;
569 0 : out_instr->accounts = payload + instr->acct_off;
570 :
571 0 : out_instr->data_cnt = instr->data_sz;
572 0 : out_instr->data_cap = instr->data_sz;
573 0 : out_instr->data = payload + instr->data_off;
574 :
575 0 : out_instr->program_id_index = instr->program_id;
576 0 : }
577 0 : out_sidecar += txn->instr_cnt*sizeof(sanitized_txn_abi_compiled_instruction_t);
578 :
579 0 : fd_memcpy( message->recent_blockhash, payload + txn->recent_blockhash_off, 32UL );
580 0 : message->header.num_required_signatures = txn->signature_cnt;
581 0 : message->header.num_readonly_signed_accounts = txn->readonly_signed_cnt;
582 0 : message->header.num_readonly_unsigned_accounts = txn->readonly_unsigned_cnt;
583 0 : return FD_BANK_ABI_TXN_INIT_SUCCESS;
584 0 : } else if( FD_LIKELY( txn->transaction_version==FD_TXN_V0 ) ){
585 0 : sanitized_txn_abi_v0_loaded_msg_t * v0 = &out_txn->message.v0;
586 0 : sanitized_txn_abi_v0_loaded_addresses_t * loaded_addresses = &v0->loaded_addresses.owned;
587 0 : sanitized_txn_abi_v0_message_t * message = &v0->message.owned;
588 :
589 0 : int result = fd_bank_abi_resolve_address_lookup_tables( bank, 1, slot, txn, payload, (fd_acct_addr_t*)out_sidecar );
590 0 : if( FD_UNLIKELY( result!=FD_BANK_ABI_TXN_INIT_SUCCESS ) ) return result;
591 :
592 0 : ulong lut_writable_acct_cnt = fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_WRITABLE_ALT );
593 0 : loaded_addresses->writable_cnt = lut_writable_acct_cnt;
594 0 : loaded_addresses->writable_cap = lut_writable_acct_cnt;
595 0 : loaded_addresses->writable = (sanitized_txn_abi_pubkey_t*)out_sidecar;
596 0 : out_sidecar += 32UL*lut_writable_acct_cnt;
597 :
598 0 : ulong lut_readonly_acct_cnt = fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_READONLY_ALT );
599 0 : loaded_addresses->readable_cnt = lut_readonly_acct_cnt;
600 0 : loaded_addresses->readable_cap = lut_readonly_acct_cnt;
601 0 : loaded_addresses->readable = (sanitized_txn_abi_pubkey_t*)out_sidecar;
602 0 : out_sidecar += 32UL*lut_readonly_acct_cnt;
603 :
604 0 : ulong total_acct_cnt = fd_txn_account_cnt( txn, FD_TXN_ACCT_CAT_ALL );
605 0 : v0->is_writable_account_cache_cnt = total_acct_cnt;
606 0 : v0->is_writable_account_cache_cap = total_acct_cnt;
607 0 : v0->is_writable_account_cache = out_sidecar;
608 :
609 : /* This looks like it will be an OOB read because we are passing
610 : just the writable account hashes, but the readable ones are
611 : immediately after them in memory, so it's ok. */
612 0 : int _is_upgradeable_loader_present = is_upgradeable_loader_present( txn, payload, loaded_addresses->writable );
613 0 : for( ushort i=0; i<txn->acct_addr_cnt; i++ ) {
614 0 : int is_writable = fd_txn_is_writable( txn, i ) &&
615 : /* Agave does this check, but we don't need to here because pack
616 : rejects these transactions before they make it to the bank.
617 :
618 : !fd_pack_unwritable_contains( (const fd_acct_addr_t*)(payload + txn->acct_addr_off + i*32UL) ) */
619 0 : (!is_key_called_as_program( txn, i ) || _is_upgradeable_loader_present);
620 0 : v0->is_writable_account_cache[ i ] = !!is_writable;
621 0 : }
622 0 : for( ushort i=0; i<txn->addr_table_adtl_writable_cnt; i++ ) {
623 : /* We do need to check is_builtin_key_or_sysvar here, because pack
624 : has not yet loaded the address LUT accounts, so it doesn't
625 : reject these yet. */
626 0 : int is_writable = !fd_pack_unwritable_contains( (const fd_acct_addr_t*)(loaded_addresses->writable + i) ) &&
627 0 : (!is_key_called_as_program( txn, (ushort)(txn->acct_addr_cnt+i) ) || _is_upgradeable_loader_present);
628 0 : v0->is_writable_account_cache[ txn->acct_addr_cnt+i ] = !!is_writable;
629 0 : }
630 0 : for( ushort i=0; i<txn->addr_table_adtl_cnt-txn->addr_table_adtl_writable_cnt; i++ ) {
631 0 : v0->is_writable_account_cache[ txn->acct_addr_cnt+txn->addr_table_adtl_writable_cnt+i ] = 0;
632 0 : }
633 :
634 0 : out_sidecar += txn->acct_addr_cnt + txn->addr_table_adtl_cnt;
635 0 : out_sidecar = (void*)fd_ulong_align_up( (ulong)out_sidecar, 8UL );
636 :
637 0 : message->account_keys_cnt = txn->acct_addr_cnt;
638 0 : message->account_keys_cap = txn->acct_addr_cnt;
639 0 : message->account_keys = (void*)(payload + txn->acct_addr_off);
640 :
641 0 : message->instructions_cnt = txn->instr_cnt;
642 0 : message->instructions_cap = txn->instr_cnt;
643 0 : message->instructions = (void*)out_sidecar;
644 0 : for( ulong i=0; i<txn->instr_cnt; i++ ) {
645 0 : fd_txn_instr_t * instr = &txn->instr[ i ];
646 0 : sanitized_txn_abi_compiled_instruction_t * out_instr = &message->instructions[ i ];
647 :
648 0 : out_instr->accounts_cnt = instr->acct_cnt;
649 0 : out_instr->accounts_cap = instr->acct_cnt;
650 0 : out_instr->accounts = payload + instr->acct_off;
651 :
652 0 : out_instr->data_cnt = instr->data_sz;
653 0 : out_instr->data_cap = instr->data_sz;
654 0 : out_instr->data = payload + instr->data_off;
655 :
656 0 : out_instr->program_id_index = instr->program_id;
657 0 : }
658 0 : out_sidecar += txn->instr_cnt*sizeof(sanitized_txn_abi_compiled_instruction_t);
659 :
660 0 : fd_memcpy( message->recent_blockhash, payload + txn->recent_blockhash_off, 32UL );
661 0 : message->header.num_required_signatures = txn->signature_cnt;
662 0 : message->header.num_readonly_signed_accounts = txn->readonly_signed_cnt;
663 0 : message->header.num_readonly_unsigned_accounts = txn->readonly_unsigned_cnt;
664 :
665 0 : message->address_table_lookups_cnt = txn->addr_table_lookup_cnt;
666 0 : message->address_table_lookups_cap = txn->addr_table_lookup_cnt;
667 0 : message->address_table_lookups = (void*)out_sidecar;
668 0 : for( ulong i=0; i<txn->addr_table_lookup_cnt; i++ ) {
669 0 : fd_txn_acct_addr_lut_t const * lookup = fd_txn_get_address_tables_const( txn ) + i;
670 0 : sanitized_txn_abi_v0_message_address_table_lookup_t * out_lookup = &message->address_table_lookups[ i ];
671 :
672 0 : out_lookup->writable_indexes_cnt = lookup->writable_cnt;
673 0 : out_lookup->writable_indexes_cap = lookup->writable_cnt;
674 0 : out_lookup->writable_indexes = payload + lookup->writable_off;
675 :
676 0 : out_lookup->readonly_indexes_cnt = lookup->readonly_cnt;
677 0 : out_lookup->readonly_indexes_cap = lookup->readonly_cnt;
678 0 : out_lookup->readonly_indexes = payload + lookup->readonly_off;
679 :
680 0 : fd_memcpy( out_lookup->account_key, payload + lookup->addr_off, 32UL );
681 0 : }
682 0 : out_sidecar += txn->addr_table_lookup_cnt*sizeof(sanitized_txn_abi_v0_message_address_table_lookup_t);
683 :
684 0 : return FD_BANK_ABI_TXN_INIT_SUCCESS;
685 0 : } else {
686 : /* A program abort case, unknown transaction version should never make it here. */
687 0 : FD_LOG_ERR(( "unknown transaction version %u", txn->transaction_version ));
688 0 : }
689 0 : }
690 :
691 : fd_acct_addr_t const *
692 0 : fd_bank_abi_get_lookup_addresses( fd_bank_abi_txn_t const * txn ) {
693 0 : return txn->message.discr==ABI_HIGH_BIT ? NULL :
694 0 : (fd_acct_addr_t const *) txn->message.v0.loaded_addresses.owned.writable;
695 0 : }
|