Line data Source code
1 : #include "fd_txn_account.h"
2 : #include "fd_runtime.h"
3 :
4 : fd_txn_account_t *
5 102 : fd_txn_account_init( void * ptr ) {
6 102 : if( FD_UNLIKELY( !ptr ) ) {
7 0 : FD_LOG_WARNING(( "NULL ptr" ));
8 0 : return NULL;
9 0 : }
10 :
11 102 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)ptr, alignof(fd_txn_account_t) ) ) ) {
12 0 : FD_LOG_WARNING(( "misaligned ptr" ));
13 0 : return NULL;
14 0 : }
15 :
16 102 : memset( ptr, 0, FD_TXN_ACCOUNT_FOOTPRINT );
17 :
18 102 : fd_txn_account_t * ret = (fd_txn_account_t *)ptr;
19 102 : ret->private_state.meta_gaddr = 0UL;
20 102 : ret->private_state.data_gaddr = 0UL;
21 102 : ret->starting_dlen = ULONG_MAX;
22 102 : ret->starting_lamports = ULONG_MAX;
23 :
24 102 : ret->private_state.const_data = NULL;
25 102 : ret->private_state.const_meta = NULL;
26 102 : ret->private_state.meta = NULL;
27 102 : ret->private_state.data = NULL;
28 :
29 : /* Defaults to writable vtable */
30 102 : ret->vt = &fd_txn_account_writable_vtable;
31 :
32 102 : FD_COMPILER_MFENCE();
33 102 : ret->magic = FD_TXN_ACCOUNT_MAGIC;
34 102 : FD_COMPILER_MFENCE();
35 :
36 102 : return ret;
37 102 : }
38 :
39 : /* A common setup helper function that sets
40 : default values for the txn account */
41 : void
42 45 : fd_txn_account_setup_common( fd_txn_account_t * acct ) {
43 45 : fd_account_meta_t const * meta = acct->private_state.const_meta ?
44 45 : acct->private_state.const_meta : acct->private_state.meta;
45 :
46 : /* TODO: Why ULONG_MAX check here? */
47 45 : if( ULONG_MAX == acct->starting_dlen ) {
48 45 : acct->starting_dlen = meta->dlen;
49 45 : }
50 :
51 45 : if( ULONG_MAX == acct->starting_lamports ) {
52 45 : acct->starting_lamports = meta->info.lamports;
53 45 : }
54 45 : }
55 :
56 : void
57 : fd_txn_account_init_from_meta_and_data_mutable( fd_txn_account_t * acct,
58 : fd_account_meta_t * meta,
59 0 : uchar * data ) {
60 0 : acct->private_state.const_data = data;
61 0 : acct->private_state.const_meta = meta;
62 0 : acct->private_state.data = data;
63 0 : acct->private_state.meta = meta;
64 0 : acct->vt = &fd_txn_account_writable_vtable;
65 0 : }
66 :
67 : void
68 : fd_txn_account_init_from_meta_and_data_readonly( fd_txn_account_t * acct,
69 : fd_account_meta_t const * meta,
70 0 : uchar const * data ) {
71 :
72 0 : acct->private_state.const_data = data;
73 0 : acct->private_state.const_meta = meta;
74 0 : acct->vt = &fd_txn_account_readonly_vtable;
75 0 : }
76 :
77 : void
78 : fd_txn_account_setup_sentinel_meta_readonly( fd_txn_account_t * acct,
79 : fd_spad_t * spad,
80 0 : fd_wksp_t * spad_wksp ) {
81 :
82 0 : fd_account_meta_t * sentinel = fd_spad_alloc( spad, FD_ACCOUNT_REC_ALIGN, sizeof(fd_account_meta_t) );
83 0 : fd_memset( sentinel, 0, sizeof(fd_account_meta_t) );
84 :
85 0 : sentinel->magic = FD_ACCOUNT_META_MAGIC;
86 0 : sentinel->info.rent_epoch = ULONG_MAX;
87 0 : acct->private_state.const_meta = sentinel;
88 0 : acct->starting_lamports = 0UL;
89 0 : acct->starting_dlen = 0UL;
90 0 : acct->private_state.meta_gaddr = fd_wksp_gaddr( spad_wksp, sentinel );
91 0 : }
92 :
93 : void
94 : fd_txn_account_setup_meta_mutable( fd_txn_account_t * acct,
95 : fd_spad_t * spad,
96 0 : ulong sz ) {
97 0 : fd_account_meta_t * meta = fd_spad_alloc( spad, alignof(fd_account_meta_t), sizeof(fd_account_meta_t) + sz );
98 0 : void * data = (uchar *)meta + sizeof(fd_account_meta_t);
99 :
100 0 : acct->private_state.const_meta = acct->private_state.meta = meta;
101 0 : acct->private_state.const_data = acct->private_state.data = data;
102 0 : acct->vt = &fd_txn_account_writable_vtable;
103 0 : }
104 :
105 : void
106 : fd_txn_account_setup_readonly( fd_txn_account_t * acct,
107 : fd_pubkey_t const * pubkey,
108 15 : fd_account_meta_t const * meta ) {
109 15 : fd_memcpy(acct->pubkey, pubkey, sizeof(fd_pubkey_t));
110 :
111 : /* We don't copy the metadata into a buffer here, because we assume
112 : that we are holding read locks on the account, because we are inside
113 : a transaction. */
114 15 : acct->private_state.const_meta = meta;
115 15 : acct->private_state.const_data = (uchar const *)meta + meta->hlen;
116 15 : acct->vt = &fd_txn_account_readonly_vtable;
117 :
118 15 : fd_txn_account_setup_common( acct );
119 15 : }
120 :
121 : void
122 : fd_txn_account_setup_mutable( fd_txn_account_t * acct,
123 : fd_pubkey_t const * pubkey,
124 30 : fd_account_meta_t * meta ) {
125 30 : fd_memcpy(acct->pubkey, pubkey, sizeof(fd_pubkey_t));
126 :
127 30 : acct->private_state.const_rec = acct->private_state.rec;
128 30 : acct->private_state.const_meta = acct->private_state.meta = meta;
129 30 : acct->private_state.const_data = acct->private_state.data = (uchar *)meta + meta->hlen;
130 30 : acct->vt = &fd_txn_account_writable_vtable;
131 :
132 30 : fd_txn_account_setup_common( acct );
133 30 : }
134 :
135 : /* Operators impl */
136 :
137 : /* Internal helper to initialize account data */
138 : uchar *
139 0 : fd_txn_account_init_data( fd_txn_account_t * acct, void * buf ) {
140 : /* Assumes that buf is pointing to account data */
141 0 : uchar * new_raw_data = (uchar *)buf;
142 0 : ulong dlen = ( acct->private_state.const_meta != NULL ) ? acct->private_state.const_meta->dlen : 0;
143 :
144 0 : if( acct->private_state.const_meta != NULL ) {
145 0 : fd_memcpy( new_raw_data, (uchar *)acct->private_state.const_meta, sizeof(fd_account_meta_t)+dlen );
146 0 : } else {
147 : /* Account did not exist, set up metadata */
148 0 : fd_account_meta_init( (fd_account_meta_t *)new_raw_data );
149 0 : }
150 :
151 0 : return new_raw_data;
152 0 : }
153 :
154 : fd_txn_account_t *
155 : fd_txn_account_make_mutable( fd_txn_account_t * acct,
156 : void * buf,
157 0 : fd_wksp_t * wksp ) {
158 0 : if( FD_UNLIKELY( acct->private_state.data != NULL ) ) {
159 0 : FD_LOG_ERR(( "borrowed account is already mutable" ));
160 0 : }
161 :
162 0 : ulong dlen = ( acct->private_state.const_meta != NULL ) ? acct->private_state.const_meta->dlen : 0UL;
163 0 : uchar * new_raw_data = fd_txn_account_init_data( acct, buf );
164 :
165 0 : acct->private_state.const_meta = acct->private_state.meta = (fd_account_meta_t *)new_raw_data;
166 0 : acct->private_state.const_data = acct->private_state.data = new_raw_data + sizeof(fd_account_meta_t);
167 0 : acct->private_state.meta->dlen = dlen;
168 :
169 : /* update global addresses of meta and data after copying into buffer */
170 0 : acct->private_state.meta_gaddr = fd_wksp_gaddr( wksp, acct->private_state.meta );
171 0 : acct->private_state.data_gaddr = fd_wksp_gaddr( wksp, acct->private_state.data );
172 0 : acct->vt = &fd_txn_account_writable_vtable;
173 :
174 0 : return acct;
175 0 : }
176 :
177 : /* Factory constructors from funk */
178 :
179 : int
180 : fd_txn_account_init_from_funk_readonly( fd_txn_account_t * acct,
181 : fd_pubkey_t const * pubkey,
182 : fd_funk_t const * funk,
183 18 : fd_funk_txn_t const * funk_txn ) {
184 18 : fd_txn_account_init( acct );
185 :
186 18 : int err = FD_ACC_MGR_SUCCESS;
187 18 : fd_account_meta_t const * meta = fd_funk_get_acc_meta_readonly( funk,
188 18 : funk_txn,
189 18 : pubkey,
190 18 : &acct->private_state.const_rec,
191 18 : &err,
192 18 : NULL );
193 :
194 18 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
195 3 : return err;
196 3 : }
197 :
198 15 : if( FD_UNLIKELY( !fd_account_meta_exists( meta ) ) ) {
199 0 : return FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
200 0 : }
201 :
202 15 : if( FD_UNLIKELY( acct->magic!=FD_TXN_ACCOUNT_MAGIC ) ) {
203 0 : return FD_ACC_MGR_ERR_WRONG_MAGIC;
204 0 : }
205 :
206 : /* setup global addresses of meta and data for exec and replay tile sharing */
207 15 : fd_wksp_t * funk_wksp = fd_funk_wksp( funk );
208 15 : acct->private_state.meta_gaddr = fd_wksp_gaddr( funk_wksp, acct->private_state.const_meta );
209 15 : acct->private_state.data_gaddr = fd_wksp_gaddr( funk_wksp, acct->private_state.const_data );
210 :
211 15 : fd_txn_account_setup_readonly( acct, pubkey, meta );
212 :
213 15 : return FD_ACC_MGR_SUCCESS;
214 15 : }
215 :
216 : int
217 : fd_txn_account_init_from_funk_mutable( fd_txn_account_t * acct,
218 : fd_pubkey_t const * pubkey,
219 : fd_funk_t * funk,
220 : fd_funk_txn_t * funk_txn,
221 : int do_create,
222 30 : ulong min_data_sz ) {
223 30 : fd_txn_account_init( acct );
224 :
225 30 : fd_funk_rec_prepare_t prepare = {0};
226 30 : int err = FD_ACC_MGR_SUCCESS;
227 30 : fd_account_meta_t * meta = fd_funk_get_acc_meta_mutable( funk,
228 30 : funk_txn,
229 30 : pubkey,
230 30 : do_create,
231 30 : min_data_sz,
232 30 : &acct->private_state.rec,
233 30 : &prepare,
234 30 : &err );
235 :
236 30 : if( FD_UNLIKELY( !meta ) ) {
237 0 : return err;
238 0 : }
239 :
240 30 : if( FD_UNLIKELY( meta->magic!=FD_ACCOUNT_META_MAGIC ) ) {
241 0 : return FD_ACC_MGR_ERR_WRONG_MAGIC;
242 0 : }
243 :
244 : /* exec tile should never call this function, so the global addresses of
245 : meta and data should never be used. Instead, populate the prepared_rec
246 : field so that any created records can be published with fd_txn_account_mutable_fini. */
247 30 : acct->prepared_rec = prepare;
248 30 : fd_txn_account_setup_mutable( acct, pubkey, meta );
249 :
250 : /* trigger a segfault if the exec tile calls this function,
251 : as funk will be mapped read-only */
252 30 : acct->private_state.data[0] = acct->private_state.data[0];
253 :
254 30 : return FD_ACC_MGR_SUCCESS;
255 30 : }
256 :
257 : /* Funk save function impl */
258 :
259 : int
260 : fd_txn_account_save_internal( fd_txn_account_t * acct,
261 0 : fd_funk_t * funk ) {
262 0 : if( acct->private_state.rec == NULL ) {
263 0 : return FD_ACC_MGR_ERR_WRITE_FAILED;
264 0 : }
265 :
266 0 : fd_wksp_t * wksp = fd_funk_wksp( funk );
267 0 : ulong reclen = sizeof(fd_account_meta_t)+acct->private_state.const_meta->dlen;
268 0 : uchar * raw = fd_funk_val( acct->private_state.rec, wksp );
269 0 : fd_memcpy( raw, acct->private_state.meta, reclen );
270 :
271 0 : return FD_ACC_MGR_SUCCESS;
272 0 : }
273 :
274 : int
275 : fd_txn_account_save( fd_txn_account_t * acct,
276 : fd_funk_t * funk,
277 : fd_funk_txn_t * txn,
278 0 : fd_wksp_t * acc_data_wksp ) {
279 0 : acct->private_state.meta = fd_wksp_laddr( acc_data_wksp, acct->private_state.meta_gaddr );
280 0 : acct->private_state.data = fd_wksp_laddr( acc_data_wksp, acct->private_state.data_gaddr );
281 :
282 0 : if( acct->private_state.meta == NULL ) {
283 : /* The meta is NULL so the account is not writable. */
284 0 : FD_LOG_DEBUG(( "fd_txn_account_save: account is not writable: %s", FD_BASE58_ENC_32_ALLOCA( acct->pubkey ) ));
285 0 : return FD_ACC_MGR_ERR_WRITE_FAILED;
286 0 : }
287 :
288 0 : acct->private_state.const_meta = acct->private_state.meta;
289 0 : acct->private_state.const_data = acct->private_state.data;
290 :
291 0 : fd_funk_rec_key_t key = fd_funk_acc_key( acct->pubkey );
292 :
293 : /* Remove previous incarnation of the account's record from the transaction, so that we don't hash it twice */
294 0 : fd_funk_rec_hard_remove( funk, txn, &key );
295 :
296 0 : int err;
297 0 : fd_funk_rec_prepare_t prepare[1];
298 0 : fd_funk_rec_t * rec = fd_funk_rec_prepare( funk, txn, &key, prepare, &err );
299 0 : if( rec == NULL ) FD_LOG_ERR(( "unable to insert a new record, error %d", err ));
300 :
301 0 : acct->private_state.rec = rec;
302 0 : ulong reclen = sizeof(fd_account_meta_t)+acct->private_state.const_meta->dlen;
303 0 : fd_wksp_t * wksp = fd_funk_wksp( funk );
304 0 : if( fd_funk_val_truncate(
305 0 : rec,
306 0 : fd_funk_alloc( funk ),
307 0 : wksp,
308 0 : 0UL,
309 0 : reclen,
310 0 : &err ) == NULL ) {
311 0 : FD_LOG_ERR(( "fd_funk_val_truncate(sz=%lu) for account failed (%i-%s)", reclen, err, fd_funk_strerror( err ) ));
312 0 : }
313 0 : err = fd_txn_account_save_internal( acct, funk );
314 0 : if( FD_UNLIKELY( err ) ) {
315 0 : FD_LOG_ERR(( "fd_txn_account_save_internal() failed (%i-%s)", err, fd_funk_strerror( err ) ));
316 0 : }
317 :
318 0 : fd_funk_rec_publish( funk, prepare );
319 :
320 0 : return err;
321 0 : }
322 :
323 : void
324 : fd_txn_account_mutable_fini( fd_txn_account_t * acct,
325 : fd_funk_t * funk,
326 30 : fd_funk_txn_t * txn ) {
327 30 : fd_funk_rec_query_t query[1];
328 :
329 30 : fd_funk_rec_key_t key = fd_funk_acc_key( acct->pubkey );
330 30 : fd_funk_rec_t * rec = (fd_funk_rec_t *)fd_funk_rec_query_try( funk, txn, &key, query );
331 :
332 : /* Check that the prepared record is still valid -
333 : if these invariants are broken something is very wrong. */
334 30 : if( acct->prepared_rec.rec ) {
335 : /* Check that the prepared record is not the Funk null value */
336 24 : if( !acct->prepared_rec.rec->val_gaddr ) {
337 0 : FD_LOG_ERR(( "invalid prepared record for %s: unexpected NULL funk record value. the record might have been modified by another thread",
338 0 : FD_BASE58_ENC_32_ALLOCA( acct->pubkey ) ));
339 0 : }
340 :
341 : /* Ensure that the prepared record key still matches our key. */
342 24 : if( FD_UNLIKELY( memcmp( acct->prepared_rec.rec->pair.key, &key, sizeof(fd_funk_rec_key_t) )!=0 ) ) {
343 0 : FD_LOG_ERR(( "invalid prepared record for %s: the record might have been modified by another thread",
344 0 : FD_BASE58_ENC_32_ALLOCA( acct->pubkey ) ));
345 0 : }
346 24 : }
347 :
348 : /* We have a prepared record, but a record already exists funk */
349 30 : if( rec!=NULL && acct->prepared_rec.rec!=NULL ) {
350 0 : FD_LOG_ERR(( "invalid prepared record for %s: trying to publish new record that is already present",
351 0 : FD_BASE58_ENC_32_ALLOCA( acct->pubkey ) ));
352 0 : }
353 :
354 : /* Publish the record if the record is not in the current funk transaction
355 : and there exists a record in preparation in the fd_txn_account_t object */
356 30 : if( rec==NULL && acct->prepared_rec.rec!=NULL ) {
357 24 : fd_funk_rec_publish( funk, &acct->prepared_rec );
358 24 : }
359 30 : }
360 :
361 : /* read/write mutual exclusion */
362 :
363 : FD_FN_PURE int
364 0 : fd_txn_account_acquire_write_is_safe( fd_txn_account_t const * acct ) {
365 0 : return (!acct->private_state.refcnt_excl);
366 0 : }
367 :
368 : /* fd_txn_account_acquire_write acquires write/exclusive access.
369 : Causes all other write or read acquire attempts will fail. Returns 1
370 : on success, 0 on failure.
371 :
372 : Mirrors a try_borrow_mut() call in Agave. */
373 : int
374 0 : fd_txn_account_acquire_write( fd_txn_account_t * acct ) {
375 0 : if( FD_UNLIKELY( !fd_txn_account_acquire_write_is_safe( acct ) ) ) {
376 0 : return 0;
377 0 : }
378 0 : acct->private_state.refcnt_excl = (ushort)1;
379 0 : return 1;
380 0 : }
381 :
382 : /* fd_txn_account_release_write{_private} releases a write/exclusive
383 : access handle. The private version should only be used by fd_borrowed_account_drop
384 : and fd_borrowed_account_destroy. */
385 : void
386 0 : fd_txn_account_release_write( fd_txn_account_t * acct ) {
387 0 : FD_TEST( acct->private_state.refcnt_excl==1U );
388 0 : acct->private_state.refcnt_excl = (ushort)0;
389 0 : }
390 :
391 : void
392 0 : fd_txn_account_release_write_private( fd_txn_account_t * acct ) {
393 : /* Only release if it is not yet released */
394 0 : if( !fd_txn_account_acquire_write_is_safe( acct ) ) {
395 0 : fd_txn_account_release_write( acct );
396 0 : }
397 0 : }
398 :
399 : /* Vtable API Impls */
400 :
401 : fd_account_meta_t const *
402 0 : fd_txn_account_get_acc_meta( fd_txn_account_t const * acct ) {
403 0 : return acct->private_state.const_meta;
404 0 : }
405 :
406 : uchar const *
407 12 : fd_txn_account_get_acc_data( fd_txn_account_t const * acct ) {
408 12 : return acct->private_state.const_data;
409 12 : }
410 :
411 : fd_funk_rec_t const *
412 0 : fd_txn_account_get_acc_rec( fd_txn_account_t const * acct ) {
413 0 : return acct->private_state.const_rec;
414 0 : }
415 :
416 : uchar *
417 18 : fd_txn_account_get_acc_data_mut_writable( fd_txn_account_t const * acct ) {
418 18 : return acct->private_state.data;
419 18 : }
420 :
421 : void
422 : fd_txn_account_set_meta_readonly( fd_txn_account_t * acct,
423 0 : fd_account_meta_t const * meta ) {
424 0 : acct->private_state.const_meta = meta;
425 0 : }
426 :
427 : void
428 : fd_txn_account_set_meta_mutable_writable( fd_txn_account_t * acct,
429 0 : fd_account_meta_t * meta ) {
430 0 : acct->private_state.const_meta = acct->private_state.meta = meta;
431 0 : }
432 :
433 : ulong
434 12 : fd_txn_account_get_data_len( fd_txn_account_t const * acct ) {
435 12 : if( FD_UNLIKELY( !acct->private_state.const_meta ) ) FD_LOG_ERR(("account is not setup" ));
436 12 : return acct->private_state.const_meta->dlen;
437 12 : }
438 :
439 : int
440 0 : fd_txn_account_is_executable( fd_txn_account_t const * acct ) {
441 0 : if( FD_UNLIKELY( !acct->private_state.const_meta ) ) FD_LOG_ERR(("account is not setup" ));
442 0 : return !!acct->private_state.const_meta->info.executable;
443 0 : }
444 :
445 : fd_pubkey_t const *
446 51 : fd_txn_account_get_owner( fd_txn_account_t const * acct ) {
447 51 : if( FD_UNLIKELY( !acct->private_state.const_meta ) ) FD_LOG_ERR(("account is not setup" ));
448 51 : return (fd_pubkey_t const *)acct->private_state.const_meta->info.owner;
449 51 : }
450 :
451 : ulong
452 0 : fd_txn_account_get_lamports( fd_txn_account_t const * acct ) {
453 : /* (!const_meta_) considered an internal error */
454 0 : if( FD_UNLIKELY( !acct->private_state.const_meta ) ) return 0UL;
455 0 : return acct->private_state.const_meta->info.lamports;
456 0 : }
457 :
458 : ulong
459 0 : fd_txn_account_get_rent_epoch( fd_txn_account_t const * acct ) {
460 0 : if( FD_UNLIKELY( !acct->private_state.const_meta ) ) FD_LOG_ERR(("account is not setup" ));
461 0 : return acct->private_state.const_meta->info.rent_epoch;
462 0 : }
463 :
464 : fd_hash_t const *
465 0 : fd_txn_account_get_hash( fd_txn_account_t const * acct ) {
466 0 : if( FD_UNLIKELY( !acct->private_state.const_meta ) ) FD_LOG_ERR(("account is not setup" ));
467 0 : return (fd_hash_t const *)acct->private_state.const_meta->hash;
468 0 : }
469 :
470 : fd_solana_account_meta_t const *
471 0 : fd_txn_account_get_info( fd_txn_account_t const * acct ) {
472 0 : if( FD_UNLIKELY( !acct->private_state.const_meta ) ) FD_LOG_ERR(("account is not setup" ));
473 0 : return &acct->private_state.const_meta->info;
474 0 : }
475 :
476 : void
477 12 : fd_txn_account_set_executable_writable( fd_txn_account_t * acct, int is_executable ) {
478 12 : if( FD_UNLIKELY( !acct->private_state.meta ) ) FD_LOG_ERR(("account is not mutable" ));
479 12 : acct->private_state.meta->info.executable = !!is_executable;
480 12 : }
481 :
482 : void
483 12 : fd_txn_account_set_owner_writable( fd_txn_account_t * acct, fd_pubkey_t const * owner ) {
484 12 : if( FD_UNLIKELY( !acct->private_state.meta ) ) FD_LOG_ERR(("account is not mutable" ));
485 12 : fd_memcpy( acct->private_state.meta->info.owner, owner, sizeof(fd_pubkey_t) );
486 12 : }
487 :
488 : void
489 12 : fd_txn_account_set_lamports_writable( fd_txn_account_t * acct, ulong lamports ) {
490 12 : if( FD_UNLIKELY( !acct->private_state.meta ) ) FD_LOG_ERR(("account is not mutable" ));
491 12 : acct->private_state.meta->info.lamports = lamports;
492 12 : }
493 :
494 : int
495 0 : fd_txn_account_checked_add_lamports_writable( fd_txn_account_t * acct, ulong lamports ) {
496 0 : ulong balance_post = 0UL;
497 0 : int err = fd_ulong_checked_add( acct->vt->get_lamports( acct ), lamports, &balance_post );
498 0 : if( FD_UNLIKELY( err ) ) {
499 0 : return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
500 0 : }
501 :
502 0 : acct->vt->set_lamports( acct, balance_post );
503 0 : return FD_EXECUTOR_INSTR_SUCCESS;
504 0 : }
505 :
506 : int
507 0 : fd_txn_account_checked_sub_lamports_writable( fd_txn_account_t * acct, ulong lamports ) {
508 0 : ulong balance_post = 0UL;
509 0 : int err = fd_ulong_checked_sub( acct->vt->get_lamports( acct ),
510 0 : lamports,
511 0 : &balance_post );
512 0 : if( FD_UNLIKELY( err ) ) {
513 0 : return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
514 0 : }
515 :
516 0 : acct->vt->set_lamports( acct, balance_post );
517 0 : return FD_EXECUTOR_INSTR_SUCCESS;
518 0 : }
519 :
520 : void
521 12 : fd_txn_account_set_rent_epoch_writable( fd_txn_account_t * acct, ulong rent_epoch ) {
522 12 : if( FD_UNLIKELY( !acct->private_state.meta ) ) FD_LOG_ERR(("account is not mutable" ));
523 12 : acct->private_state.meta->info.rent_epoch = rent_epoch;
524 12 : }
525 :
526 : void
527 : fd_txn_account_set_data_writable( fd_txn_account_t * acct,
528 : void const * data,
529 12 : ulong data_sz) {
530 12 : if( FD_UNLIKELY( !acct->private_state.meta ) ) FD_LOG_ERR(("account is not mutable" ));
531 12 : acct->private_state.meta->dlen = data_sz;
532 12 : fd_memcpy( acct->private_state.data, data, data_sz );
533 12 : }
534 :
535 : void
536 : fd_txn_account_set_data_len_writable( fd_txn_account_t * acct,
537 18 : ulong data_len ) {
538 18 : if( FD_UNLIKELY( !acct->private_state.meta ) ) FD_LOG_ERR(("account is not mutable" ));
539 18 : acct->private_state.meta->dlen = data_len;
540 18 : }
541 :
542 : void
543 : fd_txn_account_set_slot_writable( fd_txn_account_t * acct,
544 18 : ulong slot ) {
545 18 : if( FD_UNLIKELY( !acct->private_state.meta ) ) FD_LOG_ERR(("account is not mutable" ));
546 18 : acct->private_state.meta->slot = slot;
547 18 : }
548 :
549 : void
550 : fd_txn_account_set_hash_writable( fd_txn_account_t * acct,
551 18 : fd_hash_t const * hash ) {
552 18 : if( FD_UNLIKELY( !acct->private_state.meta ) ) FD_LOG_ERR(("account is not mutable" ));
553 18 : memcpy( acct->private_state.meta->hash, hash->hash, sizeof(fd_hash_t) );
554 18 : }
555 :
556 : void
557 0 : fd_txn_account_clear_owner_writable( fd_txn_account_t * acct ) {
558 0 : if( FD_UNLIKELY( !acct->private_state.meta ) ) FD_LOG_ERR(("account is not mutable" ));
559 0 : fd_memset( acct->private_state.meta->info.owner, 0, sizeof(fd_pubkey_t) );
560 0 : }
561 :
562 : void
563 : fd_txn_account_set_meta_info_writable( fd_txn_account_t * acct,
564 18 : fd_solana_account_meta_t const * info ) {
565 18 : if( FD_UNLIKELY( !acct->private_state.meta ) ) FD_LOG_ERR(("account is not mutable" ));
566 18 : acct->private_state.meta->info = *info;
567 18 : }
568 :
569 : void
570 : fd_txn_account_resize_writable( fd_txn_account_t * acct,
571 0 : ulong dlen ) {
572 0 : if( FD_UNLIKELY( !acct->private_state.meta ) ) FD_LOG_ERR(("account is not mutable" ));
573 : /* Because the memory for an account is preallocated for the transaction
574 : up to the max account size, we only need to zero out bytes (for the case
575 : where the account grew) and update the account dlen. */
576 0 : ulong old_sz = acct->private_state.meta->dlen;
577 0 : ulong new_sz = dlen;
578 0 : ulong memset_sz = fd_ulong_sat_sub( new_sz, old_sz );
579 0 : fd_memset( acct->private_state.data+old_sz, 0, memset_sz );
580 :
581 0 : acct->private_state.meta->dlen = dlen;
582 0 : }
583 :
584 : uchar *
585 0 : fd_txn_account_get_acc_data_mut_readonly( fd_txn_account_t const * acct FD_PARAM_UNUSED ) {
586 0 : FD_LOG_ERR(( "account is not mutable" ));
587 0 : return NULL;
588 0 : }
589 :
590 : void
591 : fd_txn_account_set_meta_mutable_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED,
592 0 : fd_account_meta_t * meta FD_PARAM_UNUSED ) {
593 0 : FD_LOG_ERR(( "cannot set meta as mutable in a readonly account!" ));
594 0 : }
595 :
596 : void
597 : fd_txn_account_set_executable_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED,
598 0 : int is_executable FD_PARAM_UNUSED ) {
599 0 : FD_LOG_ERR(( "cannot set executable in a readonly account!" ));
600 0 : }
601 :
602 : void
603 : fd_txn_account_set_owner_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED,
604 0 : fd_pubkey_t const * owner FD_PARAM_UNUSED ) {
605 0 : FD_LOG_ERR(( "cannot set owner in a readonly account!" ));
606 0 : }
607 :
608 : void
609 : fd_txn_account_set_lamports_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED,
610 0 : ulong lamports FD_PARAM_UNUSED ) {
611 0 : FD_LOG_ERR(( "cannot set lamports in a readonly account!" ));
612 0 : }
613 :
614 : int
615 : fd_txn_account_checked_add_lamports_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED,
616 0 : ulong lamports FD_PARAM_UNUSED ) {
617 0 : FD_LOG_ERR(( "cannot do a checked add to lamports in a readonly account!" ));
618 0 : return FD_EXECUTOR_INSTR_SUCCESS;
619 0 : }
620 :
621 : int
622 : fd_txn_account_checked_sub_lamports_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED,
623 0 : ulong lamports FD_PARAM_UNUSED ) {
624 0 : FD_LOG_ERR(( "cannot do a checked sub to lamports in a readonly account!" ));
625 0 : return FD_EXECUTOR_INSTR_SUCCESS;
626 0 : }
627 :
628 : void
629 : fd_txn_account_set_rent_epoch_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED,
630 0 : ulong rent_epoch FD_PARAM_UNUSED ) {
631 0 : FD_LOG_ERR(( "cannot set rent epoch in a readonly account!" ));
632 0 : }
633 :
634 : void
635 : fd_txn_account_set_data_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED,
636 : void const * data FD_PARAM_UNUSED,
637 0 : ulong data_sz FD_PARAM_UNUSED ) {
638 0 : FD_LOG_ERR(( "cannot set data in a readonly account!" ));
639 0 : }
640 :
641 : void
642 : fd_txn_account_set_data_len_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED,
643 0 : ulong data_len FD_PARAM_UNUSED ) {
644 0 : FD_LOG_ERR(( "cannot set data_len in a readonly account!" ));
645 0 : }
646 :
647 : void
648 : fd_txn_account_set_slot_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED,
649 0 : ulong slot FD_PARAM_UNUSED ) {
650 0 : FD_LOG_ERR(("cannot set slot in a readonly account!"));
651 0 : }
652 :
653 : void
654 : fd_txn_account_set_hash_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED,
655 0 : fd_hash_t const * hash FD_PARAM_UNUSED ) {
656 0 : FD_LOG_ERR(("cannot set hash in a readonly account!"));
657 0 : }
658 :
659 : void
660 0 : fd_txn_account_clear_owner_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED ) {
661 0 : FD_LOG_ERR(("cannot clear owner in a readonly account!"));
662 0 : }
663 :
664 : void
665 : fd_txn_account_set_meta_info_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED,
666 0 : fd_solana_account_meta_t const * info FD_PARAM_UNUSED ) {
667 0 : FD_LOG_ERR(("cannot set meta info in a readonly account!"));
668 0 : }
669 :
670 : void
671 : fd_txn_account_resize_readonly( fd_txn_account_t * acct FD_PARAM_UNUSED,
672 0 : ulong dlen FD_PARAM_UNUSED ) {
673 0 : FD_LOG_ERR(( "cannot resize a readonly account!" ));
674 0 : }
675 :
676 : ushort
677 0 : fd_txn_account_is_borrowed( fd_txn_account_t const * acct ) {
678 0 : return !!acct->private_state.refcnt_excl;
679 0 : }
680 :
681 : int
682 0 : fd_txn_account_is_mutable( fd_txn_account_t const * acct ) {
683 : /* A txn account is mutable if meta is non NULL */
684 0 : return acct->private_state.meta != NULL;
685 0 : }
686 :
687 : int
688 0 : fd_txn_account_is_readonly( fd_txn_account_t const * acct ) {
689 : /* A txn account is readonly if only the const_meta_ field is non NULL */
690 0 : return acct->private_state.const_meta!=NULL && acct->private_state.meta==NULL;
691 0 : }
692 :
693 : int
694 0 : fd_txn_account_try_borrow_mut( fd_txn_account_t * acct ) {
695 0 : return fd_txn_account_acquire_write( acct );
696 0 : }
697 :
698 : void
699 0 : fd_txn_account_drop( fd_txn_account_t * acct ) {
700 0 : fd_txn_account_release_write_private( acct );
701 0 : }
702 :
703 : void
704 12 : fd_txn_account_set_readonly( fd_txn_account_t * acct ) {
705 12 : acct->private_state.meta = NULL;
706 12 : acct->private_state.data = NULL;
707 12 : acct->private_state.rec = NULL;
708 12 : acct->vt = &fd_txn_account_readonly_vtable;
709 12 : }
710 :
711 : void
712 0 : fd_txn_account_set_mutable( fd_txn_account_t * acct ) {
713 0 : acct->private_state.meta = (void *)acct->private_state.const_meta;
714 0 : acct->private_state.data = (void *)acct->private_state.const_data;
715 0 : acct->private_state.rec = (void *)acct->private_state.const_rec;
716 0 : acct->vt = &fd_txn_account_writable_vtable;
717 0 : }
718 :
719 : /* vtable definitions */
720 :
721 : #define FD_TXN_ACCOUNT_VTABLE_DEF( type ) \
722 : const fd_txn_account_vtable_t \
723 : fd_txn_account_##type##_vtable = { \
724 : .get_meta = fd_txn_account_get_acc_meta, \
725 : .get_data = fd_txn_account_get_acc_data, \
726 : .get_rec = fd_txn_account_get_acc_rec, \
727 : \
728 : .get_data_mut = fd_txn_account_get_acc_data_mut_##type, \
729 : \
730 : .set_meta_readonly = fd_txn_account_set_meta_readonly, \
731 : .set_meta_mutable = fd_txn_account_set_meta_mutable_##type, \
732 : \
733 : .get_data_len = fd_txn_account_get_data_len, \
734 : .is_executable = fd_txn_account_is_executable, \
735 : .get_owner = fd_txn_account_get_owner, \
736 : .get_lamports = fd_txn_account_get_lamports, \
737 : .get_rent_epoch = fd_txn_account_get_rent_epoch, \
738 : .get_hash = fd_txn_account_get_hash, \
739 : .get_info = fd_txn_account_get_info, \
740 : \
741 : .set_executable = fd_txn_account_set_executable_##type, \
742 : .set_owner = fd_txn_account_set_owner_##type, \
743 : .set_lamports = fd_txn_account_set_lamports_##type, \
744 : .checked_add_lamports = fd_txn_account_checked_add_lamports_##type, \
745 : .checked_sub_lamports = fd_txn_account_checked_sub_lamports_##type, \
746 : .set_rent_epoch = fd_txn_account_set_rent_epoch_##type, \
747 : .set_data = fd_txn_account_set_data_##type, \
748 : .set_data_len = fd_txn_account_set_data_len_##type, \
749 : .set_slot = fd_txn_account_set_slot_##type, \
750 : .set_hash = fd_txn_account_set_hash_##type, \
751 : .clear_owner = fd_txn_account_clear_owner_##type, \
752 : .set_info = fd_txn_account_set_meta_info_##type, \
753 : .resize = fd_txn_account_resize_##type, \
754 : \
755 : .is_borrowed = fd_txn_account_is_borrowed, \
756 : .is_mutable = fd_txn_account_is_mutable, \
757 : .is_readonly = fd_txn_account_is_readonly, \
758 : \
759 : .try_borrow_mut = fd_txn_account_try_borrow_mut, \
760 : .drop = fd_txn_account_drop, \
761 : \
762 : .set_readonly = fd_txn_account_set_readonly, \
763 : .set_mutable = fd_txn_account_set_mutable \
764 : }
765 :
766 : FD_TXN_ACCOUNT_VTABLE_DEF( readonly );
767 : FD_TXN_ACCOUNT_VTABLE_DEF( writable );
768 :
769 : #undef FD_TXN_ACCOUNT_VTABLE_DEF
|