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_BASE58_ENCODE_32_BYTES( acct->pubkey->uc, acct_pubkey_b58 );
205 0 : FD_LOG_CRIT(( "invalid prepared record for %s: unexpected NULL funk record value. the record might have been modified by another thread",
206 0 : acct_pubkey_b58 ));
207 0 : }
208 :
209 : /* Ensure that the prepared record key still matches our key. */
210 45 : if( FD_UNLIKELY( memcmp( prepare->rec->pair.key, &key, sizeof(fd_funk_rec_key_t) )!=0 ) ) {
211 0 : FD_BASE58_ENCODE_32_BYTES( acct->pubkey->uc, acct_pubkey_b58 );
212 0 : FD_LOG_CRIT(( "invalid prepared record for %s: the record might have been modified by another thread",
213 0 : acct_pubkey_b58 ));
214 0 : }
215 :
216 : /* Crashes the app if this key already exists in funk (conflicting
217 : write) */
218 45 : fd_funk_rec_publish( accdb->funk, prepare );
219 45 : }
220 498 : }
221 :
222 : /* read/write mutual exclusion */
223 :
224 : FD_FN_PURE int
225 0 : fd_txn_account_acquire_write_is_safe( fd_txn_account_t const * acct ) {
226 0 : return !acct->refcnt_excl;
227 0 : }
228 :
229 : /* fd_txn_account_acquire_write acquires write/exclusive access.
230 : Causes all other write or read acquire attempts will fail. Returns 1
231 : on success, 0 on failure.
232 :
233 : Mirrors a try_borrow_mut() call in Agave. */
234 : int
235 0 : fd_txn_account_acquire_write( fd_txn_account_t * acct ) {
236 0 : if( FD_UNLIKELY( !fd_txn_account_acquire_write_is_safe( acct ) ) ) {
237 0 : return 0;
238 0 : }
239 0 : acct->refcnt_excl = (ushort)1;
240 0 : return 1;
241 0 : }
242 :
243 : /* fd_txn_account_release_write{_private} releases a write/exclusive
244 : access handle. The private version should only be used by fd_borrowed_account_drop
245 : and fd_borrowed_account_destroy. */
246 : void
247 0 : fd_txn_account_release_write( fd_txn_account_t * acct ) {
248 0 : if( FD_UNLIKELY( acct->refcnt_excl!=1 ) ) {
249 0 : FD_LOG_CRIT(( "refcnt_excl is %d, expected 1", acct->refcnt_excl ));
250 0 : }
251 0 : acct->refcnt_excl = (ushort)0;
252 0 : }
253 :
254 : void
255 0 : fd_txn_account_release_write_private( fd_txn_account_t * acct ) {
256 : /* Only release if it is not yet released */
257 0 : if( !fd_txn_account_acquire_write_is_safe( acct ) ) {
258 0 : fd_txn_account_release_write( acct );
259 0 : }
260 0 : }
261 :
262 : fd_pubkey_t const *
263 51 : fd_txn_account_get_owner( fd_txn_account_t const * acct ) {
264 51 : if( FD_UNLIKELY( !acct->meta ) ) {
265 0 : FD_LOG_CRIT(( "account is not setup" ));
266 0 : }
267 51 : return (fd_pubkey_t const *)acct->meta->owner;
268 51 : }
269 :
270 : fd_account_meta_t const *
271 927 : fd_txn_account_get_meta( fd_txn_account_t const * acct ) {
272 927 : return acct->meta;
273 927 : }
274 :
275 : uchar const *
276 1437 : fd_txn_account_get_data( fd_txn_account_t const * acct ) {
277 1437 : return acct->data;
278 1437 : }
279 :
280 : uchar *
281 0 : fd_txn_account_get_data_mut( fd_txn_account_t const * acct ) {
282 0 : return acct->data;
283 0 : }
284 :
285 : ulong
286 519 : fd_txn_account_get_data_len( fd_txn_account_t const * acct ) {
287 519 : if( FD_UNLIKELY( !acct->meta ) ) {
288 0 : FD_LOG_CRIT(( "account is not setup" ));
289 0 : }
290 519 : return acct->meta->dlen;
291 519 : }
292 :
293 : int
294 0 : fd_txn_account_is_executable( fd_txn_account_t const * acct ) {
295 0 : if( FD_UNLIKELY( !acct->meta ) ) {
296 0 : FD_LOG_CRIT(( "account is not setup" ));
297 0 : }
298 0 : return !!acct->meta->executable;
299 0 : }
300 :
301 : ulong
302 936 : fd_txn_account_get_lamports( fd_txn_account_t const * acct ) {
303 936 : if( FD_UNLIKELY( !acct->meta ) ) {
304 0 : FD_LOG_CRIT(( "account is not setup" ));
305 0 : }
306 936 : return acct->meta->lamports;
307 936 : }
308 :
309 : ulong
310 6 : fd_txn_account_get_rent_epoch( fd_txn_account_t const * acct ) {
311 6 : (void)acct;
312 6 : return ULONG_MAX;
313 6 : }
314 :
315 : void
316 0 : fd_txn_account_set_meta( fd_txn_account_t * acct, fd_account_meta_t * meta ) {
317 0 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
318 0 : FD_LOG_CRIT(( "account is not mutable" ));
319 0 : }
320 0 : if( FD_UNLIKELY( !meta ) ) {
321 0 : FD_LOG_CRIT(( "account is not setup" ));
322 0 : }
323 0 : acct->meta = meta;
324 0 : }
325 :
326 : void
327 36 : fd_txn_account_set_executable( fd_txn_account_t * acct, int is_executable ) {
328 36 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
329 0 : FD_LOG_CRIT(( "account is not mutable" ));
330 0 : }
331 36 : if( FD_UNLIKELY( !acct->meta ) ) {
332 0 : FD_LOG_CRIT(( "account is not setup" ));
333 0 : }
334 36 : acct->meta->executable = !!is_executable;
335 36 : }
336 :
337 : void
338 498 : fd_txn_account_set_owner( fd_txn_account_t * acct, fd_pubkey_t const * owner ) {
339 498 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
340 0 : FD_LOG_CRIT(( "account is not mutable" ));
341 0 : }
342 498 : if( FD_UNLIKELY( !acct->meta ) ) {
343 0 : FD_LOG_CRIT(( "account is not setup" ));
344 0 : }
345 498 : fd_memcpy( acct->meta->owner, owner, sizeof(fd_pubkey_t) );
346 498 : }
347 :
348 : void
349 501 : fd_txn_account_set_lamports( fd_txn_account_t * acct, ulong lamports ) {
350 501 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
351 0 : FD_LOG_CRIT(( "account is not mutable" ));
352 0 : }
353 501 : if( FD_UNLIKELY( !acct->meta ) ) {
354 0 : FD_LOG_CRIT(( "account is not setup" ));
355 0 : }
356 501 : acct->meta->lamports = lamports;
357 501 : }
358 :
359 : int
360 0 : fd_txn_account_checked_add_lamports( fd_txn_account_t * acct, ulong lamports ) {
361 0 : ulong balance_post = 0UL;
362 0 : int err = fd_ulong_checked_add( fd_txn_account_get_lamports( acct ), lamports, &balance_post );
363 0 : if( FD_UNLIKELY( err ) ) {
364 0 : return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
365 0 : }
366 :
367 0 : fd_txn_account_set_lamports( acct, balance_post );
368 0 : return FD_EXECUTOR_INSTR_SUCCESS;
369 0 : }
370 :
371 : int
372 0 : fd_txn_account_checked_sub_lamports( fd_txn_account_t * acct, ulong lamports ) {
373 0 : ulong balance_post = 0UL;
374 0 : int err = fd_ulong_checked_sub( fd_txn_account_get_lamports( acct ),
375 0 : lamports,
376 0 : &balance_post );
377 0 : if( FD_UNLIKELY( err ) ) {
378 0 : return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
379 0 : }
380 :
381 0 : fd_txn_account_set_lamports( acct, balance_post );
382 0 : return FD_EXECUTOR_INSTR_SUCCESS;
383 0 : }
384 :
385 : void
386 : fd_txn_account_set_data( fd_txn_account_t * acct,
387 : void const * data,
388 498 : ulong data_sz ) {
389 498 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
390 0 : FD_LOG_CRIT(( "account is not mutable" ));
391 0 : }
392 498 : if( FD_UNLIKELY( !acct->meta ) ) {
393 0 : FD_LOG_CRIT(( "account is not setup" ));
394 0 : }
395 498 : acct->meta->dlen = (uint)data_sz;
396 498 : fd_memcpy( acct->data, data, data_sz );
397 498 : }
398 :
399 : void
400 3 : fd_txn_account_set_data_len( fd_txn_account_t * acct, ulong data_len ) {
401 3 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
402 0 : FD_LOG_CRIT(( "account is not mutable" ));
403 0 : }
404 3 : if( FD_UNLIKELY( !acct->meta ) ) {
405 0 : FD_LOG_CRIT(( "account is not setup" ));
406 0 : }
407 3 : acct->meta->dlen = (uint)data_len;
408 3 : }
409 :
410 : void
411 462 : fd_txn_account_set_slot( fd_txn_account_t * acct, ulong slot ) {
412 462 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
413 0 : FD_LOG_CRIT(( "account is not mutable" ));
414 0 : }
415 462 : if( FD_UNLIKELY( !acct->meta ) ) {
416 0 : FD_LOG_CRIT(( "account is not setup" ));
417 0 : }
418 462 : acct->meta->slot = slot;
419 462 : }
420 :
421 : void
422 0 : fd_txn_account_clear_owner( fd_txn_account_t * acct ) {
423 0 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
424 0 : FD_LOG_CRIT(( "account is not mutable" ));
425 0 : }
426 0 : if( FD_UNLIKELY( !acct->meta ) ) {
427 0 : FD_LOG_CRIT(( "account is not setup" ));
428 0 : }
429 0 : fd_memset( acct->meta->owner, 0, sizeof(fd_pubkey_t) );
430 0 : }
431 :
432 : void
433 : fd_txn_account_resize( fd_txn_account_t * acct,
434 0 : ulong dlen ) {
435 0 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
436 0 : FD_LOG_CRIT(( "account is not mutable" ));
437 0 : }
438 0 : if( FD_UNLIKELY( !acct->meta ) ) {
439 0 : FD_LOG_CRIT(( "account is not setup" ));
440 0 : }
441 : /* Because the memory for an account is preallocated for the transaction
442 : up to the max account size, we only need to zero out bytes (for the case
443 : where the account grew) and update the account dlen. */
444 0 : ulong old_sz = acct->meta->dlen;
445 0 : ulong new_sz = dlen;
446 0 : ulong memset_sz = fd_ulong_sat_sub( new_sz, old_sz );
447 0 : fd_memset( acct->data+old_sz, 0, memset_sz );
448 :
449 0 : acct->meta->dlen = (uint)dlen;
450 0 : }
451 :
452 : ushort
453 0 : fd_txn_account_is_borrowed( fd_txn_account_t const * acct ) {
454 0 : return !!acct->refcnt_excl;
455 0 : }
456 :
457 : int
458 6 : fd_txn_account_is_mutable( fd_txn_account_t const * acct ) {
459 : /* A txn account is mutable if meta is non NULL */
460 6 : return acct->is_mutable;
461 6 : }
462 :
463 : int
464 6 : fd_txn_account_is_readonly( fd_txn_account_t const * acct ) {
465 : /* A txn account is readonly if only the meta_ field is non NULL */
466 6 : return !acct->is_mutable;
467 6 : }
468 :
469 : int
470 0 : fd_txn_account_try_borrow_mut( fd_txn_account_t * acct ) {
471 0 : return fd_txn_account_acquire_write( acct );
472 0 : }
473 :
474 : void
475 0 : fd_txn_account_drop( fd_txn_account_t * acct ) {
476 0 : fd_txn_account_release_write_private( acct );
477 0 : }
478 :
479 : void
480 0 : fd_txn_account_set_readonly( fd_txn_account_t * acct ) {
481 0 : acct->is_mutable = 0;
482 0 : }
483 :
484 : void
485 0 : fd_txn_account_set_mutable( fd_txn_account_t * acct ) {
486 0 : acct->is_mutable = 1;
487 0 : }
|