Skip to content

Commit 0a6014f

Browse files
committed
flamenco, precompile: clean up precompile execution
1 parent ebc737a commit 0a6014f

File tree

5 files changed

+164
-213
lines changed

5 files changed

+164
-213
lines changed

src/flamenco/runtime/fd_executor.c

Lines changed: 108 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -81,36 +81,54 @@ typedef struct fd_native_prog_info fd_native_prog_info_t;
8181
#undef PERFECT_HASH
8282

8383
#define MAP_PERFECT_NAME fd_native_precompile_program_fn_lookup_tbl
84-
#define MAP_PERFECT_LG_TBL_SZ 4
84+
#define MAP_PERFECT_LG_TBL_SZ 2
8585
#define MAP_PERFECT_T fd_native_prog_info_t
86-
#define MAP_PERFECT_HASH_C 478U
86+
#define MAP_PERFECT_HASH_C 63546U
8787
#define MAP_PERFECT_KEY key.uc
8888
#define MAP_PERFECT_KEY_T fd_pubkey_t const *
8989
#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)
9090
#define MAP_PERFECT_COMPLEX_KEY 1
9191
#define MAP_PERFECT_KEYS_EQUAL(k1,k2) (!memcmp( (k1), (k2), 32UL ))
9292

93-
#define PERFECT_HASH( u ) (((MAP_PERFECT_HASH_C*(u))>>28)&0xFU)
93+
#define PERFECT_HASH( u ) (((MAP_PERFECT_HASH_C*(u))>>30)&0x3U)
9494

9595
#define MAP_PERFECT_HASH_PP( a00,a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15, \
9696
a16,a17,a18,a19,a20,a21,a22,a23,a24,a25,a26,a27,a28,a29,a30,a31) \
97-
PERFECT_HASH( (a08 | (a09<<8) | (a10<<16) | (a11<<24)) )
98-
#define MAP_PERFECT_HASH_R( ptr ) PERFECT_HASH( fd_uint_load_4( (uchar const *)ptr + 8UL ) )
97+
PERFECT_HASH( (a00 | (a01<<8)) )
98+
#define MAP_PERFECT_HASH_R( ptr ) PERFECT_HASH( fd_uint_load_2( (uchar const *)ptr ) )
9999

100-
#define MAP_PERFECT_0 ( ED25519_SV_PROG_ID ), .fn = fd_precompile_ed25519_execute
101-
#define MAP_PERFECT_1 ( KECCAK_SECP_PROG_ID ), .fn = fd_precompile_secp256k1_execute
102-
#define MAP_PERFECT_2 ( SECP256R1_PROG_ID ), .fn = fd_precompile_secp256r1_execute
100+
#define MAP_PERFECT_0 ( ED25519_SV_PROG_ID ), .fn = fd_precompile_ed25519_verify
101+
#define MAP_PERFECT_1 ( KECCAK_SECP_PROG_ID ), .fn = fd_precompile_secp256k1_verify
102+
#define MAP_PERFECT_2 ( SECP256R1_PROG_ID ), .fn = fd_precompile_secp256r1_verify
103103

104104
#include "../../util/tmpl/fd_map_perfect.c"
105105
#undef PERFECT_HASH
106106

