Line data Source code
1 : #include "fd_account.h"
2 : #include "context/fd_exec_instr_ctx.h"
3 :
4 : /* https://github.com/anza-xyz/agave/blob/89872fdb074e6658646b2b57a299984f0059cc84/sdk/transaction-context/src/lib.rs#L789-L815 */
5 : /* Assignes the owner of this account (transaction wide) */
6 : int
7 : fd_account_set_owner( fd_exec_instr_ctx_t const * ctx,
8 : ulong instr_acc_idx,
9 2220 : fd_pubkey_t const * owner ) {
10 :
11 2220 : fd_instr_info_t const * instr = ctx->instr;
12 2220 : fd_borrowed_account_t * account = NULL;
13 2220 : do {
14 2220 : int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
15 2220 : if( FD_UNLIKELY( err ) ) {
16 0 : FD_LOG_ERR(( "fd_instr_borrowed_account_view_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
17 0 : }
18 2220 : } while(0);
19 :
20 2220 : fd_account_meta_t const * meta = account->const_meta;
21 :
22 : /* Only the owner can assign a new owner */
23 2220 : if( FD_UNLIKELY( !fd_account_is_owned_by_current_program( instr, meta ) ) ) {
24 204 : return FD_EXECUTOR_INSTR_ERR_MODIFIED_PROGRAM_ID;
25 204 : }
26 : /* And only if the account is writable */
27 2016 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( instr, instr_acc_idx ) ) ) {
28 48 : return FD_EXECUTOR_INSTR_ERR_MODIFIED_PROGRAM_ID;
29 48 : }
30 : /* And only if the account is not executable */
31 1968 : if( FD_UNLIKELY( fd_account_is_executable_internal( ctx->slot_ctx, meta ) ) ) {
32 24 : return FD_EXECUTOR_INSTR_ERR_MODIFIED_PROGRAM_ID;
33 24 : }
34 : /* And only if the data is zero-initialized or empty */
35 1944 : if( FD_UNLIKELY( !fd_account_is_zeroed( meta ) ) ) {
36 57 : return FD_EXECUTOR_INSTR_ERR_MODIFIED_PROGRAM_ID;
37 57 : }
38 : /* Don't copy the account if the owner does not change */
39 1887 : if( !memcmp( account->const_meta->info.owner, owner, sizeof( fd_pubkey_t ) ) ) {
40 0 : return FD_EXECUTOR_INSTR_SUCCESS;
41 0 : }
42 :
43 1887 : do {
44 1887 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
45 1887 : if( FD_UNLIKELY( err ) ) {
46 0 : FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
47 0 : }
48 1887 : } while(0);
49 :
50 : /* Agave self.touch() is a no-op */
51 :
52 1887 : memcpy( account->meta->info.owner, owner, sizeof(fd_pubkey_t) );
53 1887 : return FD_EXECUTOR_INSTR_SUCCESS;
54 1887 : }
55 :
56 : /* https://github.com/anza-xyz/agave/blob/89872fdb074e6658646b2b57a299984f0059cc84/sdk/transaction-context/src/lib.rs#L823-L845 */
57 : /* Overwrites the number of lamports of this account (transaction wide) */
58 : int
59 : fd_account_set_lamports( fd_exec_instr_ctx_t const * ctx,
60 : ulong instr_acc_idx,
61 7770 : ulong lamports ) {
62 :
63 7770 : fd_borrowed_account_t * account = NULL;
64 7770 : do {
65 7770 : int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
66 7770 : if( FD_UNLIKELY( err ) ) {
67 0 : FD_LOG_ERR(( "fd_instr_borrowed_account_view_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
68 0 : }
69 7770 : } while(0);
70 :
71 : /* An account not owned by the program cannot have its blanace decrease */
72 7770 : if( FD_UNLIKELY( ( !fd_account_is_owned_by_current_program( ctx->instr, account->const_meta ) ) &&
73 7770 : ( lamports < account->const_meta->info.lamports ) ) ) {
74 48 : return FD_EXECUTOR_INSTR_ERR_EXTERNAL_ACCOUNT_LAMPORT_SPEND;
75 48 : }
76 :
77 : /* The balance of read-only may not change */
78 7722 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( ctx->instr, instr_acc_idx ) ) ) {
79 201 : return FD_EXECUTOR_INSTR_ERR_READONLY_LAMPORT_CHANGE;
80 201 : }
81 :
82 : /* The balance of executable accounts may not change */
83 7521 : if( FD_UNLIKELY( fd_account_is_executable_internal( ctx->slot_ctx, account->const_meta ) ) ) {
84 90 : return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_LAMPORT_CHANGE;
85 90 : }
86 :
87 : /* Don't copy the account if the lamports do not change */
88 7431 : if( lamports==account->const_meta->info.lamports ) {
89 690 : return FD_EXECUTOR_INSTR_SUCCESS;
90 690 : }
91 :
92 6741 : do {
93 6741 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
94 6741 : if( FD_UNLIKELY( err ) ) {
95 0 : FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
96 0 : }
97 6741 : } while(0);
98 :
99 : /* Agave self.touch() is a no-op */
100 :
101 6741 : account->meta->info.lamports = lamports;
102 6741 : return FD_EXECUTOR_INSTR_SUCCESS;
103 6741 : }
104 :
105 : int
106 : fd_account_get_data_mut( fd_exec_instr_ctx_t const * ctx,
107 : ulong instr_acc_idx,
108 : uchar * * data_out,
109 3759 : ulong * dlen_out ) {
110 :
111 3759 : int err;
112 3759 : if( FD_UNLIKELY( !fd_account_can_data_be_changed( ctx, instr_acc_idx, &err ) ) ) {
113 273 : return err;
114 273 : }
115 :
116 3486 : fd_borrowed_account_t * account = NULL;
117 3486 : do {
118 3486 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
119 3486 : if( FD_UNLIKELY( err ) ) {
120 0 : FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
121 0 : }
122 3486 : } while(0);
123 :
124 : /* Agave self.touch() is a no-op */
125 :
126 3486 : if (NULL != data_out)
127 3486 : *data_out = account->data;
128 3486 : if (NULL != dlen_out)
129 3486 : *dlen_out = account->meta->dlen;
130 :
131 3486 : return FD_EXECUTOR_INSTR_SUCCESS;
132 3486 : }
133 :
134 : int
135 : fd_account_set_data_from_slice( fd_exec_instr_ctx_t const * ctx,
136 : ulong instr_acc_idx,
137 : uchar const * data,
138 774 : ulong data_sz ) {
139 :
140 774 : fd_borrowed_account_t * account = NULL;
141 774 : do {
142 774 : int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
143 774 : if( FD_UNLIKELY( err ) ) {
144 0 : FD_LOG_ERR(( "fd_instr_borrowed_account_view_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
145 0 : }
146 774 : } while(0);
147 :
148 774 : int err;
149 774 : if( FD_UNLIKELY( !fd_account_can_data_be_resized( ctx, account->const_meta, data_sz, &err ) ) ) {
150 0 : return err;
151 0 : }
152 :
153 774 : if( FD_UNLIKELY( !fd_account_can_data_be_changed( ctx, instr_acc_idx, &err ) ) ) {
154 3 : return err;
155 3 : }
156 :
157 : /* Agave self.touch() is a no-op */
158 :
159 771 : if( FD_UNLIKELY( !fd_account_update_accounts_resize_delta( ctx, instr_acc_idx, data_sz, &err ) ) ) {
160 0 : return err;
161 0 : }
162 :
163 771 : do {
164 771 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, data_sz, &account );
165 771 : if( FD_UNLIKELY( err ) ) {
166 0 : FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
167 0 : }
168 771 : } while(0);
169 :
170 : /* AccountSharedData::set_data_from_slice() */
171 771 : account->meta->dlen = data_sz;
172 771 : fd_memcpy( account->data, data, data_sz );
173 :
174 771 : return FD_EXECUTOR_INSTR_SUCCESS;
175 771 : }
176 :
177 : int
178 : fd_account_set_data_length( fd_exec_instr_ctx_t const * ctx,
179 : ulong instr_acc_idx,
180 2913 : ulong new_len ) {
181 :
182 2913 : fd_borrowed_account_t * account = NULL;
183 2913 : do {
184 2913 : int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
185 2913 : if( FD_UNLIKELY( err ) ) {
186 0 : FD_LOG_ERR(( "fd_instr_borrowed_account_view_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
187 0 : }
188 2913 : } while(0);
189 :
190 2913 : int err = FD_EXECUTOR_INSTR_SUCCESS;
191 2913 : if( FD_UNLIKELY( !fd_account_can_data_be_resized( ctx, account->const_meta, new_len, &err ) ) ) {
192 3 : return err;
193 3 : }
194 :
195 2910 : if( FD_UNLIKELY( !fd_account_can_data_be_changed( ctx, instr_acc_idx, &err ) ) ) {
196 315 : return err;
197 315 : }
198 :
199 2595 : ulong old_len = account->const_meta->dlen;
200 :
201 : /* Don't copy the account if the length does not change */
202 2595 : if( old_len==new_len ) {
203 45 : return FD_EXECUTOR_INSTR_SUCCESS;
204 45 : }
205 :
206 : /* Agave self.touch() is a no-op */
207 :
208 2550 : if( FD_UNLIKELY( !fd_account_update_accounts_resize_delta( ctx, instr_acc_idx, new_len, &err ) ) ) {
209 0 : return err;
210 0 : }
211 :
212 2550 : do {
213 2550 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, new_len, &account );
214 2550 : if( FD_UNLIKELY( err ) ) {
215 0 : FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
216 0 : }
217 2550 : } while(0);
218 :
219 2550 : if( new_len>old_len ) {
220 2493 : fd_memset( account->data+old_len, 0, new_len-old_len );
221 2493 : }
222 :
223 2550 : account->meta->dlen = new_len;
224 2550 : return FD_EXECUTOR_INSTR_SUCCESS;
225 2550 : }
226 :
227 : int
228 : fd_account_set_executable( fd_exec_instr_ctx_t const * ctx,
229 : ulong instr_acc_idx,
230 0 : int is_executable ) {
231 :
232 0 : fd_instr_info_t const * instr = ctx->instr;
233 :
234 0 : fd_borrowed_account_t * account = NULL;
235 0 : do {
236 0 : int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
237 0 : if( FD_UNLIKELY( err ) ) {
238 0 : FD_LOG_ERR(( "fd_instr_borrowed_account_view_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
239 0 : }
240 0 : } while(0);
241 :
242 0 : fd_account_meta_t const * meta = account->const_meta;
243 :
244 : /* To become executable an account must be rent exempt */
245 0 : fd_epoch_bank_t const * epoch_bank = fd_exec_epoch_ctx_epoch_bank_const( ctx->epoch_ctx );
246 0 : fd_rent_t const * rent = &epoch_bank->rent;
247 0 : if( FD_UNLIKELY( !fd_rent_exempt_minimum_balance( rent, meta->dlen ) ) ) {
248 0 : return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_ACCOUNT_NOT_RENT_EXEMPT;
249 0 : }
250 :
251 : /* Only the owner can set the exectuable flag */
252 0 : if( FD_UNLIKELY( !fd_account_is_owned_by_current_program( instr, meta ) ) ) {
253 0 : return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_MODIFIED;
254 0 : }
255 :
256 : /* And only if the account is writable */
257 0 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( instr, instr_acc_idx ) ) ) {
258 0 : return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_MODIFIED;
259 0 : }
260 :
261 : /* One can not clear the executable flag */
262 0 : if( FD_UNLIKELY( fd_account_is_executable_internal( ctx->slot_ctx, meta ) && !is_executable ) ) {
263 0 : return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_MODIFIED;
264 0 : }
265 :
266 : /* Don't copy the account if the exectuable flag does not change */
267 0 : if( fd_account_is_executable( meta ) == is_executable ) {
268 0 : return FD_EXECUTOR_INSTR_SUCCESS;
269 0 : }
270 :
271 0 : do {
272 0 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
273 0 : if( FD_UNLIKELY( err ) ) {
274 0 : FD_LOG_ERR(( "fd_instr_borrowed_account_modify_idx failed (%d-%s)", err, fd_acc_mgr_strerror( err ) ));
275 0 : }
276 0 : } while(0);
277 :
278 : /* Agave self.touch() is a no-op */
279 :
280 0 : account->meta->info.executable = !!is_executable;
281 :
282 0 : return FD_EXECUTOR_INSTR_SUCCESS;
283 0 : }
284 :
285 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/sdk/src/transaction_context.rs#L1128-L1138 */
286 : int
287 : fd_account_update_accounts_resize_delta( fd_exec_instr_ctx_t const * ctx,
288 : ulong instr_acc_idx,
289 : ulong new_len,
290 3321 : int * err ) {
291 :
292 3321 : fd_borrowed_account_t * account = NULL;
293 3321 : *err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
294 3321 : if( FD_UNLIKELY( *err ) ) {
295 0 : return 0;
296 0 : }
297 :
298 3321 : ulong size_delta = fd_ulong_sat_sub( new_len, account->const_meta->dlen );
299 :
300 : /* TODO: The size delta should never exceed the value of ULONG_MAX so this
301 : could be replaced with a normal addition. However to match execution with
302 : the agave client, this is being left as a sat add */
303 3321 : ctx->txn_ctx->accounts_resize_delta = fd_ulong_sat_add( ctx->txn_ctx->accounts_resize_delta, size_delta );
304 3321 : *err = FD_EXECUTOR_INSTR_SUCCESS;
305 3321 : return 1;
306 3321 : }
|