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 0 : fd_pubkey_t const * owner ) {
10 :
11 0 : fd_instr_info_t const * instr = ctx->instr;
12 0 : fd_borrowed_account_t * account = NULL;
13 0 : do {
14 0 : int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
15 0 : 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 0 : } while(0);
19 :
20 0 : fd_account_meta_t const * meta = account->const_meta;
21 :
22 : /* Only the owner can assign a new owner */
23 0 : if( FD_UNLIKELY( !fd_account_is_owned_by_current_program( instr, meta ) ) ) {
24 0 : return FD_EXECUTOR_INSTR_ERR_MODIFIED_PROGRAM_ID;
25 0 : }
26 : /* And only if the account is writable */
27 0 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( instr, instr_acc_idx ) ) ) {
28 0 : return FD_EXECUTOR_INSTR_ERR_MODIFIED_PROGRAM_ID;
29 0 : }
30 : /* And only if the account is not executable */
31 0 : if( FD_UNLIKELY( fd_account_is_executable_internal( ctx->slot_ctx, meta ) ) ) {
32 0 : return FD_EXECUTOR_INSTR_ERR_MODIFIED_PROGRAM_ID;
33 0 : }
34 : /* And only if the data is zero-initialized or empty */
35 0 : if( FD_UNLIKELY( !fd_account_is_zeroed( meta ) ) ) {
36 0 : return FD_EXECUTOR_INSTR_ERR_MODIFIED_PROGRAM_ID;
37 0 : }
38 : /* Don't copy the account if the owner does not change */
39 0 : if( !memcmp( account->const_meta->info.owner, owner, sizeof( fd_pubkey_t ) ) ) {
40 0 : return FD_EXECUTOR_INSTR_SUCCESS;
41 0 : }
42 :
43 0 : do {
44 0 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
45 0 : 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 0 : } while(0);
49 :
50 : /* Agave self.touch() is a no-op */
51 :
52 0 : memcpy( account->meta->info.owner, owner, sizeof(fd_pubkey_t) );
53 0 : return FD_EXECUTOR_INSTR_SUCCESS;
54 0 : }
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 0 : ulong lamports ) {
62 :
63 0 : fd_borrowed_account_t * account = NULL;
64 0 : do {
65 0 : int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
66 0 : 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 0 : } while(0);
70 :
71 : /* An account not owned by the program cannot have its blanace decrease */
72 0 : if( FD_UNLIKELY( ( !fd_account_is_owned_by_current_program( ctx->instr, account->const_meta ) ) &&
73 0 : ( lamports < account->const_meta->info.lamports ) ) ) {
74 0 : return FD_EXECUTOR_INSTR_ERR_EXTERNAL_ACCOUNT_LAMPORT_SPEND;
75 0 : }
76 :
77 : /* The balance of read-only may not change */
78 0 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( ctx->instr, instr_acc_idx ) ) ) {
79 0 : return FD_EXECUTOR_INSTR_ERR_READONLY_LAMPORT_CHANGE;
80 0 : }
81 :
82 : /* The balance of executable accounts may not change */
83 0 : if( FD_UNLIKELY( fd_account_is_executable_internal( ctx->slot_ctx, account->const_meta ) ) ) {
84 0 : return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_LAMPORT_CHANGE;
85 0 : }
86 :
87 : /* Don't copy the account if the lamports do not change */
88 0 : if( lamports==account->const_meta->info.lamports ) {
89 0 : return FD_EXECUTOR_INSTR_SUCCESS;
90 0 : }
91 :
92 0 : do {
93 0 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
94 0 : 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 0 : } while(0);
98 :
99 : /* Agave self.touch() is a no-op */
100 :
101 0 : account->meta->info.lamports = lamports;
102 0 : return FD_EXECUTOR_INSTR_SUCCESS;
103 0 : }
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 0 : ulong * dlen_out ) {
110 :
111 0 : int err;
112 0 : if( FD_UNLIKELY( !fd_account_can_data_be_changed( ctx, instr_acc_idx, &err ) ) ) {
113 0 : return err;
114 0 : }
115 :
116 0 : fd_borrowed_account_t * account = NULL;
117 0 : do {
118 0 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
119 0 : 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 0 : } while(0);
123 :
124 : /* Agave self.touch() is a no-op */
125 :
126 0 : if (NULL != data_out)
127 0 : *data_out = account->data;
128 0 : if (NULL != dlen_out)
129 0 : *dlen_out = account->meta->dlen;
130 :
131 0 : return FD_EXECUTOR_INSTR_SUCCESS;
132 0 : }
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 0 : ulong data_sz ) {
139 :
140 0 : fd_borrowed_account_t * account = NULL;
141 0 : do {
142 0 : int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
143 0 : 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 0 : } while(0);
147 :
148 0 : int err;
149 0 : if( FD_UNLIKELY( !fd_account_can_data_be_resized( ctx, account->const_meta, data_sz, &err ) ) ) {
150 0 : return err;
151 0 : }
152 :
153 0 : if( FD_UNLIKELY( !fd_account_can_data_be_changed( ctx, instr_acc_idx, &err ) ) ) {
154 0 : return err;
155 0 : }
156 :
157 : /* Agave self.touch() is a no-op */
158 :
159 0 : if( FD_UNLIKELY( !fd_account_update_accounts_resize_delta( ctx, instr_acc_idx, data_sz, &err ) ) ) {
160 0 : return err;
161 0 : }
162 :
163 0 : do {
164 0 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, data_sz, &account );
165 0 : 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 0 : } while(0);
169 :
170 : /* AccountSharedData::set_data_from_slice() */
171 0 : account->meta->dlen = data_sz;
172 0 : fd_memcpy( account->data, data, data_sz );
173 :
174 0 : return FD_EXECUTOR_INSTR_SUCCESS;
175 0 : }
176 :
177 : int
178 : fd_account_set_data_length( fd_exec_instr_ctx_t const * ctx,
179 : ulong instr_acc_idx,
180 0 : ulong new_len ) {
181 :
182 0 : fd_borrowed_account_t * account = NULL;
183 0 : do {
184 0 : int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
185 0 : 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 0 : } while(0);
189 :
190 0 : int err = FD_EXECUTOR_INSTR_SUCCESS;
191 0 : if( FD_UNLIKELY( !fd_account_can_data_be_resized( ctx, account->const_meta, new_len, &err ) ) ) {
192 0 : return err;
193 0 : }
194 :
195 0 : if( FD_UNLIKELY( !fd_account_can_data_be_changed( ctx, instr_acc_idx, &err ) ) ) {
196 0 : return err;
197 0 : }
198 :
199 0 : ulong old_len = account->const_meta->dlen;
200 :
201 : /* Don't copy the account if the length does not change */
202 0 : if( old_len==new_len ) {
203 0 : return FD_EXECUTOR_INSTR_SUCCESS;
204 0 : }
205 :
206 : /* Agave self.touch() is a no-op */
207 :
208 0 : if( FD_UNLIKELY( !fd_account_update_accounts_resize_delta( ctx, instr_acc_idx, new_len, &err ) ) ) {
209 0 : return err;
210 0 : }
211 :
212 0 : do {
213 0 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, new_len, &account );
214 0 : 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 0 : } while(0);
218 :
219 0 : if( new_len>old_len ) {
220 0 : fd_memset( account->data+old_len, 0, new_len-old_len );
221 0 : }
222 :
223 0 : account->meta->dlen = new_len;
224 0 : return FD_EXECUTOR_INSTR_SUCCESS;
225 0 : }
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( account->const_meta->info.lamports<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 0 : int * err ) {
291 :
292 0 : fd_borrowed_account_t * account = NULL;
293 0 : *err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
294 0 : if( FD_UNLIKELY( *err ) ) {
295 0 : return 0;
296 0 : }
297 :
298 0 : 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 0 : ctx->txn_ctx->accounts_resize_delta = fd_ulong_sat_add( ctx->txn_ctx->accounts_resize_delta, size_delta );
304 0 : *err = FD_EXECUTOR_INSTR_SUCCESS;
305 0 : return 1;
306 0 : }
|