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