107-
/* https://github.com/anza-xyz/agave/blob/v2.2.6/program-runtime/src/invoke_context.rs#L520-L544 */
108-
int
107+
fd_exec_instr_fn_t
108+
fd_executor_lookup_native_precompile_program( fd_txn_account_t const * prog_acc ) {
109+
fd_pubkey_t const * pubkey = prog_acc->pubkey;
110+
const fd_native_prog_info_t null_function = {0};
111+
return fd_native_precompile_program_fn_lookup_tbl_query( pubkey, &null_function )->fn;
112+
}
113+
114+
/* fd_executor_lookup_native_program returns the appropriate instruction processor for the given
115+
native program ID. Returns NULL if given ID is not a recognized native program.
116+
https://github.com/anza-xyz/agave/blob/v2.2.6/program-runtime/src/invoke_context.rs#L520-L544 */
117+
static int
109118
fd_executor_lookup_native_program( fd_txn_account_t const * prog_acc,
110119
fd_exec_txn_ctx_t * txn_ctx,
111-
fd_exec_instr_fn_t * native_prog_fn ) {
112-
fd_pubkey_t const * pubkey = prog_acc->pubkey;
113-
fd_pubkey_t const * owner = prog_acc->vt->get_owner( prog_acc );
120+
fd_exec_instr_fn_t * native_prog_fn,
121+
uchar * is_precompile ) {
122+
/* First lookup to see if the program key is a precompile */
123+
*is_precompile = 0;
124+
*native_prog_fn = fd_executor_lookup_native_precompile_program( prog_acc );
125+
if( FD_UNLIKELY( *native_prog_fn!=NULL ) ) {
126+
*is_precompile = 1;
127+
return 0;
128+
}
129+
130+
fd_pubkey_t const * pubkey = prog_acc->pubkey;
131+
fd_pubkey_t const * owner = prog_acc->vt->get_owner( prog_acc );
114132

115133
/* Native programs should be owned by the native loader...
116134
This will not be the case though once core programs are migrated to BPF. */
@@ -125,25 +143,12 @@ fd_executor_lookup_native_program( fd_txn_account_t const * prog_acc,
125143
}
126144
}
127145

128-
if( FD_UNLIKELY( !memcmp( pubkey, fd_solana_ed25519_sig_verify_program_id.key, sizeof(fd_pubkey_t) ) &&
129-
!memcmp( owner, fd_solana_system_program_id.key, sizeof(fd_pubkey_t) ) ) ) {
130-
/* ... except for the special case for testnet ed25519, which is
131-
bizarrely owned by the system program. */
132-
is_native_program = 1;
133-
}
134-
fd_pubkey_t const * lookup_pubkey = is_native_program ? pubkey : owner;
135-
const fd_native_prog_info_t null_function = {0};
146+
fd_pubkey_t const * lookup_pubkey = is_native_program ? pubkey : owner;
147+
fd_native_prog_info_t const null_function = {0};
136148
*native_prog_fn = fd_native_program_fn_lookup_tbl_query( lookup_pubkey, &null_function )->fn;
137149
return 0;
138150
}
139151

