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