Line data Source code
1 : #include "fd_txn_account.h"
2 : #include "fd_runtime.h"
3 :
4 : void *
5 : fd_txn_account_new( void * mem,
6 : fd_pubkey_t const * pubkey,
7 : fd_account_meta_t * meta,
8 1029 : int is_mutable ) {
9 1029 : if( FD_UNLIKELY( !mem ) ) {
10 3 : FD_LOG_WARNING(( "NULL mem" ));
11 3 : return NULL;
12 3 : }
13 :
14 1026 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, alignof(fd_txn_account_t) ) ) ) {
15 0 : FD_LOG_WARNING(( "misaligned mem" ));
16 0 : return NULL;
17 0 : }
18 :
19 1026 : if( FD_UNLIKELY( !pubkey ) ) {
20 3 : FD_LOG_WARNING(( "NULL pubkey" ));
21 3 : return NULL;
22 3 : }
23 :
24 1023 : if( FD_UNLIKELY( !meta ) ) {
25 3 : FD_LOG_WARNING(( "NULL meta" ));
26 3 : return NULL;
27 3 : }
28 :
29 1020 : fd_txn_account_t * txn_account = (fd_txn_account_t *)mem;
30 :
31 1020 : fd_memcpy( txn_account->pubkey, pubkey, sizeof(fd_pubkey_t) );
32 :
33 1020 : fd_wksp_t * wksp = fd_wksp_containing( meta );
34 :
35 1020 : txn_account->magic = FD_TXN_ACCOUNT_MAGIC;
36 :
37 1020 : txn_account->starting_dlen = meta->dlen;
38 1020 : txn_account->starting_lamports = meta->lamports;
39 :
40 1020 : uchar * data = (uchar *)meta + sizeof(fd_account_meta_t);
41 :
42 1020 : txn_account->meta_gaddr = fd_wksp_gaddr( wksp, meta );
43 1020 : if( FD_UNLIKELY( !txn_account->meta_gaddr ) ) {
44 0 : FD_LOG_WARNING(( "meta_gaddr is 0" ));
45 0 : return NULL;
46 0 : }
47 :
48 1020 : txn_account->data_gaddr = fd_wksp_gaddr( wksp, data );
49 1020 : if( FD_UNLIKELY( !txn_account->data_gaddr ) ) {
50 0 : FD_LOG_WARNING(( "data_gaddr is 0" ));
51 0 : return NULL;
52 0 : }
53 :
54 1020 : txn_account->meta = meta;
55 1020 : txn_account->data = data;
56 1020 : txn_account->is_mutable = is_mutable;
57 :
58 1020 : return mem;
59 1020 : }
60 :
61 : fd_txn_account_t *
62 1035 : fd_txn_account_join( void * mem, fd_wksp_t * data_wksp ) {
63 1035 : if( FD_UNLIKELY( !mem ) ) {
64 3 : FD_LOG_WARNING(( "NULL mem" ));
65 3 : return NULL;
66 3 : }
67 :
68 1032 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, alignof(fd_txn_account_t) ) ) ) {
69 0 : FD_LOG_WARNING(( "misaligned mem" ));
70 0 : return NULL;
71 0 : }
72 :
73 1032 : if( FD_UNLIKELY( !data_wksp ) ) {
74 3 : FD_LOG_WARNING(( "NULL data_wksp" ));
75 3 : return NULL;
76 3 : }
77 :
78 1029 : fd_txn_account_t * txn_account = (fd_txn_account_t *)mem;
79 :
80 1029 : if( FD_UNLIKELY( txn_account->magic != FD_TXN_ACCOUNT_MAGIC ) ) {
81 6 : FD_LOG_WARNING(( "wrong magic" ));
82 6 : return NULL;
83 6 : }
84 :
85 1023 : if( FD_UNLIKELY( txn_account->meta_gaddr==0UL ) ) {
86 0 : FD_LOG_WARNING(( "`meta gaddr is 0" ));
87 0 : return NULL;
88 0 : }
89 :
90 1023 : txn_account->meta = fd_wksp_laddr( data_wksp, txn_account->meta_gaddr );
91 1023 : if( FD_UNLIKELY( !txn_account->meta ) ) {
92 0 : FD_LOG_WARNING(( "meta is NULL" ));
93 0 : return NULL;
94 0 : }
95 :
96 1023 : txn_account->data = fd_wksp_laddr( data_wksp, txn_account->data_gaddr );
97 1023 : if( FD_UNLIKELY( !txn_account->data && txn_account->meta->dlen ) ) {
98 0 : FD_LOG_WARNING(( "data is NULL" ));
99 0 : return NULL;
100 0 : }
101 :
102 1023 : return txn_account;
103 1023 : }
104 :
105 : void *
106 15 : fd_txn_account_leave( fd_txn_account_t * acct ) {
107 :
108 15 : if( FD_UNLIKELY( !acct ) ) {
109 3 : FD_LOG_WARNING(( "NULL acct" ));
110 3 : return NULL;
111 3 : }
112 :
113 12 : if( FD_UNLIKELY( acct->magic != FD_TXN_ACCOUNT_MAGIC ) ) {
114 0 : FD_LOG_WARNING(( "wrong magic" ));
115 0 : return NULL;
116 0 : }
117 :
118 12 : acct->meta = NULL;
119 12 : acct->data = NULL;
120 :
121 12 : return acct;
122 12 : }
123 :
124 : void *
125 9 : fd_txn_account_delete( void * mem ) {
126 9 : if( FD_UNLIKELY( !mem ) ) {
127 3 : FD_LOG_WARNING(( "NULL mem" ));
128 3 : return NULL;
129 3 : }
130 :
131 6 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, alignof(fd_txn_account_t) ) ) ) {
132 0 : FD_LOG_WARNING(( "misaligned mem" ));
133 0 : return NULL;
134 0 : }
135 :
136 6 : fd_txn_account_t * txn_account = (fd_txn_account_t *)mem;
137 :
138 6 : if( FD_UNLIKELY( txn_account->magic != FD_TXN_ACCOUNT_MAGIC ) ) {
139 0 : FD_LOG_WARNING(( "wrong magic" ));
140 0 : return NULL;
141 0 : }
142 :
143 6 : txn_account->magic = 0UL;
144 :
145 6 : return mem;
146 6 : }
147 :
148 : /* Factory constructors from funk */
149 :
150 : int
151 : fd_txn_account_init_from_funk_readonly( fd_txn_account_t * acct,
152 : fd_pubkey_t const * pubkey,
153 : fd_funk_t const * funk,
154 4194 : fd_funk_txn_t const * funk_txn ) {
155 :
156 4194 : int err = FD_ACC_MGR_SUCCESS;
157 4194 : fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly(
158 4194 : funk,
159 4194 : funk_txn,
160 4194 : pubkey,
161 4194 : NULL,
162 4194 : &err,
163 4194 : NULL );
164 :
165 4194 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
166 3675 : return err;
167 3675 : }
168 :
169 519 : if( FD_UNLIKELY( !fd_txn_account_join( fd_txn_account_new(
170 519 : acct,
171 519 : pubkey,
172 519 : (fd_account_meta_t *)meta,
173 519 : 0 ), fd_funk_wksp( funk ) ) ) ) {
174 0 : FD_LOG_CRIT(( "Failed to join txn account" ));
175 0 : }
176 :
177 519 : return FD_ACC_MGR_SUCCESS;
178 519 : }
179 :
180 : int
181 : fd_txn_account_init_from_funk_mutable( fd_txn_account_t * acct,
182 : fd_pubkey_t const * pubkey,
183 : fd_funk_t * funk,
184 : fd_funk_txn_t * funk_txn,
185 : int do_create,
186 : ulong min_data_sz,
187 495 : fd_funk_rec_prepare_t * prepare_out ) {
188 495 : memset( prepare_out, 0, sizeof(fd_funk_rec_prepare_t) );
189 495 : int err = FD_ACC_MGR_SUCCESS;
190 495 : fd_account_meta_t * meta = fd_funk_get_acc_meta_mutable(
191 495 : funk,
192 495 : funk_txn,
193 495 : pubkey,
194 495 : do_create,
195 495 : min_data_sz,
196 495 : NULL,
197 495 : prepare_out,
198 495 : &err );
199 :
200 495 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
201 0 : return err;
202 0 : }
203 :
204 : /* exec tile should never call this function, so the global addresses
205 : of meta and data should never be used. Instead, populate the
206 : prepared_rec field so that any created records can be published
207 : with fd_txn_account_mutable_fini. */
208 :
209 495 : if( FD_UNLIKELY( !fd_txn_account_join( fd_txn_account_new(
210 495 : acct,
211 495 : pubkey,
212 495 : (fd_account_meta_t *)meta,
213 495 : 1 ), fd_funk_wksp( funk ) ) ) ) {
214 0 : FD_LOG_CRIT(( "Failed to join txn account" ));
215 0 : }
216 :
217 495 : return FD_ACC_MGR_SUCCESS;
218 495 : }
219 :
220 : void
221 : fd_txn_account_mutable_fini( fd_txn_account_t * acct,
222 : fd_funk_t * funk,
223 : fd_funk_txn_t * txn,
224 495 : fd_funk_rec_prepare_t * prepare ) {
225 495 : (void)txn;
226 495 : fd_funk_rec_key_t key = fd_funk_acc_key( acct->pubkey );
227 :
228 : /* Check that the prepared record is still valid -
229 : if these invariants are broken something is very wrong. */
230 495 : if( prepare->rec ) {
231 : /* Check that the prepared record is not the Funk null value */
232 39 : if( !prepare->rec->val_gaddr ) {
233 0 : FD_LOG_CRIT(( "invalid prepared record for %s: unexpected NULL funk record value. the record might have been modified by another thread",
234 0 : FD_BASE58_ENC_32_ALLOCA( acct->pubkey ) ));
235 0 : }
236 :
237 : /* Ensure that the prepared record key still matches our key. */
238 39 : if( FD_UNLIKELY( memcmp( prepare->rec->pair.key, &key, sizeof(fd_funk_rec_key_t) )!=0 ) ) {
239 0 : FD_LOG_CRIT(( "invalid prepared record for %s: the record might have been modified by another thread",
240 0 : FD_BASE58_ENC_32_ALLOCA( acct->pubkey ) ));
241 0 : }
242 :
243 : /* Crashes the app if this key already exists in funk (conflicting
244 : write) */
245 39 : fd_funk_rec_publish( funk, prepare );
246 39 : }
247 495 : }
248 :
249 : /* read/write mutual exclusion */
250 :
251 : FD_FN_PURE int
252 0 : fd_txn_account_acquire_write_is_safe( fd_txn_account_t const * acct ) {
253 0 : return !acct->refcnt_excl;
254 0 : }
255 :
256 : /* fd_txn_account_acquire_write acquires write/exclusive access.
257 : Causes all other write or read acquire attempts will fail. Returns 1
258 : on success, 0 on failure.
259 :
260 : Mirrors a try_borrow_mut() call in Agave. */
261 : int
262 0 : fd_txn_account_acquire_write( fd_txn_account_t * acct ) {
263 0 : if( FD_UNLIKELY( !fd_txn_account_acquire_write_is_safe( acct ) ) ) {
264 0 : return 0;
265 0 : }
266 0 : acct->refcnt_excl = (ushort)1;
267 0 : return 1;
268 0 : }
269 :
270 : /* fd_txn_account_release_write{_private} releases a write/exclusive
271 : access handle. The private version should only be used by fd_borrowed_account_drop
272 : and fd_borrowed_account_destroy. */
273 : void
274 0 : fd_txn_account_release_write( fd_txn_account_t * acct ) {
275 0 : if( FD_UNLIKELY( acct->refcnt_excl!=1 ) ) {
276 0 : FD_LOG_CRIT(( "refcnt_excl is %d, expected 1", acct->refcnt_excl ));
277 0 : }
278 0 : acct->refcnt_excl = (ushort)0;
279 0 : }
280 :
281 : void
282 0 : fd_txn_account_release_write_private( fd_txn_account_t * acct ) {
283 : /* Only release if it is not yet released */
284 0 : if( !fd_txn_account_acquire_write_is_safe( acct ) ) {
285 0 : fd_txn_account_release_write( acct );
286 0 : }
287 0 : }
288 :
289 : fd_pubkey_t const *
290 174 : fd_txn_account_get_owner( fd_txn_account_t const * acct ) {
291 174 : if( FD_UNLIKELY( !acct->meta ) ) {
292 0 : FD_LOG_CRIT(( "account is not setup" ));
293 0 : }
294 174 : return (fd_pubkey_t const *)acct->meta->owner;
295 174 : }
296 :
297 : fd_account_meta_t const *
298 927 : fd_txn_account_get_meta( fd_txn_account_t const * acct ) {
299 927 : return acct->meta;
300 927 : }
301 :
302 : uchar const *
303 1434 : fd_txn_account_get_data( fd_txn_account_t const * acct ) {
304 1434 : return acct->data;
305 1434 : }
306 :
307 : uchar *
308 0 : fd_txn_account_get_data_mut( fd_txn_account_t const * acct ) {
309 0 : return acct->data;
310 0 : }
311 :
312 : ulong
313 516 : fd_txn_account_get_data_len( fd_txn_account_t const * acct ) {
314 516 : if( FD_UNLIKELY( !acct->meta ) ) {
315 0 : FD_LOG_CRIT(( "account is not setup" ));
316 0 : }
317 516 : return acct->meta->dlen;
318 516 : }
319 :
320 : int
321 0 : fd_txn_account_is_executable( fd_txn_account_t const * acct ) {
322 0 : if( FD_UNLIKELY( !acct->meta ) ) {
323 0 : FD_LOG_CRIT(( "account is not setup" ));
324 0 : }
325 0 : return !!acct->meta->executable;
326 0 : }
327 :
328 : ulong
329 936 : fd_txn_account_get_lamports( fd_txn_account_t const * acct ) {
330 936 : if( FD_UNLIKELY( !acct->meta ) ) {
331 0 : FD_LOG_CRIT(( "account is not setup" ));
332 0 : }
333 936 : return acct->meta->lamports;
334 936 : }
335 :
336 : ulong
337 6 : fd_txn_account_get_rent_epoch( fd_txn_account_t const * acct ) {
338 6 : (void)acct;
339 6 : return ULONG_MAX;
340 6 : }
341 :
342 : void
343 0 : fd_txn_account_set_meta( fd_txn_account_t * acct, fd_account_meta_t * meta ) {
344 0 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
345 0 : FD_LOG_CRIT(( "account is not mutable" ));
346 0 : }
347 0 : if( FD_UNLIKELY( !meta ) ) {
348 0 : FD_LOG_CRIT(( "account is not setup" ));
349 0 : }
350 0 : acct->meta = meta;
351 0 : }
352 :
353 : void
354 30 : fd_txn_account_set_executable( fd_txn_account_t * acct, int is_executable ) {
355 30 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
356 0 : FD_LOG_CRIT(( "account is not mutable" ));
357 0 : }
358 30 : if( FD_UNLIKELY( !acct->meta ) ) {
359 0 : FD_LOG_CRIT(( "account is not setup" ));
360 0 : }
361 30 : acct->meta->executable = !!is_executable;
362 30 : }
363 :
364 : void
365 492 : fd_txn_account_set_owner( fd_txn_account_t * acct, fd_pubkey_t const * owner ) {
366 492 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
367 0 : FD_LOG_CRIT(( "account is not mutable" ));
368 0 : }
369 492 : if( FD_UNLIKELY( !acct->meta ) ) {
370 0 : FD_LOG_CRIT(( "account is not setup" ));
371 0 : }
372 492 : fd_memcpy( acct->meta->owner, owner, sizeof(fd_pubkey_t) );
373 492 : }
374 :
375 : void
376 495 : fd_txn_account_set_lamports( fd_txn_account_t * acct, ulong lamports ) {
377 495 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
378 0 : FD_LOG_CRIT(( "account is not mutable" ));
379 0 : }
380 495 : if( FD_UNLIKELY( !acct->meta ) ) {
381 0 : FD_LOG_CRIT(( "account is not setup" ));
382 0 : }
383 495 : acct->meta->lamports = lamports;
384 495 : }
385 :
386 : int
387 0 : fd_txn_account_checked_add_lamports( fd_txn_account_t * acct, ulong lamports ) {
388 0 : ulong balance_post = 0UL;
389 0 : int err = fd_ulong_checked_add( fd_txn_account_get_lamports( acct ), lamports, &balance_post );
390 0 : if( FD_UNLIKELY( err ) ) {
391 0 : return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
392 0 : }
393 :
394 0 : fd_txn_account_set_lamports( acct, balance_post );
395 0 : return FD_EXECUTOR_INSTR_SUCCESS;
396 0 : }
397 :
398 : int
399 0 : fd_txn_account_checked_sub_lamports( fd_txn_account_t * acct, ulong lamports ) {
400 0 : ulong balance_post = 0UL;
401 0 : int err = fd_ulong_checked_sub( fd_txn_account_get_lamports( acct ),
402 0 : lamports,
403 0 : &balance_post );
404 0 : if( FD_UNLIKELY( err ) ) {
405 0 : return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
406 0 : }
407 :
408 0 : fd_txn_account_set_lamports( acct, balance_post );
409 0 : return FD_EXECUTOR_INSTR_SUCCESS;
410 0 : }
411 :
412 : void
413 : fd_txn_account_set_data( fd_txn_account_t * acct,
414 : void const * data,
415 495 : ulong data_sz ) {
416 495 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
417 0 : FD_LOG_CRIT(( "account is not mutable" ));
418 0 : }
419 495 : if( FD_UNLIKELY( !acct->meta ) ) {
420 0 : FD_LOG_CRIT(( "account is not setup" ));
421 0 : }
422 495 : acct->meta->dlen = (uint)data_sz;
423 495 : fd_memcpy( acct->data, data, data_sz );
424 495 : }
425 :
426 : void
427 3 : fd_txn_account_set_data_len( fd_txn_account_t * acct, ulong data_len ) {
428 3 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
429 0 : FD_LOG_CRIT(( "account is not mutable" ));
430 0 : }
431 3 : if( FD_UNLIKELY( !acct->meta ) ) {
432 0 : FD_LOG_CRIT(( "account is not setup" ));
433 0 : }
434 3 : acct->meta->dlen = (uint)data_len;
435 3 : }
436 :
437 : void
438 462 : fd_txn_account_set_slot( fd_txn_account_t * acct, ulong slot ) {
439 462 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
440 0 : FD_LOG_CRIT(( "account is not mutable" ));
441 0 : }
442 462 : if( FD_UNLIKELY( !acct->meta ) ) {
443 0 : FD_LOG_CRIT(( "account is not setup" ));
444 0 : }
445 462 : acct->meta->slot = slot;
446 462 : }
447 :
448 : void
449 0 : fd_txn_account_clear_owner( fd_txn_account_t * acct ) {
450 0 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
451 0 : FD_LOG_CRIT(( "account is not mutable" ));
452 0 : }
453 0 : if( FD_UNLIKELY( !acct->meta ) ) {
454 0 : FD_LOG_CRIT(( "account is not setup" ));
455 0 : }
456 0 : fd_memset( acct->meta->owner, 0, sizeof(fd_pubkey_t) );
457 0 : }
458 :
459 : void
460 : fd_txn_account_resize( fd_txn_account_t * acct,
461 0 : ulong dlen ) {
462 0 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
463 0 : FD_LOG_CRIT(( "account is not mutable" ));
464 0 : }
465 0 : if( FD_UNLIKELY( !acct->meta ) ) {
466 0 : FD_LOG_CRIT(( "account is not setup" ));
467 0 : }
468 : /* Because the memory for an account is preallocated for the transaction
469 : up to the max account size, we only need to zero out bytes (for the case
470 : where the account grew) and update the account dlen. */
471 0 : ulong old_sz = acct->meta->dlen;
472 0 : ulong new_sz = dlen;
473 0 : ulong memset_sz = fd_ulong_sat_sub( new_sz, old_sz );
474 0 : fd_memset( acct->data+old_sz, 0, memset_sz );
475 :
476 0 : acct->meta->dlen = (uint)dlen;
477 0 : }
478 :
479 : ushort
480 0 : fd_txn_account_is_borrowed( fd_txn_account_t const * acct ) {
481 0 : return !!acct->refcnt_excl;
482 0 : }
483 :
484 : int
485 6 : fd_txn_account_is_mutable( fd_txn_account_t const * acct ) {
486 : /* A txn account is mutable if meta is non NULL */
487 6 : return acct->is_mutable;
488 6 : }
489 :
490 : int
491 6 : fd_txn_account_is_readonly( fd_txn_account_t const * acct ) {
492 : /* A txn account is readonly if only the meta_ field is non NULL */
493 6 : return !acct->is_mutable;
494 6 : }
495 :
496 : int
497 0 : fd_txn_account_try_borrow_mut( fd_txn_account_t * acct ) {
498 0 : return fd_txn_account_acquire_write( acct );
499 0 : }
500 :
501 : void
502 0 : fd_txn_account_drop( fd_txn_account_t * acct ) {
503 0 : fd_txn_account_release_write_private( acct );
504 0 : }
505 :
506 : void
507 0 : fd_txn_account_set_readonly( fd_txn_account_t * acct ) {
508 0 : acct->is_mutable = 0;
509 0 : }
510 :
511 : void
512 0 : fd_txn_account_set_mutable( fd_txn_account_t * acct ) {
513 0 : acct->is_mutable = 1;
514 0 : }
515 :
516 : fd_solana_account_meta_t
517 0 : fd_txn_account_get_solana_meta( fd_txn_account_t const * acct ) {
518 0 : fd_solana_account_meta_t meta = {
519 0 : .lamports = acct->meta->lamports,
520 : .rent_epoch = ULONG_MAX,
521 0 : .executable = acct->meta->executable,
522 0 : };
523 0 : memcpy( meta.owner, acct->meta->owner, sizeof(fd_pubkey_t) );
524 0 : return meta;
525 0 : }
|