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 1059 : int is_mutable ) {
9 1059 : if( FD_UNLIKELY( !mem ) ) {
10 3 : FD_LOG_WARNING(( "NULL mem" ));
11 3 : return NULL;
12 3 : }
13 :
14 1056 : 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 1056 : if( FD_UNLIKELY( !pubkey ) ) {
20 3 : FD_LOG_WARNING(( "NULL pubkey" ));
21 3 : return NULL;
22 3 : }
23 :
24 1053 : if( FD_UNLIKELY( !meta ) ) {
25 3 : FD_LOG_WARNING(( "NULL meta" ));
26 3 : return NULL;
27 3 : }
28 :
29 1050 : fd_txn_account_t * txn_account = (fd_txn_account_t *)mem;
30 :
31 1050 : fd_memcpy( txn_account->pubkey, pubkey, sizeof(fd_pubkey_t) );
32 :
33 1050 : txn_account->magic = FD_TXN_ACCOUNT_MAGIC;
34 :
35 1050 : txn_account->starting_dlen = meta->dlen;
36 1050 : txn_account->starting_lamports = meta->lamports;
37 :
38 1050 : uchar * data = (uchar *)meta + sizeof(fd_account_meta_t);
39 :
40 1050 : txn_account->meta_soff = (long)( (ulong)meta - (ulong)mem );
41 :
42 1050 : txn_account->meta = meta;
43 1050 : txn_account->data = data;
44 1050 : txn_account->is_mutable = is_mutable;
45 :
46 1050 : return mem;
47 1053 : }
48 :
49 : fd_txn_account_t *
50 1065 : fd_txn_account_join( void * mem, fd_wksp_t * data_wksp ) {
51 1065 : if( FD_UNLIKELY( !mem ) ) {
52 3 : FD_LOG_WARNING(( "NULL mem" ));
53 3 : return NULL;
54 3 : }
55 :
56 1062 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, alignof(fd_txn_account_t) ) ) ) {
57 0 : FD_LOG_WARNING(( "misaligned mem" ));
58 0 : return NULL;
59 0 : }
60 :
61 1062 : if( FD_UNLIKELY( !data_wksp ) ) {
62 3 : FD_LOG_WARNING(( "NULL data_wksp" ));
63 3 : return NULL;
64 3 : }
65 :
66 1059 : fd_txn_account_t * txn_account = (fd_txn_account_t *)mem;
67 :
68 1059 : if( FD_UNLIKELY( txn_account->magic != FD_TXN_ACCOUNT_MAGIC ) ) {
69 6 : FD_LOG_WARNING(( "wrong magic" ));
70 6 : return NULL;
71 6 : }
72 :
73 1053 : if( FD_UNLIKELY( txn_account->meta_soff==0UL ) ) {
74 0 : FD_LOG_CRIT(( "invalid meta_soff" ));
75 0 : }
76 :
77 1053 : txn_account->meta = (void *)( (ulong)mem + (ulong)txn_account->meta_soff );
78 1053 : txn_account->data = (void *)( (ulong)txn_account->meta + sizeof(fd_account_meta_t) );
79 :
80 1053 : return txn_account;
81 1053 : }
82 :
83 : void *
84 15 : fd_txn_account_leave( fd_txn_account_t * acct ) {
85 :
86 15 : if( FD_UNLIKELY( !acct ) ) {
87 3 : FD_LOG_WARNING(( "NULL acct" ));
88 3 : return NULL;
89 3 : }
90 :
91 12 : if( FD_UNLIKELY( acct->magic != FD_TXN_ACCOUNT_MAGIC ) ) {
92 0 : FD_LOG_WARNING(( "wrong magic" ));
93 0 : return NULL;
94 0 : }
95 :
96 12 : acct->meta = NULL;
97 12 : acct->data = NULL;
98 :
99 12 : return acct;
100 12 : }
101 :
102 : void *
103 9 : fd_txn_account_delete( void * mem ) {
104 9 : if( FD_UNLIKELY( !mem ) ) {
105 3 : FD_LOG_WARNING(( "NULL mem" ));
106 3 : return NULL;
107 3 : }
108 :
109 6 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, alignof(fd_txn_account_t) ) ) ) {
110 0 : FD_LOG_WARNING(( "misaligned mem" ));
111 0 : return NULL;
112 0 : }
113 :
114 6 : fd_txn_account_t * txn_account = (fd_txn_account_t *)mem;
115 :
116 6 : if( FD_UNLIKELY( txn_account->magic != FD_TXN_ACCOUNT_MAGIC ) ) {
117 0 : FD_LOG_WARNING(( "wrong magic" ));
118 0 : return NULL;
119 0 : }
120 :
121 6 : txn_account->magic = 0UL;
122 :
123 6 : return mem;
124 6 : }
125 :
126 : /* Factory constructors from funk */
127 :
128 : int
129 : fd_txn_account_init_from_funk_readonly( fd_txn_account_t * acct,
130 : fd_pubkey_t const * pubkey,
131 : fd_funk_t const * funk,
132 4209 : fd_funk_txn_xid_t const * xid ) {
133 :
134 4209 : int err = FD_ACC_MGR_SUCCESS;
135 4209 : fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly(
136 4209 : funk,
137 4209 : xid,
138 4209 : pubkey,
139 4209 : NULL,
140 4209 : &err,
141 4209 : NULL );
142 :
143 4209 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
144 3675 : return err;
145 3675 : }
146 :
147 534 : if( FD_UNLIKELY( !fd_txn_account_join( fd_txn_account_new(
148 534 : acct,
149 534 : pubkey,
150 534 : (fd_account_meta_t *)meta,
151 534 : 0 ), fd_funk_wksp( funk ) ) ) ) {
152 0 : FD_LOG_CRIT(( "Failed to join txn account" ));
153 0 : }
154 :
155 534 : return FD_ACC_MGR_SUCCESS;
156 534 : }
157 :
158 : int
159 : fd_txn_account_init_from_funk_mutable( fd_txn_account_t * acct,
160 : fd_pubkey_t const * pubkey,
161 : fd_funk_t * funk,
162 : fd_funk_txn_xid_t const * xid,
163 : int do_create,
164 : ulong min_data_sz,
165 510 : fd_funk_rec_prepare_t * prepare_out ) {
166 510 : memset( prepare_out, 0, sizeof(fd_funk_rec_prepare_t) );
167 510 : int err = FD_ACC_MGR_SUCCESS;
168 510 : fd_account_meta_t * meta = fd_funk_get_acc_meta_mutable(
169 510 : funk,
170 510 : xid,
171 510 : pubkey,
172 510 : do_create,
173 510 : min_data_sz,
174 510 : NULL,
175 510 : prepare_out,
176 510 : &err );
177 :
178 510 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
179 0 : return err;
180 0 : }
181 :
182 : /* exec tile should never call this function, so the global addresses
183 : of meta and data should never be used. Instead, populate the
184 : prepared_rec field so that any created records can be published
185 : with fd_txn_account_mutable_fini. */
186 :
187 510 : if( FD_UNLIKELY( !fd_txn_account_join( fd_txn_account_new(
188 510 : acct,
189 510 : pubkey,
190 510 : (fd_account_meta_t *)meta,
191 510 : 1 ), fd_funk_wksp( funk ) ) ) ) {
192 0 : FD_LOG_CRIT(( "Failed to join txn account" ));
193 0 : }
194 :
195 510 : return FD_ACC_MGR_SUCCESS;
196 510 : }
197 :
198 : void
199 : fd_txn_account_mutable_fini( fd_txn_account_t * acct,
200 : fd_funk_t * funk,
201 510 : fd_funk_rec_prepare_t * prepare ) {
202 510 : fd_funk_rec_key_t key = fd_funk_acc_key( acct->pubkey );
203 :
204 : /* Check that the prepared record is still valid -
205 : if these invariants are broken something is very wrong. */
206 510 : if( prepare->rec ) {
207 : /* Check that the prepared record is not the Funk null value */
208 45 : if( !prepare->rec->val_gaddr ) {
209 0 : FD_LOG_CRIT(( "invalid prepared record for %s: unexpected NULL funk record value. the record might have been modified by another thread",
210 0 : FD_BASE58_ENC_32_ALLOCA( acct->pubkey ) ));
211 0 : }
212 :
213 : /* Ensure that the prepared record key still matches our key. */
214 45 : if( FD_UNLIKELY( memcmp( prepare->rec->pair.key, &key, sizeof(fd_funk_rec_key_t) )!=0 ) ) {
215 0 : FD_LOG_CRIT(( "invalid prepared record for %s: the record might have been modified by another thread",
216 0 : FD_BASE58_ENC_32_ALLOCA( acct->pubkey ) ));
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 510 : }
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 198 : fd_txn_account_get_owner( fd_txn_account_t const * acct ) {
267 198 : if( FD_UNLIKELY( !acct->meta ) ) {
268 0 : FD_LOG_CRIT(( "account is not setup" ));
269 0 : }
270 198 : return (fd_pubkey_t const *)acct->meta->owner;
271 198 : }
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 1473 : fd_txn_account_get_data( fd_txn_account_t const * acct ) {
280 1473 : return acct->data;
281 1473 : }
282 :
283 : uchar *
284 36 : fd_txn_account_get_data_mut( fd_txn_account_t const * acct ) {
285 36 : return acct->data;
286 36 : }
287 :
288 : ulong
289 555 : fd_txn_account_get_data_len( fd_txn_account_t const * acct ) {
290 555 : if( FD_UNLIKELY( !acct->meta ) ) {
291 0 : FD_LOG_CRIT(( "account is not setup" ));
292 0 : }
293 555 : return acct->meta->dlen;
294 555 : }
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 33 : fd_txn_account_set_executable( fd_txn_account_t * acct, int is_executable ) {
331 33 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
332 0 : FD_LOG_CRIT(( "account is not mutable" ));
333 0 : }
334 33 : if( FD_UNLIKELY( !acct->meta ) ) {
335 0 : FD_LOG_CRIT(( "account is not setup" ));
336 0 : }
337 33 : acct->meta->executable = !!is_executable;
338 33 : }
339 :
340 : void
341 507 : fd_txn_account_set_owner( fd_txn_account_t * acct, fd_pubkey_t const * owner ) {
342 507 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
343 0 : FD_LOG_CRIT(( "account is not mutable" ));
344 0 : }
345 507 : if( FD_UNLIKELY( !acct->meta ) ) {
346 0 : FD_LOG_CRIT(( "account is not setup" ));
347 0 : }
348 507 : fd_memcpy( acct->meta->owner, owner, sizeof(fd_pubkey_t) );
349 507 : }
350 :
351 : void
352 498 : fd_txn_account_set_lamports( fd_txn_account_t * acct, ulong lamports ) {
353 498 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
354 0 : FD_LOG_CRIT(( "account is not mutable" ));
355 0 : }
356 498 : if( FD_UNLIKELY( !acct->meta ) ) {
357 0 : FD_LOG_CRIT(( "account is not setup" ));
358 0 : }
359 498 : acct->meta->lamports = lamports;
360 498 : }
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 15 : fd_txn_account_set_data_len( fd_txn_account_t * acct, ulong data_len ) {
404 15 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
405 0 : FD_LOG_CRIT(( "account is not mutable" ));
406 0 : }
407 15 : if( FD_UNLIKELY( !acct->meta ) ) {
408 0 : FD_LOG_CRIT(( "account is not setup" ));
409 0 : }
410 15 : acct->meta->dlen = (uint)data_len;
411 15 : }
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 : }
491 :
492 : fd_solana_account_meta_t
493 0 : fd_txn_account_get_solana_meta( fd_txn_account_t const * acct ) {
494 0 : fd_solana_account_meta_t meta = {
495 0 : .lamports = acct->meta->lamports,
496 : .rent_epoch = ULONG_MAX,
497 0 : .executable = acct->meta->executable,
498 0 : };
499 0 : memcpy( meta.owner, acct->meta->owner, sizeof(fd_pubkey_t) );
500 0 : return meta;
501 0 : }
|