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 : uchar * data = (uchar *)meta + sizeof(fd_account_meta_t);
39 :
40 1017 : txn_account->meta_soff = (long)( (ulong)meta - (ulong)mem );
41 :
42 1017 : txn_account->meta = meta;
43 1017 : txn_account->data = data;
44 1017 : txn_account->is_mutable = is_mutable;
45 :
46 1017 : return mem;
47 1020 : }
48 :
49 : fd_txn_account_t *
50 558 : fd_txn_account_join( void * mem ) {
51 558 : if( FD_UNLIKELY( !mem ) ) {
52 0 : FD_LOG_WARNING(( "NULL mem" ));
53 0 : return NULL;
54 0 : }
55 :
56 558 : 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 558 : fd_txn_account_t * txn_account = (fd_txn_account_t *)mem;
62 :
63 558 : if( FD_UNLIKELY( txn_account->magic != FD_TXN_ACCOUNT_MAGIC ) ) {
64 3 : FD_LOG_WARNING(( "wrong magic" ));
65 3 : return NULL;
66 3 : }
67 :
68 555 : if( FD_UNLIKELY( txn_account->meta_soff==0UL ) ) {
69 0 : FD_LOG_CRIT(( "invalid meta_soff" ));
70 0 : }
71 :
72 555 : return txn_account;
73 555 : }
74 :
75 : void *
76 15 : fd_txn_account_leave( fd_txn_account_t * acct ) {
77 :
78 15 : if( FD_UNLIKELY( !acct ) ) {
79 3 : FD_LOG_WARNING(( "NULL acct" ));
80 3 : return NULL;
81 3 : }
82 :
83 12 : if( FD_UNLIKELY( acct->magic != FD_TXN_ACCOUNT_MAGIC ) ) {
84 0 : FD_LOG_WARNING(( "wrong magic" ));
85 0 : return NULL;
86 0 : }
87 :
88 12 : acct->meta = NULL;
89 12 : acct->data = NULL;
90 :
91 12 : return acct;
92 12 : }
93 :
94 : void *
95 9 : fd_txn_account_delete( void * mem ) {
96 9 : if( FD_UNLIKELY( !mem ) ) {
97 3 : FD_LOG_WARNING(( "NULL mem" ));
98 3 : return NULL;
99 3 : }
100 :
101 6 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, alignof(fd_txn_account_t) ) ) ) {
102 0 : FD_LOG_WARNING(( "misaligned mem" ));
103 0 : return NULL;
104 0 : }
105 :
106 6 : fd_txn_account_t * txn_account = (fd_txn_account_t *)mem;
107 :
108 6 : if( FD_UNLIKELY( txn_account->magic != FD_TXN_ACCOUNT_MAGIC ) ) {
109 0 : FD_LOG_WARNING(( "wrong magic" ));
110 0 : return NULL;
111 0 : }
112 :
113 6 : txn_account->magic = 0UL;
114 :
115 6 : return mem;
116 6 : }
117 :
118 : /* Factory constructors from funk */
119 :
120 : int
121 : fd_txn_account_init_from_funk_readonly( fd_txn_account_t * acct,
122 : fd_pubkey_t const * pubkey,
123 : fd_funk_t const * funk,
124 4137 : fd_funk_txn_xid_t const * xid ) {
125 4137 : fd_accdb_user_t accdb[1];
126 4137 : fd_accdb_user_v1_init( accdb, funk->shmem );
127 :
128 4137 : fd_accdb_ro_t ro[1];
129 4137 : if( FD_UNLIKELY( !fd_accdb_open_ro( accdb, ro, xid, pubkey ) ) ) {
130 3672 : fd_accdb_user_fini( accdb );
131 3672 : return FD_ACC_MGR_ERR_UNKNOWN_ACCOUNT;
132 3672 : }
133 :
134 : /* HACKY: Convert accdb_rw writable reference into txn_account.
135 : In the future, use fd_accdb_modify_publish instead */
136 465 : accdb->base.ro_active--;
137 465 : if( FD_UNLIKELY( !fd_txn_account_new(
138 465 : acct,
139 465 : pubkey,
140 465 : (fd_account_meta_t *)ro->meta,
141 465 : 0 ) ) ) {
142 0 : FD_LOG_CRIT(( "Failed to join txn account" ));
143 0 : }
144 465 : fd_accdb_user_fini( accdb );
145 :
146 465 : return FD_ACC_MGR_SUCCESS;
147 465 : }
148 :
149 : fd_account_meta_t *
150 : fd_txn_account_init_from_funk_mutable( fd_txn_account_t * acct,
151 : fd_pubkey_t const * pubkey,
152 : fd_accdb_user_t * accdb,
153 : fd_funk_txn_xid_t const * xid,
154 : int do_create,
155 : ulong min_data_sz,
156 498 : fd_funk_rec_prepare_t * prepare_out ) {
157 498 : memset( prepare_out, 0, sizeof(fd_funk_rec_prepare_t) );
158 :
159 498 : fd_accdb_rw_t rw[1];
160 498 : int flags = do_create ? FD_ACCDB_FLAG_CREATE : 0;
161 498 : if( FD_UNLIKELY( !fd_accdb_open_rw( accdb, rw, xid, pubkey->uc, min_data_sz, flags ) ) ) {
162 0 : return NULL;
163 0 : }
164 :
165 498 : if( FD_UNLIKELY( !fd_txn_account_join( fd_txn_account_new(
166 498 : acct,
167 498 : pubkey,
168 498 : (fd_account_meta_t *)rw->meta,
169 498 : 1 ) ) ) ) {
170 0 : FD_LOG_CRIT(( "Failed to join txn account" ));
171 0 : }
172 :
173 : /* HACKY: Convert accdb_rw writable reference into txn_account.
174 : In the future, use fd_accdb_modify_publish instead */
175 498 : accdb->base.rw_active--;
176 498 : fd_accdb_user_v1_t * accdb_v1 = fd_type_pun( accdb );
177 498 : fd_funk_txn_t * txn = accdb_v1->funk->txn_pool->ele + accdb_v1->tip_txn_idx;
178 498 : if( FD_UNLIKELY( !fd_funk_txn_xid_eq( &txn->xid, xid ) ) ) FD_LOG_CRIT(( "accdb_user corrupt: not joined to the expected transaction" ));
179 498 : if( !rw->published ) {
180 45 : *prepare_out = (fd_funk_rec_prepare_t) {
181 45 : .rec = rw->rec,
182 45 : .rec_head_idx = &txn->rec_head_idx,
183 45 : .rec_tail_idx = &txn->rec_tail_idx
184 45 : };
185 453 : } else {
186 453 : memset( prepare_out, 0, sizeof(fd_funk_rec_prepare_t) );
187 453 : }
188 :
189 498 : return rw->meta;
190 498 : }
191 :
192 : void
193 : fd_txn_account_mutable_fini( fd_txn_account_t * acct,
194 : fd_accdb_user_t * accdb,
195 498 : fd_funk_rec_prepare_t * prepare ) {
196 498 : fd_funk_rec_key_t key = fd_funk_acc_key( acct->pubkey );
197 498 : fd_funk_t * funk = fd_accdb_user_v1_funk( accdb );
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( funk, prepare );
219 45 : }
220 498 : }
221 :
222 : fd_pubkey_t const *
223 51 : fd_txn_account_get_owner( fd_txn_account_t const * acct ) {
224 51 : if( FD_UNLIKELY( !acct->meta ) ) {
225 0 : FD_LOG_CRIT(( "account is not setup" ));
226 0 : }
227 51 : return (fd_pubkey_t const *)acct->meta->owner;
228 51 : }
229 :
230 : fd_account_meta_t const *
231 465 : fd_txn_account_get_meta( fd_txn_account_t const * acct ) {
232 465 : return acct->meta;
233 465 : }
234 :
235 : uchar const *
236 975 : fd_txn_account_get_data( fd_txn_account_t const * acct ) {
237 975 : return acct->data;
238 975 : }
239 :
240 : uchar *
241 0 : fd_txn_account_get_data_mut( fd_txn_account_t const * acct ) {
242 0 : return acct->data;
243 0 : }
244 :
245 : ulong
246 519 : fd_txn_account_get_data_len( fd_txn_account_t const * acct ) {
247 519 : if( FD_UNLIKELY( !acct->meta ) ) {
248 0 : FD_LOG_CRIT(( "account is not setup" ));
249 0 : }
250 519 : return acct->meta->dlen;
251 519 : }
252 :
253 : int
254 0 : fd_txn_account_is_executable( fd_txn_account_t const * acct ) {
255 0 : if( FD_UNLIKELY( !acct->meta ) ) {
256 0 : FD_LOG_CRIT(( "account is not setup" ));
257 0 : }
258 0 : return !!acct->meta->executable;
259 0 : }
260 :
261 : ulong
262 936 : fd_txn_account_get_lamports( fd_txn_account_t const * acct ) {
263 936 : if( FD_UNLIKELY( !acct->meta ) ) {
264 0 : FD_LOG_CRIT(( "account is not setup" ));
265 0 : }
266 936 : return acct->meta->lamports;
267 936 : }
268 :
269 : void
270 0 : fd_txn_account_set_meta( fd_txn_account_t * acct, fd_account_meta_t * meta ) {
271 0 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
272 0 : FD_LOG_CRIT(( "account is not mutable" ));
273 0 : }
274 0 : if( FD_UNLIKELY( !meta ) ) {
275 0 : FD_LOG_CRIT(( "account is not setup" ));
276 0 : }
277 0 : acct->meta = meta;
278 0 : }
279 :
280 : void
281 36 : fd_txn_account_set_executable( fd_txn_account_t * acct, int is_executable ) {
282 36 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
283 0 : FD_LOG_CRIT(( "account is not mutable" ));
284 0 : }
285 36 : if( FD_UNLIKELY( !acct->meta ) ) {
286 0 : FD_LOG_CRIT(( "account is not setup" ));
287 0 : }
288 36 : acct->meta->executable = !!is_executable;
289 36 : }
290 :
291 : void
292 498 : fd_txn_account_set_owner( fd_txn_account_t * acct, fd_pubkey_t const * owner ) {
293 498 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
294 0 : FD_LOG_CRIT(( "account is not mutable" ));
295 0 : }
296 498 : if( FD_UNLIKELY( !acct->meta ) ) {
297 0 : FD_LOG_CRIT(( "account is not setup" ));
298 0 : }
299 498 : fd_memcpy( acct->meta->owner, owner, sizeof(fd_pubkey_t) );
300 498 : }
301 :
302 : void
303 501 : fd_txn_account_set_lamports( fd_txn_account_t * acct, ulong lamports ) {
304 501 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
305 0 : FD_LOG_CRIT(( "account is not mutable" ));
306 0 : }
307 501 : if( FD_UNLIKELY( !acct->meta ) ) {
308 0 : FD_LOG_CRIT(( "account is not setup" ));
309 0 : }
310 501 : acct->meta->lamports = lamports;
311 501 : }
312 :
313 : int
314 0 : fd_txn_account_checked_add_lamports( fd_txn_account_t * acct, ulong lamports ) {
315 0 : ulong balance_post = 0UL;
316 0 : int err = fd_ulong_checked_add( fd_txn_account_get_lamports( acct ), lamports, &balance_post );
317 0 : if( FD_UNLIKELY( err ) ) {
318 0 : return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
319 0 : }
320 :
321 0 : fd_txn_account_set_lamports( acct, balance_post );
322 0 : return FD_EXECUTOR_INSTR_SUCCESS;
323 0 : }
324 :
325 : int
326 0 : fd_txn_account_checked_sub_lamports( fd_txn_account_t * acct, ulong lamports ) {
327 0 : ulong balance_post = 0UL;
328 0 : int err = fd_ulong_checked_sub( fd_txn_account_get_lamports( acct ),
329 0 : lamports,
330 0 : &balance_post );
331 0 : if( FD_UNLIKELY( err ) ) {
332 0 : return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
333 0 : }
334 :
335 0 : fd_txn_account_set_lamports( acct, balance_post );
336 0 : return FD_EXECUTOR_INSTR_SUCCESS;
337 0 : }
338 :
339 : void
340 : fd_txn_account_set_data( fd_txn_account_t * acct,
341 : void const * data,
342 498 : ulong data_sz ) {
343 498 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
344 0 : FD_LOG_CRIT(( "account is not mutable" ));
345 0 : }
346 498 : if( FD_UNLIKELY( !acct->meta ) ) {
347 0 : FD_LOG_CRIT(( "account is not setup" ));
348 0 : }
349 498 : acct->meta->dlen = (uint)data_sz;
350 498 : fd_memcpy( acct->data, data, data_sz );
351 498 : }
352 :
353 : void
354 3 : fd_txn_account_set_data_len( fd_txn_account_t * acct, ulong data_len ) {
355 3 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
356 0 : FD_LOG_CRIT(( "account is not mutable" ));
357 0 : }
358 3 : if( FD_UNLIKELY( !acct->meta ) ) {
359 0 : FD_LOG_CRIT(( "account is not setup" ));
360 0 : }
361 3 : acct->meta->dlen = (uint)data_len;
362 3 : }
363 :
364 : void
365 462 : fd_txn_account_set_slot( fd_txn_account_t * acct, ulong slot ) {
366 462 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
367 0 : FD_LOG_CRIT(( "account is not mutable" ));
368 0 : }
369 462 : if( FD_UNLIKELY( !acct->meta ) ) {
370 0 : FD_LOG_CRIT(( "account is not setup" ));
371 0 : }
372 462 : acct->meta->slot = slot;
373 462 : }
374 :
375 : void
376 0 : fd_txn_account_clear_owner( fd_txn_account_t * acct ) {
377 0 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
378 0 : FD_LOG_CRIT(( "account is not mutable" ));
379 0 : }
380 0 : if( FD_UNLIKELY( !acct->meta ) ) {
381 0 : FD_LOG_CRIT(( "account is not setup" ));
382 0 : }
383 0 : fd_memset( acct->meta->owner, 0, sizeof(fd_pubkey_t) );
384 0 : }
385 :
386 : void
387 : fd_txn_account_resize( fd_txn_account_t * acct,
388 0 : ulong dlen ) {
389 0 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
390 0 : FD_LOG_CRIT(( "account is not mutable" ));
391 0 : }
392 0 : if( FD_UNLIKELY( !acct->meta ) ) {
393 0 : FD_LOG_CRIT(( "account is not setup" ));
394 0 : }
395 : /* Because the memory for an account is preallocated for the transaction
396 : up to the max account size, we only need to zero out bytes (for the case
397 : where the account grew) and update the account dlen. */
398 0 : ulong old_sz = acct->meta->dlen;
399 0 : ulong new_sz = dlen;
400 0 : ulong memset_sz = fd_ulong_sat_sub( new_sz, old_sz );
401 0 : fd_memset( acct->data+old_sz, 0, memset_sz );
402 :
403 0 : acct->meta->dlen = (uint)dlen;
404 0 : }
405 :
406 : int
407 6 : fd_txn_account_is_mutable( fd_txn_account_t const * acct ) {
408 : /* A txn account is mutable if meta is non NULL */
409 6 : return acct->is_mutable;
410 6 : }
411 :
412 : int
413 6 : fd_txn_account_is_readonly( fd_txn_account_t const * acct ) {
414 : /* A txn account is readonly if only the meta_ field is non NULL */
415 6 : return !acct->is_mutable;
416 6 : }
417 :
418 : void
419 0 : fd_txn_account_set_readonly( fd_txn_account_t * acct ) {
420 0 : acct->is_mutable = 0;
421 0 : }
422 :
423 : void
424 0 : fd_txn_account_set_mutable( fd_txn_account_t * acct ) {
425 0 : acct->is_mutable = 1;
426 0 : }
|