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