140-
fd_exec_instr_fn_t
141-
fd_executor_lookup_native_precompile_program( fd_txn_account_t const * prog_acc ) {
142-
fd_pubkey_t const * pubkey = prog_acc->pubkey;
143-
const fd_native_prog_info_t null_function = {0};
144-
return fd_native_precompile_program_fn_lookup_tbl_query( pubkey, &null_function )->fn;
145-
}
146-
147152
/* Returns 1 if the sysvar instruction is used, 0 otherwise */
148153
uint
149154
fd_executor_txn_uses_sysvar_instructions( fd_exec_txn_ctx_t const * txn_ctx ) {
@@ -336,35 +341,50 @@ fd_executor_check_transactions( fd_exec_txn_ctx_t * txn_ctx ) {
336341
/* https://github.com/anza-xyz/agave/blob/v2.1.0/runtime/src/verify_precompiles.rs#L11-L34 */
337342
int
338343
fd_executor_verify_precompiles( fd_exec_txn_ctx_t * txn_ctx ) {
339-
ushort instr_cnt = txn_ctx->txn_descriptor->instr_cnt;
340-
fd_acct_addr_t const * tx_accs = fd_txn_get_acct_addrs( txn_ctx->txn_descriptor, txn_ctx->_txn_raw->raw );
341-
int err = 0;
344+
ushort instr_cnt = txn_ctx->txn_descriptor->instr_cnt;
345+
int err = 0;
346+
342347
for( ushort i=0; i<instr_cnt; i++ ) {
343-
fd_txn_instr_t const * instr = &txn_ctx->txn_descriptor->instr[i];
344-
fd_acct_addr_t const * program_id = tx_accs + instr->program_id;
345-
if( !memcmp( program_id, &fd_solana_ed25519_sig_verify_program_id, sizeof(fd_pubkey_t) ) ) {
346-
err = fd_precompile_ed25519_verify( txn_ctx, instr );
347-
if( FD_UNLIKELY( err ) ) {
348-
FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, err, i );
349-
return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
350-
}
351-
} else if( !memcmp( program_id, &fd_solana_keccak_secp_256k_program_id, sizeof(fd_pubkey_t) )) {
352-
err = fd_precompile_secp256k1_verify( txn_ctx, instr );
353-
if( FD_UNLIKELY( err ) ) {
354-
FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, err, i );
355-
return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
356-
}
357-
} else if( !memcmp( program_id, &fd_solana_secp256r1_program_id, sizeof(fd_pubkey_t)) && FD_FEATURE_ACTIVE( txn_ctx->slot, txn_ctx->features, enable_secp256r1_precompile ) ) {
358-
err = fd_precompile_secp256r1_verify( txn_ctx, instr );
359-
if( FD_UNLIKELY( err ) ) {
360-
FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, err, i );
361-
return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
362-
}
348+
fd_instr_info_t const * instr = &txn_ctx->instr_infos[i];
349+
fd_txn_account_t const * program_acc = &txn_ctx->accounts[ instr->program_id ];
350+
fd_exec_instr_fn_t precompile_fn = fd_executor_lookup_native_precompile_program( program_acc );
351+
352+
/* We need to handle feature-gated precompiles here as well since they're not accounted for in the precompile lookup table. */
353+
if( FD_LIKELY( precompile_fn==NULL ||
354+
( !memcmp( program_acc->pubkey->key, &fd_solana_secp256r1_program_id.key, sizeof(fd_pubkey_t) ) &&
355+
!FD_FEATURE_ACTIVE( txn_ctx->slot, txn_ctx->features, enable_secp256r1_precompile ) ) ) ) {
356+
continue;
357+
}
358+
359+
/* We can create a mock instr_ctx since we only need the txn_ctx and instr fields */
360+
fd_exec_instr_ctx_t instr_ctx = {
361+
.txn_ctx = txn_ctx,
362+
.instr = instr,
363+
};
364+
365+
err = precompile_fn( &instr_ctx );
366+
if( FD_UNLIKELY( err ) ) {
367+
FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, err, i );
368+
return FD_RUNTIME_TXN_ERR_INSTRUCTION_ERROR;
363369
}
364370
}
371+
365372
return FD_RUNTIME_EXECUTE_SUCCESS;
366373
}
367374

