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 51 : int is_mutable ) {
12 51 : if( FD_UNLIKELY( !mem ) ) {
13 3 : FD_LOG_WARNING(( "NULL mem" ));
14 3 : return NULL;
15 3 : }
16 :
17 48 : 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 48 : if( FD_UNLIKELY( !pubkey ) ) {
23 3 : FD_LOG_WARNING(( "NULL pubkey" ));
24 3 : return NULL;
25 3 : }
26 :
27 45 : if( FD_UNLIKELY( !meta ) ) {
28 3 : FD_LOG_WARNING(( "NULL meta" ));
29 3 : return NULL;
30 3 : }
31 :
32 42 : fd_txn_account_t * txn_account = (fd_txn_account_t *)mem;
33 :
34 42 : fd_memcpy( txn_account->pubkey, pubkey, sizeof(fd_pubkey_t) );
35 :
36 42 : txn_account->magic = FD_TXN_ACCOUNT_MAGIC;
37 :
38 42 : uchar * data = (uchar *)meta + sizeof(fd_account_meta_t);
39 :
40 42 : txn_account->meta_soff = (long)( (ulong)meta - (ulong)mem );
41 :
42 42 : txn_account->meta = meta;
43 42 : txn_account->data = data;
44 42 : txn_account->is_mutable = is_mutable;
45 :
46 42 : return mem;
47 45 : }
48 :
49 : fd_txn_account_t *
50 48 : fd_txn_account_join( void * mem ) {
51 48 : if( FD_UNLIKELY( !mem ) ) {
52 0 : FD_LOG_WARNING(( "NULL mem" ));
53 0 : return NULL;
54 0 : }
55 :
56 48 : 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 48 : fd_txn_account_t * txn_account = (fd_txn_account_t *)mem;
62 :
63 48 : 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 45 : if( FD_UNLIKELY( txn_account->meta_soff==0UL ) ) {
69 0 : FD_LOG_CRIT(( "invalid meta_soff" ));
70 0 : }
71 :
72 45 : return txn_account;
73 45 : }
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 : fd_account_meta_t *
119 : fd_txn_account_init_from_funk_mutable( fd_txn_account_t * acct,
120 : fd_pubkey_t const * pubkey,
121 : fd_accdb_user_t * accdb,
122 : fd_funk_txn_xid_t const * xid,
123 : int do_create,
124 : ulong min_data_sz,
125 36 : fd_funk_rec_prepare_t * prepare_out ) {
126 36 : memset( prepare_out, 0, sizeof(fd_funk_rec_prepare_t) );
127 :
128 36 : fd_accdb_rw_t rw[1];
129 36 : int flags = do_create ? FD_ACCDB_FLAG_CREATE : 0;
130 36 : if( FD_UNLIKELY( !fd_accdb_open_rw( accdb, rw, xid, pubkey->uc, min_data_sz, flags ) ) ) {
131 0 : return NULL;
132 0 : }
133 :
134 36 : if( FD_UNLIKELY( !fd_txn_account_join( fd_txn_account_new(
135 36 : acct,
136 36 : pubkey,
137 36 : (fd_account_meta_t *)rw->meta,
138 36 : 1 ) ) ) ) {
139 0 : FD_LOG_CRIT(( "Failed to join txn account" ));
140 0 : }
141 :
142 : /* HACKY: Convert accdb_rw writable reference into txn_account.
143 : In the future, use fd_accdb_modify_publish instead */
144 36 : accdb->base.rw_active--;
145 36 : fd_accdb_user_v1_t * accdb_v1 = fd_type_pun( accdb );
146 36 : fd_funk_txn_t * txn = accdb_v1->funk->txn_pool->ele + accdb_v1->tip_txn_idx;
147 36 : if( FD_UNLIKELY( !fd_funk_txn_xid_eq( &txn->xid, xid ) ) ) FD_LOG_CRIT(( "accdb_user corrupt: not joined to the expected transaction" ));
148 36 : fd_funk_rec_t * rec = (fd_funk_rec_t *)rw->ref->user_data;
149 36 : if( !rec->pub ) {
150 36 : *prepare_out = (fd_funk_rec_prepare_t) {
151 36 : .rec = rec,
152 36 : .rec_head_idx = &txn->rec_head_idx,
153 36 : .rec_tail_idx = &txn->rec_tail_idx
154 36 : };
155 36 : } else {
156 0 : memset( prepare_out, 0, sizeof(fd_funk_rec_prepare_t) );
157 0 : }
158 :
159 36 : return rw->meta;
160 36 : }
161 :
162 : void
163 : fd_txn_account_mutable_fini( fd_txn_account_t * acct,
164 : fd_accdb_user_t * accdb,
165 36 : fd_funk_rec_prepare_t * prepare ) {
166 36 : fd_funk_rec_key_t key = fd_funk_acc_key( acct->pubkey );
167 36 : fd_funk_t * funk = fd_accdb_user_v1_funk( accdb );
168 :
169 : /* Check that the prepared record is still valid -
170 : if these invariants are broken something is very wrong. */
171 36 : if( prepare->rec ) {
172 : /* Check that the prepared record is not the Funk null value */
173 36 : if( !prepare->rec->val_gaddr ) {
174 0 : FD_BASE58_ENCODE_32_BYTES( acct->pubkey->uc, acct_pubkey_b58 );
175 0 : FD_LOG_CRIT(( "invalid prepared record for %s: unexpected NULL funk record value. the record might have been modified by another thread",
176 0 : acct_pubkey_b58 ));
177 0 : }
178 :
179 : /* Ensure that the prepared record key still matches our key. */
180 36 : if( FD_UNLIKELY( memcmp( prepare->rec->pair.key, &key, sizeof(fd_funk_rec_key_t) )!=0 ) ) {
181 0 : FD_BASE58_ENCODE_32_BYTES( acct->pubkey->uc, acct_pubkey_b58 );
182 0 : FD_LOG_CRIT(( "invalid prepared record for %s: the record might have been modified by another thread",
183 0 : acct_pubkey_b58 ));
184 0 : }
185 :
186 : /* Crashes the app if this key already exists in funk (conflicting
187 : write) */
188 36 : fd_funk_rec_publish( funk, prepare );
189 36 : }
190 36 : }
191 :
192 : fd_pubkey_t const *
193 3 : fd_txn_account_get_owner( fd_txn_account_t const * acct ) {
194 3 : if( FD_UNLIKELY( !acct->meta ) ) {
195 0 : FD_LOG_CRIT(( "account is not setup" ));
196 0 : }
197 3 : return (fd_pubkey_t const *)acct->meta->owner;
198 3 : }
199 :
200 : fd_account_meta_t const *
201 3 : fd_txn_account_get_meta( fd_txn_account_t const * acct ) {
202 3 : return acct->meta;
203 3 : }
204 :
205 : uchar const *
206 3 : fd_txn_account_get_data( fd_txn_account_t const * acct ) {
207 3 : return acct->data;
208 3 : }
209 :
210 : uchar *
211 0 : fd_txn_account_get_data_mut( fd_txn_account_t const * acct ) {
212 0 : return acct->data;
213 0 : }
214 :
215 : ulong
216 9 : fd_txn_account_get_data_len( fd_txn_account_t const * acct ) {
217 9 : if( FD_UNLIKELY( !acct->meta ) ) {
218 0 : FD_LOG_CRIT(( "account is not setup" ));
219 0 : }
220 9 : return acct->meta->dlen;
221 9 : }
222 :
223 : int
224 0 : fd_txn_account_is_executable( fd_txn_account_t const * acct ) {
225 0 : if( FD_UNLIKELY( !acct->meta ) ) {
226 0 : FD_LOG_CRIT(( "account is not setup" ));
227 0 : }
228 0 : return !!acct->meta->executable;
229 0 : }
230 :
231 : ulong
232 9 : fd_txn_account_get_lamports( fd_txn_account_t const * acct ) {
233 9 : if( FD_UNLIKELY( !acct->meta ) ) {
234 0 : FD_LOG_CRIT(( "account is not setup" ));
235 0 : }
236 9 : return acct->meta->lamports;
237 9 : }
238 :
239 : void
240 0 : fd_txn_account_set_meta( fd_txn_account_t * acct, fd_account_meta_t * meta ) {
241 0 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
242 0 : FD_LOG_CRIT(( "account is not mutable" ));
243 0 : }
244 0 : if( FD_UNLIKELY( !meta ) ) {
245 0 : FD_LOG_CRIT(( "account is not setup" ));
246 0 : }
247 0 : acct->meta = meta;
248 0 : }
249 :
250 : void
251 36 : fd_txn_account_set_executable( fd_txn_account_t * acct, int is_executable ) {
252 36 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
253 0 : FD_LOG_CRIT(( "account is not mutable" ));
254 0 : }
255 36 : if( FD_UNLIKELY( !acct->meta ) ) {
256 0 : FD_LOG_CRIT(( "account is not setup" ));
257 0 : }
258 36 : acct->meta->executable = !!is_executable;
259 36 : }
260 :
261 : void
262 36 : fd_txn_account_set_owner( fd_txn_account_t * acct, fd_pubkey_t const * owner ) {
263 36 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
264 0 : FD_LOG_CRIT(( "account is not mutable" ));
265 0 : }
266 36 : if( FD_UNLIKELY( !acct->meta ) ) {
267 0 : FD_LOG_CRIT(( "account is not setup" ));
268 0 : }
269 36 : fd_memcpy( acct->meta->owner, owner, sizeof(fd_pubkey_t) );
270 36 : }
271 :
272 : void
273 39 : fd_txn_account_set_lamports( fd_txn_account_t * acct, ulong lamports ) {
274 39 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
275 0 : FD_LOG_CRIT(( "account is not mutable" ));
276 0 : }
277 39 : if( FD_UNLIKELY( !acct->meta ) ) {
278 0 : FD_LOG_CRIT(( "account is not setup" ));
279 0 : }
280 39 : acct->meta->lamports = lamports;
281 39 : }
282 :
283 : int
284 0 : fd_txn_account_checked_add_lamports( fd_txn_account_t * acct, ulong lamports ) {
285 0 : ulong balance_post = 0UL;
286 0 : int err = fd_ulong_checked_add( fd_txn_account_get_lamports( acct ), lamports, &balance_post );
287 0 : if( FD_UNLIKELY( err ) ) {
288 0 : return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
289 0 : }
290 :
291 0 : fd_txn_account_set_lamports( acct, balance_post );
292 0 : return FD_EXECUTOR_INSTR_SUCCESS;
293 0 : }
294 :
295 : int
296 0 : fd_txn_account_checked_sub_lamports( fd_txn_account_t * acct, ulong lamports ) {
297 0 : ulong balance_post = 0UL;
298 0 : int err = fd_ulong_checked_sub( fd_txn_account_get_lamports( acct ),
299 0 : lamports,
300 0 : &balance_post );
301 0 : if( FD_UNLIKELY( err ) ) {
302 0 : return FD_EXECUTOR_INSTR_ERR_ARITHMETIC_OVERFLOW;
303 0 : }
304 :
305 0 : fd_txn_account_set_lamports( acct, balance_post );
306 0 : return FD_EXECUTOR_INSTR_SUCCESS;
307 0 : }
308 :
309 : void
310 : fd_txn_account_set_data( fd_txn_account_t * acct,
311 : void const * data,
312 36 : ulong data_sz ) {
313 36 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
314 0 : FD_LOG_CRIT(( "account is not mutable" ));
315 0 : }
316 36 : if( FD_UNLIKELY( !acct->meta ) ) {
317 0 : FD_LOG_CRIT(( "account is not setup" ));
318 0 : }
319 36 : acct->meta->dlen = (uint)data_sz;
320 36 : fd_memcpy( acct->data, data, data_sz );
321 36 : }
322 :
323 : void
324 3 : fd_txn_account_set_data_len( fd_txn_account_t * acct, ulong data_len ) {
325 3 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
326 0 : FD_LOG_CRIT(( "account is not mutable" ));
327 0 : }
328 3 : if( FD_UNLIKELY( !acct->meta ) ) {
329 0 : FD_LOG_CRIT(( "account is not setup" ));
330 0 : }
331 3 : acct->meta->dlen = (uint)data_len;
332 3 : }
333 :
334 : void
335 0 : fd_txn_account_set_slot( fd_txn_account_t * acct, ulong slot ) {
336 0 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
337 0 : FD_LOG_CRIT(( "account is not mutable" ));
338 0 : }
339 0 : if( FD_UNLIKELY( !acct->meta ) ) {
340 0 : FD_LOG_CRIT(( "account is not setup" ));
341 0 : }
342 0 : acct->meta->slot = slot;
343 0 : }
344 :
345 : void
346 0 : fd_txn_account_clear_owner( fd_txn_account_t * acct ) {
347 0 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
348 0 : FD_LOG_CRIT(( "account is not mutable" ));
349 0 : }
350 0 : if( FD_UNLIKELY( !acct->meta ) ) {
351 0 : FD_LOG_CRIT(( "account is not setup" ));
352 0 : }
353 0 : fd_memset( acct->meta->owner, 0, sizeof(fd_pubkey_t) );
354 0 : }
355 :
356 : void
357 : fd_txn_account_resize( fd_txn_account_t * acct,
358 0 : ulong dlen ) {
359 0 : if( FD_UNLIKELY( !acct->is_mutable ) ) {
360 0 : FD_LOG_CRIT(( "account is not mutable" ));
361 0 : }
362 0 : if( FD_UNLIKELY( !acct->meta ) ) {
363 0 : FD_LOG_CRIT(( "account is not setup" ));
364 0 : }
365 : /* Because the memory for an account is preallocated for the transaction
366 : up to the max account size, we only need to zero out bytes (for the case
367 : where the account grew) and update the account dlen. */
368 0 : ulong old_sz = acct->meta->dlen;
369 0 : ulong new_sz = dlen;
370 0 : ulong memset_sz = fd_ulong_sat_sub( new_sz, old_sz );
371 0 : fd_memset( acct->data+old_sz, 0, memset_sz );
372 :
373 0 : acct->meta->dlen = (uint)dlen;
374 0 : }
375 :
376 : int
377 6 : fd_txn_account_is_mutable( fd_txn_account_t const * acct ) {
378 : /* A txn account is mutable if meta is non NULL */
379 6 : return acct->is_mutable;
380 6 : }
381 :
382 : int
383 6 : fd_txn_account_is_readonly( fd_txn_account_t const * acct ) {
384 : /* A txn account is readonly if only the meta_ field is non NULL */
385 6 : return !acct->is_mutable;
386 6 : }
387 :
388 : void
389 0 : fd_txn_account_set_readonly( fd_txn_account_t * acct ) {
390 0 : acct->is_mutable = 0;
391 0 : }
392 :
393 : void
394 0 : fd_txn_account_set_mutable( fd_txn_account_t * acct ) {
395 0 : acct->is_mutable = 1;
396 0 : }
|