Skip to content

Commit 11274c7

Browse files
flamenco: refactor txn_ctx/instr_ctx account getters API
1 parent e5440af commit 11274c7

20 files changed

+364
-258
lines changed

Diff for: src/flamenco/runtime/context/fd_exec_instr_ctx.c

+57-23
Original file line numberDiff line numberDiff line change
@@ -81,54 +81,88 @@ fd_exec_instr_ctx_delete( void * mem ) {
8181
return mem;
8282
}
8383

84+
int
85+
fd_exec_instr_ctx_find_idx_of_instr_account( fd_exec_instr_ctx_t const * ctx,
86+
fd_pubkey_t const * pubkey ) {
87+
for( int i = 0; i < ctx->instr->acct_cnt; i++ ) {
88+
if( memcmp( pubkey->uc, ctx->instr->acct_pubkeys[i].uc, sizeof(fd_pubkey_t) )==0 ) {
89+
return i;
90+
}
91+
}
92+
return -1;
93+
}
94+
8495
int
8596
fd_exec_instr_ctx_try_borrow_account( fd_exec_instr_ctx_t const * ctx,
8697
ulong idx,
98+
fd_txn_account_t * txn_account,
8799
fd_borrowed_account_t * account ) {
88100
/* TODO this is slightly wrong. Agave returns NotEnoughAccountKeys when the account index is
89101
out of bounds in the transaction context and MissingAccount when the account index is out of
90102
bounds in the instruction context. */
91103

92-
/* Return a NotEnoughAccountKeys error if the idx is out of bounds.
93-
https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L603 */
94-
if( FD_UNLIKELY( idx >= ctx->instr->acct_cnt ) ) {
95-
return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
96-
}
97-
98-
fd_txn_account_t * instr_account = ctx->instr->accounts[idx];
99-
100104
/* Return an AccountBorrowFailed error if the write is not acquirable.
101105
https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L605 */
102-
int acquire_result = fd_txn_account_acquire_write( instr_account );
106+
int acquire_result = fd_txn_account_acquire_write( txn_account );
103107
if( FD_UNLIKELY( !acquire_result ) ) {
104108
return FD_EXECUTOR_INSTR_ERR_ACC_BORROW_FAILED;
105109
}
106110

107111
/* Create a BorrowedAccount upon success.
108112
https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L606 */
109-
fd_borrowed_account_init( account, instr_account, ctx, (int)idx );
113+
fd_borrowed_account_init( account, txn_account, ctx, (ushort)idx );
110114
return FD_EXECUTOR_INSTR_SUCCESS;
111115
}
112116

