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/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/sdk/src/transaction_context.rs#L740-L767 */
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 2250 : fd_pubkey_t const * owner ) {
10 :
11 2250 : fd_instr_info_t const * instr = ctx->instr;
12 2250 : fd_borrowed_account_t * account = NULL;
13 2250 : do {
14 2250 : int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
15 2250 : 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 2250 : } while(0);
19 :
20 2250 : fd_account_meta_t const * meta = account->const_meta;
21 :
22 : /* Only the owner can assign a new owner */
23 2250 : if( !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 2046 : if( !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 1998 : if( fd_account_is_executable( 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 1974 : if( !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 1917 : if( !memcmp( account->const_meta->info.owner, owner, sizeof( fd_pubkey_t ) ) ) {
40 0 : return FD_EXECUTOR_INSTR_SUCCESS;
41 0 : }
42 :
43 1917 : do {
44 1917 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
45 1917 : 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 1917 : } while(0);
49 :
50 : /* Agave self.touch() is a no-op */
51 :
52 1917 : memcpy( account->meta->info.owner, owner, sizeof(fd_pubkey_t) );
53 1917 : return FD_EXECUTOR_INSTR_SUCCESS;
54 1917 : }
55 :
56 : /* https://github.com/anza-xyz/agave/blob/b5f5c3cdd3f9a5859c49ebc27221dc27e143d760/sdk/src/transaction_context.rs#L774-L796 */
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 8226 : ulong lamports ) {
62 :
63 8226 : fd_borrowed_account_t * account = NULL;
64 8226 : do {
65 8226 : int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
66 8226 : 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 8226 : } while(0);
70 :
71 : /* An account not owned by the program cannot have its blanace decrease */
72 8226 : if( FD_UNLIKELY( ( !fd_account_is_owned_by_current_program( ctx->instr, account->const_meta ) ) &&
73 8226 : ( lamports < account->const_meta->info.lamports ) ) ) {
74 66 : return FD_EXECUTOR_INSTR_ERR_EXTERNAL_ACCOUNT_LAMPORT_SPEND;
75 66 : }
76 :
77 : /* The balance of read-only may not change */
78 8160 : if( FD_UNLIKELY( !fd_instr_acc_is_writable_idx( ctx->instr, instr_acc_idx ) ) ) {
79 225 : return FD_EXECUTOR_INSTR_ERR_READONLY_LAMPORT_CHANGE;
80 225 : }
81 :
82 : /* The balance of executable accounts may not change */
83 7935 : if( FD_UNLIKELY( fd_account_is_executable( account->const_meta ) ) ) {
84 174 : return FD_EXECUTOR_INSTR_ERR_EXECUTABLE_LAMPORT_CHANGE;
85 174 : }
86 :
87 : /* Don't copy the account if the lamports do not change */
88 7761 : if( lamports==account->const_meta->info.lamports ) {
89 708 : return FD_EXECUTOR_INSTR_SUCCESS;
90 708 : }
91 :
92 7053 : do {
93 7053 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
94 7053 : 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 7053 : } while(0);
98 :
99 : /* Agave self.touch() is a no-op */
100 :
101 7053 : account->meta->info.lamports = lamports;
102 7053 : return FD_EXECUTOR_INSTR_SUCCESS;
103 7053 : }
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 4155 : ulong * dlen_out ) {
110 :
111 4155 : int err;
112 4155 : if( FD_UNLIKELY( !fd_account_can_data_be_changed( ctx->instr, instr_acc_idx, &err ) ) ) {
113 453 : return err;
114 453 : }
115 :
116 3702 : fd_borrowed_account_t * account = NULL;
117 3702 : do {
118 3702 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, 0UL, &account );
119 3702 : 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 3702 : } while(0);
123 :
124 : /* Agave self.touch() is a no-op */
125 :
126 3702 : if (NULL != data_out)
127 3702 : *data_out = account->data;
128 3702 : if (NULL != dlen_out)
129 3702 : *dlen_out = account->meta->dlen;
130 :
131 3702 : return FD_EXECUTOR_INSTR_SUCCESS;
132 3702 : }
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 777 : ulong data_sz ) {
139 :
140 777 : fd_borrowed_account_t * account = NULL;
141 777 : do {
142 777 : int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
143 777 : 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 777 : } while(0);
147 :
148 777 : int err;
149 777 : if( FD_UNLIKELY( !fd_account_can_data_be_resized( ctx, account->const_meta, data_sz, &err ) ) ) {
150 0 : return err;
151 0 : }
152 :
153 777 : if( FD_UNLIKELY( !fd_account_can_data_be_changed( ctx->instr, instr_acc_idx, &err ) ) ) {
154 3 : return err;
155 3 : }
156 :
157 : /* Agave self.touch() is a no-op */
158 :
159 774 : if( FD_UNLIKELY( !fd_account_update_accounts_resize_delta( ctx, instr_acc_idx, data_sz, &err ) ) ) {
160 0 : return err;
161 0 : }
162 :
163 774 : do {
164 774 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, data_sz, &account );
165 774 : 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 774 : } while(0);
169 :
170 : /* AccountSharedData::set_data_from_slice() */
171 774 : account->meta->dlen = data_sz;
172 774 : fd_memcpy( account->data, data, data_sz );
173 :
174 774 : return FD_EXECUTOR_INSTR_SUCCESS;
175 774 : }
176 :
177 : int
178 : fd_account_set_data_length( fd_exec_instr_ctx_t const * ctx,
179 : ulong instr_acc_idx,
180 3021 : ulong new_len ) {
181 :
182 3021 : fd_borrowed_account_t * account = NULL;
183 3021 : do {
184 3021 : int err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
185 3021 : 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 3021 : } while(0);
189 :
190 3021 : int err = FD_EXECUTOR_INSTR_SUCCESS;
191 3021 : if( !fd_account_can_data_be_resized( ctx, account->const_meta, new_len, &err ) ) {
192 3 : return err;
193 3 : }
194 :
195 3018 : if( !fd_account_can_data_be_changed( ctx->instr, instr_acc_idx, &err ) ) {
196 318 : return err;
197 318 : }
198 :
199 2700 : ulong old_len = account->const_meta->dlen;
200 :
201 : /* Don't copy the account if the length does not change */
202 2700 : 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 2655 : if( !fd_account_update_accounts_resize_delta( ctx, instr_acc_idx, new_len, &err ) ) {
209 0 : return err;
210 0 : }
211 :
212 2655 : do {
213 2655 : int err = fd_instr_borrowed_account_modify_idx( ctx, (uchar)instr_acc_idx, new_len, &account );
214 2655 : 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 2655 : } while(0);
218 :
219 2655 : if( new_len>old_len ) {
220 2529 : fd_memset( account->data+old_len, 0, new_len-old_len );
221 2529 : }
222 :
223 2655 : account->meta->dlen = new_len;
224 2655 : return FD_EXECUTOR_INSTR_SUCCESS;
225 2655 : }
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( 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 3429 : int * err ) {
291 :
292 3429 : fd_borrowed_account_t * account = NULL;
293 3429 : *err = fd_instr_borrowed_account_view_idx( ctx, (uchar)instr_acc_idx, &account );
294 3429 : if( FD_UNLIKELY( *err ) ) {
295 0 : return 0;
296 0 : }
297 :
298 3429 : 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 3429 : ctx->txn_ctx->accounts_resize_delta = fd_ulong_sat_add( ctx->txn_ctx->accounts_resize_delta, size_delta );
304 3429 : *err = FD_EXECUTOR_INSTR_SUCCESS;
305 3429 : return 1;
306 3429 : }
|