375+
static void
376+
fd_executor_setup_instr_infos_from_txn_instrs( fd_exec_txn_ctx_t * txn_ctx ) {
377+
ushort instr_cnt = txn_ctx->txn_descriptor->instr_cnt;
378+
379+
/* Set up the instr infos for the transaction */
380+
for( ushort i=0; i<instr_cnt; i++ ) {
381+
fd_txn_instr_t const * instr = &txn_ctx->txn_descriptor->instr[i];
382+
fd_instr_info_init_from_txn_instr( &txn_ctx->instr_infos[i], txn_ctx, instr );
383+
}
384+
385+
txn_ctx->instr_info_cnt = instr_cnt;
386+
}
387+
368388
/* https://github.com/anza-xyz/agave/blob/v2.0.9/svm/src/account_loader.rs#L410-427 */
369389
static int
370390
accumulate_and_check_loaded_account_data_size( ulong acc_size,
@@ -432,18 +452,9 @@ load_transaction_account( fd_exec_txn_ctx_t * txn_ctx,
432452
https://github.com/anza-xyz/agave/blob/v2.2.0/svm/src/account_loader.rs#L393-L534 */
433453
int
434454
fd_executor_load_transaction_accounts( fd_exec_txn_ctx_t * txn_ctx ) {
435-
ulong requested_loaded_accounts_data_size = txn_ctx->loaded_accounts_data_size_limit;
436-
ushort instr_cnt = txn_ctx->txn_descriptor->instr_cnt;
437-
438-
/* Set up the instr infos for the transaction */
439-
for( ushort i=0; i<instr_cnt; i++ ) {
440-
fd_txn_instr_t const * instr = &txn_ctx->txn_descriptor->instr[i];
441-
fd_instr_info_init_from_txn_instr( &txn_ctx->instr_infos[i], txn_ctx, instr );
442-
}
443-
txn_ctx->instr_info_cnt = txn_ctx->txn_descriptor->instr_cnt;
444-
445-
fd_epoch_schedule_t const * schedule = fd_sysvar_cache_epoch_schedule( txn_ctx->sysvar_cache );
446-
ulong epoch = fd_slot_to_epoch( schedule, txn_ctx->slot, NULL );
455+
ulong requested_loaded_accounts_data_size = txn_ctx->loaded_accounts_data_size_limit;
456+
fd_epoch_schedule_t const * schedule = fd_sysvar_cache_epoch_schedule( txn_ctx->sysvar_cache );
457+
ulong epoch = fd_slot_to_epoch( schedule, txn_ctx->slot, NULL );
447458

448459
/* https://github.com/anza-xyz/agave/blob/v2.2.0/svm/src/account_loader.rs#L429-L443 */
449460
for( ushort i=0; i<txn_ctx->accounts_cnt; i++ ) {
@@ -481,6 +492,7 @@ fd_executor_load_transaction_accounts( fd_exec_txn_ctx_t * txn_ctx ) {
481492
}
482493

483494
/* TODO: Consider using a hash set (if its more performant) */
495+
ushort instr_cnt = txn_ctx->txn_descriptor->instr_cnt;
484496
fd_pubkey_t validated_loaders[instr_cnt];
485497
ushort validated_loaders_cnt = 0;
486498

@@ -1065,28 +1077,42 @@ fd_execute_instr( fd_exec_txn_ctx_t * txn_ctx,
10651077
.stack_height = txn_ctx->instr_stack_sz,
10661078
};
10671079

1068-
/* Lookup whether the program is a native precompiled program first
1080+
/* Look up the native program. We check for precompiles within the lookup function as well.
10691081
https://github.com/anza-xyz/agave/blob/v2.1.6/svm/src/message_processor.rs#L88 */
1070-
fd_exec_instr_fn_t native_prog_fn = fd_executor_lookup_native_precompile_program( &txn_ctx->accounts[ instr->program_id ] );
1082+
fd_exec_instr_fn_t native_prog_fn;
1083+
uchar is_precompile;
1084+
int err = fd_executor_lookup_native_program( &txn_ctx->accounts[ instr->program_id ],
1085+
txn_ctx,
1086+
&native_prog_fn,
1087+
&is_precompile );
10711088

1072-
if( FD_LIKELY( native_prog_fn == NULL ) ) {
1073-
/* Lookup a native builtin program if the program is not a precompiled program */
1074-
int err = fd_executor_lookup_native_program( &txn_ctx->accounts[ instr->program_id ], txn_ctx, &native_prog_fn );
1075-
if( FD_UNLIKELY( err ) ) {
1076-
FD_TXN_PREPARE_ERR_OVERWRITE( txn_ctx );
1077-
FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, err, txn_ctx->instr_err_idx );
1078-
return err;
1079-
}
1080-
/* Only reset the return data when executing a native builtin program (not a precompile)
1081-
https://github.com/anza-xyz/agave/blob/v2.1.6/program-runtime/src/invoke_context.rs#L536-L537 */
1082-
fd_exec_txn_ctx_reset_return_data( txn_ctx );
1089+
if( FD_UNLIKELY( err ) ) {
1090+
FD_TXN_PREPARE_ERR_OVERWRITE( txn_ctx );
1091+
FD_TXN_ERR_FOR_LOG_INSTR( txn_ctx, err, txn_ctx->instr_err_idx );
1092+
return err;
10831093
}
10841094

1085-
if( FD_LIKELY( native_prog_fn != NULL ) ) {
1086-
/* Log program invokation (internally caches program_id base58) */
1095+
if( FD_LIKELY( native_prog_fn!=NULL ) ) {
1096+
/* If this branch is taken, we've found an entrypoint to execute. */
10871097
fd_log_collector_program_invoke( ctx );
1088-
instr_exec_result = native_prog_fn( ctx );
1098+
1099+
/* Only reset the return data when executing a native builtin program (not a precompile)
1100+
https://github.com/anza-xyz/agave/blob/v2.1.6/program-runtime/src/invoke_context.rs#L536-L537 */
1101+
if( FD_LIKELY( !is_precompile ) ) {
1102+
fd_exec_txn_ctx_reset_return_data( txn_ctx );
1103+
}
1104+
1105+
/* Unconditionally execute the native program if precompile verification has been moved to svm,
1106+
or if the native program is not a precompile */
1107+
if( FD_LIKELY( FD_FEATURE_ACTIVE( txn_ctx->slot, txn_ctx->features, move_precompile_verification_to_svm ) ||
1108+
!is_precompile ) ) {
1109+
instr_exec_result = native_prog_fn( ctx );
1110+
} else {
1111+
/* The precompile was already executed at the transaction level, return success */
1112+
instr_exec_result = FD_EXECUTOR_INSTR_SUCCESS;
1113+
}
10891114
} else {
1115+
/* Unknown program */
10901116
instr_exec_result = FD_EXECUTOR_INSTR_ERR_UNSUPPORTED_PROGRAM_ID;
10911117
}
10921118

@@ -1257,6 +1283,9 @@ fd_executor_setup_accounts_for_txn( fd_exec_txn_ctx_t * txn_ctx ) {
12571283

12581284
txn_ctx->nonce_account_idx_in_txn = ULONG_MAX;
12591285
txn_ctx->executable_cnt = j;
1286+
1287+
/* Set up instr infos from the txn descriptor. No Agave equivalent to this function. */
1288+
fd_executor_setup_instr_infos_from_txn_instrs( txn_ctx );
12601289
}
12611290

12621291
/* Stuff to be done before multithreading can begin */

src/flamenco/runtime/fd_executor.h

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,15 +41,6 @@ get_transaction_account_lock_limit( fd_exec_txn_ctx_t const * txn_ctx ) {
4141

4242
typedef int (* fd_exec_instr_fn_t)( fd_exec_instr_ctx_t * ctx );
4343

44-
/* fd_executor_lookup_native_program returns the appropriate instruction
45-
processor for the given native program ID. Returns NULL if given ID
46-
is not a recognized native program. */
47-
48-
int
49-
fd_executor_lookup_native_program( fd_txn_account_t const * prog_acc,
50-
fd_exec_txn_ctx_t * txn_ctx,
51-
fd_exec_instr_fn_t * native_prog_fn );
52-
5344
fd_exec_instr_fn_t
5445
fd_executor_lookup_native_precompile_program( fd_txn_account_t const * prog_acc );
5546

src/flamenco/runtime/fd_runtime.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1749,6 +1749,7 @@ fd_runtime_pre_execute_check( fd_execute_txn_task_info_t * task_info,
17491749
general transaction execution
17501750
17511751
*/
1752+
17521753
if( !FD_FEATURE_ACTIVE_( txn_ctx->slot, txn_ctx->features, move_precompile_verification_to_svm ) ) {
17531754
err = fd_executor_verify_precompiles( txn_ctx );
17541755
if( FD_UNLIKELY( err!=FD_RUNTIME_EXECUTE_SUCCESS ) ) {

0 commit comments

Comments
 (0)