113117
int
114-
fd_exec_instr_ctx_try_borrow_account_with_key( fd_exec_instr_ctx_t * ctx,
115-
fd_pubkey_t const * pubkey,
116-
fd_borrowed_account_t * account ) {
117-
for( ulong i = 0; i < ctx->instr->acct_cnt; i++ ) {
118-
if( memcmp( pubkey->uc, ctx->instr->acct_pubkeys[i].uc, sizeof(fd_pubkey_t) )==0 ) {
119-
return fd_exec_instr_ctx_try_borrow_account( ctx, i, account );
120-
}
118+
fd_exec_instr_ctx_try_borrow_instr_account( fd_exec_instr_ctx_t const * ctx,
119+
ulong idx,
120+
fd_borrowed_account_t * account ) {
121+
/* Return a NotEnoughAccountKeys error if the idx is out of bounds.
122+
https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L603 */
123+
if( FD_UNLIKELY( idx >= ctx->instr->acct_cnt ) ) {
124+
return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
121125
}
122-
return FD_EXECUTOR_INSTR_ERR_MISSING_ACC;
126+
127+
fd_txn_account_t * instr_account = ctx->instr->accounts[idx];
128+
129+
/* Calculate the index_in_instruction by adding the number of program accounts to the instruction account index.
130+
https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L654-L655 */
131+
ulong idx_in_instr = fd_ulong_sat_add(idx, fd_exec_instr_ctx_get_number_of_program_accounts( ctx ) );
132+
133+
return fd_exec_instr_ctx_try_borrow_account( ctx,
134+
idx_in_instr,
135+
instr_account,
136+
account );
123137
}
124138

125139
int
126-
fd_exec_instr_ctx_find_idx_of_instr_account( fd_exec_instr_ctx_t const * ctx,
127-
fd_pubkey_t const * pubkey ) {
128-
for( int i = 0; i < ctx->instr->acct_cnt; i++ ) {
140+
fd_exec_instr_ctx_try_borrow_instr_account_with_key( fd_exec_instr_ctx_t const * ctx,
141+
fd_pubkey_t const * pubkey,
142+
fd_borrowed_account_t * account ) {
143+
for( ulong i = 0; i < ctx->instr->acct_cnt; i++ ) {
129144
if( memcmp( pubkey->uc, ctx->instr->acct_pubkeys[i].uc, sizeof(fd_pubkey_t) )==0 ) {
130-
return i;
145+
return fd_exec_instr_ctx_try_borrow_instr_account( ctx, i, account );
131146
}
132147
}
133-
return -1;
148+
149+
/* Return a NotEnoughAccountKeys error if the account is not found
150+
in the instruction context to match the error code returned by
151+
fd_exec_instr_ctx_try_borrow_instr_account. */
152+
return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
153+
}
154+
155+
int
156+
fd_exec_instr_ctx_try_borrow_last_program_account( fd_exec_instr_ctx_t const * ctx,
157+
fd_borrowed_account_t * account ) {
158+
fd_txn_account_t * program_account = NULL;
159+
fd_exec_txn_ctx_get_account_at_index( ctx->txn_ctx,
160+
ctx->instr->program_id,
161+
&program_account,
162+
NULL );
163+
164+
return fd_exec_instr_ctx_try_borrow_account( ctx,
165+
0UL,
166+
program_account,
167+
account );
134168
}

Diff for: src/flamenco/runtime/context/fd_exec_instr_ctx.h

+38-15
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ struct __attribute__((aligned(8UL))) fd_exec_instr_ctx {
4141
/* Be careful when using this macro. There may be places where the error
4242
will need to be handled differently. */
4343
#define FD_TRY_BORROW_INSTR_ACCOUNT_DEFAULT_ERR_CHECK( ctx, idx, acc ) do { \
44-
int err = fd_exec_instr_ctx_try_borrow_account( ctx, idx, acc ); \
44+
int err = fd_exec_instr_ctx_try_borrow_instr_account( ctx, idx, acc ); \
4545
if( FD_UNLIKELY( err ) ) return err; \
4646
} while (0)
4747

@@ -71,45 +71,68 @@ fd_exec_instr_ctx_delete( void * mem );
7171
7272
https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L490 */
7373
static inline int
74-
fd_exec_instr_ctx_check_num_insn_accounts( fd_exec_instr_ctx_t * ctx,
75-
uint expected_accounts ) {
74+
fd_exec_instr_ctx_check_num_insn_accounts( fd_exec_instr_ctx_t const * ctx,
75+
uint expected_accounts ) {
7676

7777
if( FD_UNLIKELY( ctx->instr->acct_cnt<expected_accounts ) ) {
7878
return FD_EXECUTOR_INSTR_ERR_NOT_ENOUGH_ACC_KEYS;
7979
}
8080
return FD_EXECUTOR_INSTR_SUCCESS;
8181
}
8282

83+
/* Mirrors Agave function solana_sdk::transaction_context::InstructionContext::find_index_of_instruction_account.
84+
85+
Returns the index of the the instruction account given the account pubkey
86+
or -1 if the account is not found.
87+
88+
https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L524-L538 */
89+
90+
int
91+
fd_exec_instr_ctx_find_idx_of_instr_account( fd_exec_instr_ctx_t const * ctx,
92+
fd_pubkey_t const * pubkey );
93+
94+
/* Mirrors Agave function solana_sdk::transaction_context::InstructionContext::get_number_of_program_accounts.
95+
96+
Strictly returns 1, as we only support one program account per instruction for now.
97+
98+
https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L480-L482 */
99+
100+
static inline ushort
101+
fd_exec_instr_ctx_get_number_of_program_accounts( fd_exec_instr_ctx_t const * ctx ) {
102+
(void) ctx;
103+
return 1U;
104+
}
105+
83106
/* Mirrors Agave function solana_sdk::transaction_context::InstructionContext::try_borrow_account.
84107
85108
Borrows an account from the instruction context with a given account index.
86109
87110
https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L594 */
88111

89112
int
90-
fd_exec_instr_ctx_try_borrow_account( fd_exec_instr_ctx_t const * ctx,
91-
ulong idx,
92-
fd_borrowed_account_t * account );
113+
fd_exec_instr_ctx_try_borrow_instr_account( fd_exec_instr_ctx_t const * ctx,
114+
ulong idx,
115+
fd_borrowed_account_t * account );
93116

94117
/* A wrapper around fd_exec_instr_ctx_try_borrow_account that accepts an account pubkey.
95118
96119
Borrows an account from the instruction context with a given pubkey. */
97120

98121
int
99-
fd_exec_instr_ctx_try_borrow_account_with_key( fd_exec_instr_ctx_t * ctx,
100-
fd_pubkey_t const * pubkey,
101-
fd_borrowed_account_t * account );
122+
fd_exec_instr_ctx_try_borrow_instr_account_with_key( fd_exec_instr_ctx_t const * ctx,
123+
fd_pubkey_t const * pubkey,
124+
fd_borrowed_account_t * account );
102125

103-
/* Mirrors Agave function solana_sdk::transaction_context::InstructionContext::find_index_of_instruction_account.
126+
/* Mirrors Agave function solana_sdk::transaction_context::InstructionContext::try_borrow_last_program_account
104127
105-
Returns the index of the the instruction account given the account pubkey
106-
or -1 if the account is not found.
128+
Borrows the instruction's program account. Since there is only one program account per
129+
instruction, this function simply borrows the instruction's only program account, despite the name.
107130
108-
https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L524-L538 */
131+
https://github.com/anza-xyz/agave/blob/v2.1.14/sdk/src/transaction_context.rs#L616 */
109132

110133
int
111-
fd_exec_instr_ctx_find_idx_of_instr_account( fd_exec_instr_ctx_t const * ctx,
112-
fd_pubkey_t const * pubkey );
134+
fd_exec_instr_ctx_try_borrow_last_program_account( fd_exec_instr_ctx_t const * ctx,
135+
fd_borrowed_account_t * account );
113136

114137
FD_PROTOTYPES_END
115138

Diff for: src/flamenco/runtime/context/fd_exec_txn_ctx.c

+55-83
Original file line numberDiff line numberDiff line change
@@ -84,121 +84,66 @@ fd_exec_txn_ctx_delete( void * mem ) {
8484
}
8585

8686
int
87-
fd_exec_txn_ctx_get_account_view_idx( fd_exec_txn_ctx_t * ctx,
88-
uchar idx,
89-
fd_txn_account_t * * account ) {
87+
fd_exec_txn_ctx_get_account_at_index( fd_exec_txn_ctx_t * ctx,
88+
uchar idx,
89+
fd_txn_account_t * * account,
90+
fd_txn_account_condition_fn_t * condition ) {
9091
if( FD_UNLIKELY( idx>=ctx->accounts_cnt ) ) {
9192
return FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
9293
}
9394

9495
fd_txn_account_t * txn_account = &ctx->accounts[idx];
9596
*account = txn_account;
9697

97-
if( FD_UNLIKELY( !fd_acc_exists( txn_account->const_meta ) ) ) {
98-
return FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
98+
if( condition != NULL ) {
99+
if( FD_UNLIKELY( !condition( ctx, idx, *account ) ) )
100+
return FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
99101
}
100102

101103
return FD_ACC_MGR_SUCCESS;
102104
}
103105

104106
int
105-
fd_exec_txn_ctx_get_account_view( fd_exec_txn_ctx_t * ctx,
106-
fd_pubkey_t const * pubkey,
107-
fd_txn_account_t * * account ) {
108-
for( ulong i = 0; i < ctx->accounts_cnt; i++ ) {
109-
if( memcmp( pubkey->uc, ctx->account_keys[i].uc, sizeof(fd_pubkey_t) )==0 ) {
110-
// TODO: check if readable???
111-
fd_txn_account_t * txn_account = &ctx->accounts[i];
112-
*account = txn_account;
113-
114-
if( FD_UNLIKELY( !fd_acc_exists( txn_account->const_meta ) ) ) {
115-
return FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
116-
}
117-
118-
return FD_ACC_MGR_SUCCESS;
119-
}
107+
fd_exec_txn_ctx_get_account_with_key( fd_exec_txn_ctx_t * ctx,
108+
fd_pubkey_t const * pubkey,
109+
fd_txn_account_t * * account,
110+
fd_txn_account_condition_fn_t * condition ) {
111+
int index = fd_exec_txn_ctx_find_index_of_account( ctx, pubkey );
112+
if( FD_UNLIKELY( index==-1 ) ) {
113+
return FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
120114
}
121115

122-
return FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
116+
return fd_exec_txn_ctx_get_account_at_index( ctx,
117+
(uchar)index,
118+
account,
119+
condition );
123120
}
124121

125122
int
126-
fd_exec_txn_ctx_get_account_executable_view( fd_exec_txn_ctx_t * ctx,
127-
fd_pubkey_t const * pubkey,
128-
fd_txn_account_t * * account ) {
123+
fd_exec_txn_ctx_get_executable_account( fd_exec_txn_ctx_t * ctx,
124+
fd_pubkey_t const * pubkey,
125+
fd_txn_account_t * * account,
126+
fd_txn_account_condition_fn_t * condition ) {
129127
/* First try to fetch the executable account from the existing borrowed accounts.
130128
If the pubkey is in the account keys, then we want to re-use that
131129
borrowed account since it reflects changes from prior instructions. Referencing the
132130
read-only executable accounts list is incorrect behavior when the program
133131
data account is written to in a prior instruction (e.g. program upgrade + invoke within the same txn) */
134-
int err = fd_exec_txn_ctx_get_account_view( ctx, pubkey, account );
132+
int err = fd_exec_txn_ctx_get_account_with_key( ctx, pubkey, account, condition );
135133
if( FD_UNLIKELY( err==FD_ACC_MGR_SUCCESS ) ) {
136134
return FD_ACC_MGR_SUCCESS;
137135
}
138136

139137
for( ulong i = 0; i < ctx->executable_cnt; i++ ) {
140138
if( memcmp( pubkey->uc, ctx->executable_accounts[i].pubkey->uc, sizeof(fd_pubkey_t) )==0 ) {
141-
// TODO: check if readable???
142139
fd_txn_account_t * txn_account = &ctx->executable_accounts[i];
143140
*account = txn_account;
144141

145-
if( FD_UNLIKELY( !fd_acc_exists( txn_account->const_meta ) ) )
146-
return FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
147-
148-
return FD_ACC_MGR_SUCCESS;
149-
}
150-
}
151-
152-
return FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
153-
}
154-
155-
int
156-
fd_exec_txn_ctx_get_account_modify_fee_payer( fd_exec_txn_ctx_t * ctx,
157-
fd_txn_account_t * * account ) {
158-
159-
*account = &ctx->accounts[ FD_FEE_PAYER_TXN_IDX ];
160-
161-
if( FD_UNLIKELY( !fd_txn_is_writable( ctx->txn_descriptor, FD_FEE_PAYER_TXN_IDX ) ) ) {
162-
return FD_ACC_MGR_ERR_WRITE_FAILED;
163-
}
164-
return FD_ACC_MGR_SUCCESS;
165-
}
166-
167-
int
168-
fd_exec_txn_ctx_get_account_modify_idx( fd_exec_txn_ctx_t * ctx,
169-
uchar idx,
170-
ulong min_data_sz,
171-
fd_txn_account_t * * account ) {
172-
if( idx >= ctx->accounts_cnt ) {
173-
return FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
174-
}
175-
176-
fd_txn_account_t * txn_account = &ctx->accounts[idx];
177-
if( FD_UNLIKELY( !fd_txn_account_is_writable_idx( ctx, (int)idx ) ) ) {
178-
return FD_ACC_MGR_ERR_WRITE_FAILED;
179-
}
180-
181-
if( min_data_sz > txn_account->const_meta->dlen ) {
182-
fd_txn_account_resize( txn_account, min_data_sz );
183-
}
184-
185-
*account = txn_account;
186-
return FD_ACC_MGR_SUCCESS;
187-
}
188-
189-
int
190-
fd_exec_txn_ctx_get_account_modify( fd_exec_txn_ctx_t * ctx,
191-
fd_pubkey_t const * pubkey,
192-
ulong min_data_sz,
193-
fd_txn_account_t * * account ) {
194-
for( ulong i = 0; i < ctx->accounts_cnt; i++ ) {
195-
if( memcmp( pubkey->uc, ctx->account_keys[i].uc, sizeof(fd_pubkey_t) )==0 ) {
196-
// TODO: check if writable???
197-
fd_txn_account_t * txn_account = &ctx->accounts[i];
198-
if( min_data_sz > txn_account->const_meta->dlen ) {
199-
fd_txn_account_resize( txn_account, min_data_sz );
142+
if( condition != NULL ) {
143+
if( FD_UNLIKELY( !condition( ctx, (int)i, *account ) ) )
144+
return FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
200145
}
201-
*account = txn_account;
146+
202147
return FD_ACC_MGR_SUCCESS;
203148
}
204149
}
@@ -336,7 +281,7 @@ fd_txn_account_is_demotion( fd_exec_txn_ctx_t const * txn_ctx, int idx )
336281
337282
https://github.com/anza-xyz/agave/blob/v2.1.11/sdk/program/src/message/sanitized.rs#L38-L47 */
338283
int
339-
fd_txn_account_is_writable_idx( fd_exec_txn_ctx_t const * txn_ctx, int idx ) {
284+
fd_exec_txn_ctx_account_is_writable_idx( fd_exec_txn_ctx_t const * txn_ctx, int idx ) {
340285

341286
/* https://github.com/anza-xyz/agave/blob/v2.1.11/sdk/program/src/message/sanitized.rs#L43 */
342287
if( !fd_txn_is_writable( txn_ctx->txn_descriptor, idx ) ) {
@@ -360,3 +305,30 @@ fd_txn_account_is_writable_idx( fd_exec_txn_ctx_t const * txn_ctx, int idx ) {
360305

361306
return 1;
362307
}
308+
309+
/* Account pre-condition filtering functions */
310+
311+
int
312+
fd_txn_account_exists( fd_exec_txn_ctx_t const * ctx,
313+
int idx,
314+
fd_txn_account_t * acc ) {
315+
(void) ctx;
316+
(void) idx;
317+
return fd_acc_exists( acc->const_meta );
318+
}
319+
320+
int
321+
fd_txn_account_is_writable( fd_exec_txn_ctx_t const * ctx,
322+
int idx,
323+
fd_txn_account_t * acc ) {
324+
(void) acc;
325+
return fd_exec_txn_ctx_account_is_writable_idx( ctx, idx );
326+
}
327+
328+
int
329+
fd_txn_account_fee_payer_writable( fd_exec_txn_ctx_t const * ctx,
330+
int idx,
331+
fd_txn_account_t * acc ) {
332+
(void) acc;
333+
return fd_txn_is_writable( ctx->txn_descriptor, idx );
334+
}

0 commit comments

Comments
 (0)