Line data Source code
1 : #include "fd_sysvar_slot_hashes.h"
2 : #include "fd_sysvar.h"
3 : #include "../fd_acc_mgr.h"
4 : #include "../fd_system_ids.h"
5 : /* FIXME These constants should be header defines */
6 :
7 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/slot_hashes.rs#L11 */
8 : FD_FN_UNUSED static const ulong slot_hashes_max_entries = 512;
9 :
10 : /* https://github.com/solana-labs/solana/blob/8f2c8b8388a495d2728909e30460aa40dcc5d733/sdk/program/src/sysvar/slot_hashes.rs#L12 */
11 : static const ulong slot_hashes_account_size = 20488;
12 :
13 : void
14 : fd_sysvar_slot_hashes_write( fd_bank_t * bank,
15 : fd_funk_t * funk,
16 : fd_funk_txn_xid_t const * xid,
17 : fd_capture_ctx_t * capture_ctx,
18 0 : fd_slot_hashes_global_t * slot_hashes_global ) {
19 0 : uchar enc[slot_hashes_account_size];
20 0 : fd_memset( enc, 0, slot_hashes_account_size );
21 0 : fd_bincode_encode_ctx_t ctx = {
22 0 : .data = enc,
23 0 : .dataend = enc + slot_hashes_account_size,
24 0 : };
25 0 : if( fd_slot_hashes_encode_global( slot_hashes_global, &ctx ) ) {
26 0 : FD_LOG_ERR(("fd_slot_hashes_encode failed"));
27 0 : }
28 0 : fd_sysvar_account_update( bank, funk, xid, capture_ctx, &fd_sysvar_slot_hashes_id, enc, slot_hashes_account_size );
29 0 : }
30 :
31 : ulong
32 0 : fd_sysvar_slot_hashes_footprint( ulong slot_hashes_cap ) {
33 0 : return sizeof(fd_slot_hashes_global_t) +
34 0 : deq_fd_slot_hash_t_footprint( slot_hashes_cap ) + deq_fd_slot_hash_t_align();
35 0 : }
36 :
37 : void *
38 : fd_sysvar_slot_hashes_new( void * mem,
39 0 : ulong slot_hashes_cap ) {
40 0 : if( FD_UNLIKELY( !mem ) ) {
41 0 : FD_LOG_ERR(( "Unable to allocate memory for slot hashes" ));
42 0 : }
43 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)mem, FD_SYSVAR_SLOT_HASHES_ALIGN ) ) ) {
44 0 : FD_LOG_ERR(( "Memory for slot hashes is not aligned" ));
45 0 : }
46 :
47 0 : fd_slot_hashes_global_t * slot_hashes_global = (fd_slot_hashes_global_t *)mem;
48 :
49 0 : uchar * slot_hash_mem = (uchar*)fd_ulong_align_up( (ulong)((uchar *)mem + sizeof(fd_slot_hashes_global_t)), deq_fd_slot_hash_t_align() );
50 0 : deq_fd_slot_hash_t_new( (void*)slot_hash_mem, slot_hashes_cap );
51 0 : slot_hashes_global->hashes_offset = (ulong)slot_hash_mem - (ulong)slot_hashes_global;
52 :
53 0 : return slot_hashes_global;
54 0 : }
55 :
56 : fd_slot_hashes_global_t *
57 : fd_sysvar_slot_hashes_join( void * shmem,
58 0 : fd_slot_hash_t ** slot_hash ) {
59 0 : fd_slot_hashes_global_t * slot_hashes_global = (fd_slot_hashes_global_t *)shmem;
60 0 : *slot_hash = deq_fd_slot_hash_t_join( (uchar*)shmem + slot_hashes_global->hashes_offset );
61 :
62 0 : return slot_hashes_global;
63 0 : }
64 :
65 : void *
66 : fd_sysvar_slot_hashes_leave( fd_slot_hashes_global_t * slot_hashes_global,
67 0 : fd_slot_hash_t * slot_hash ) {
68 0 : deq_fd_slot_hash_t_leave( slot_hash );
69 :
70 0 : return slot_hashes_global;
71 0 : }
72 :
73 : void *
74 0 : fd_sysvar_slot_hashes_delete( void * mem ) {
75 0 : void * slot_hash_mem = (void *)fd_ulong_align_up( (ulong)((uchar *)mem + sizeof(fd_slot_hashes_global_t)), deq_fd_slot_hash_t_align() );
76 0 : deq_fd_slot_hash_t_delete( slot_hash_mem );
77 :
78 0 : return mem;
79 0 : }
80 :
81 : void
82 : fd_sysvar_slot_hashes_init( fd_bank_t * bank,
83 : fd_funk_t * funk,
84 : fd_funk_txn_xid_t const * xid,
85 : fd_capture_ctx_t * capture_ctx,
86 0 : fd_spad_t * runtime_spad ) {
87 0 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
88 0 : void * mem = fd_spad_alloc( runtime_spad, FD_SYSVAR_SLOT_HASHES_ALIGN, fd_sysvar_slot_hashes_footprint( FD_SYSVAR_SLOT_HASHES_CAP ) );
89 0 : fd_slot_hash_t * shnull = NULL;
90 0 : fd_slot_hashes_global_t * slot_hashes_global = fd_sysvar_slot_hashes_join( fd_sysvar_slot_hashes_new( mem, FD_SYSVAR_SLOT_HASHES_CAP ), &shnull );
91 :
92 0 : fd_sysvar_slot_hashes_write( bank, funk, xid, capture_ctx, slot_hashes_global );
93 0 : fd_sysvar_slot_hashes_delete( fd_sysvar_slot_hashes_leave( slot_hashes_global, shnull ) );
94 0 : } FD_SPAD_FRAME_END;
95 0 : }
96 :
97 : /* https://github.com/anza-xyz/agave/blob/b11ca828cfc658b93cb86a6c5c70561875abe237/runtime/src/bank.rs#L2283-L2294 */
98 : void
99 : fd_sysvar_slot_hashes_update( fd_bank_t * bank,
100 : fd_funk_t * funk,
101 : fd_funk_txn_xid_t const * xid,
102 : fd_capture_ctx_t * capture_ctx,
103 0 : fd_spad_t * runtime_spad ) {
104 0 : FD_SPAD_FRAME_BEGIN( runtime_spad ) {
105 0 : fd_slot_hashes_global_t * slot_hashes_global = fd_sysvar_slot_hashes_read( funk, xid, runtime_spad );
106 0 : fd_slot_hash_t * hashes = NULL;
107 0 : if( FD_UNLIKELY( !slot_hashes_global ) ) {
108 : /* Note: Agave's implementation initializes a new slot_hashes if it doesn't already exist (refer to above URL). */
109 0 : void * mem = fd_spad_alloc( runtime_spad, FD_SYSVAR_SLOT_HASHES_ALIGN, fd_sysvar_slot_hashes_footprint( FD_SYSVAR_SLOT_HASHES_CAP ) );
110 0 : slot_hashes_global = fd_sysvar_slot_hashes_new( mem, FD_SYSVAR_SLOT_HASHES_CAP );
111 0 : }
112 0 : slot_hashes_global = fd_sysvar_slot_hashes_join( slot_hashes_global, &hashes );
113 :
114 0 : uchar found = 0;
115 0 : for( deq_fd_slot_hash_t_iter_t iter = deq_fd_slot_hash_t_iter_init( hashes );
116 0 : !deq_fd_slot_hash_t_iter_done( hashes, iter );
117 0 : iter = deq_fd_slot_hash_t_iter_next( hashes, iter ) ) {
118 0 : fd_slot_hash_t * ele = deq_fd_slot_hash_t_iter_ele( hashes, iter );
119 0 : if( ele->slot == fd_bank_parent_slot_get( bank ) ) {
120 0 : fd_hash_t const * bank_hash = fd_bank_bank_hash_query( bank );
121 0 : memcpy( &ele->hash, bank_hash, sizeof(fd_hash_t) );
122 0 : found = 1;
123 0 : }
124 0 : }
125 :
126 0 : if( !found ) {
127 : // https://github.com/firedancer-io/solana/blob/08a1ef5d785fe58af442b791df6c4e83fe2e7c74/runtime/src/bank.rs#L2371
128 0 : fd_slot_hash_t slot_hash = {
129 0 : .hash = fd_bank_bank_hash_get( bank ), // parent hash?
130 0 : .slot = fd_bank_parent_slot_get( bank ), // parent_slot
131 0 : };
132 0 : FD_LOG_DEBUG(( "fd_sysvar_slot_hash_update: slot %lu, hash %s", slot_hash.slot, FD_BASE58_ENC_32_ALLOCA( slot_hash.hash.key ) ));
133 :
134 0 : if( deq_fd_slot_hash_t_full( hashes ) )
135 0 : memset( deq_fd_slot_hash_t_pop_tail_nocopy( hashes ), 0, sizeof(fd_slot_hash_t) );
136 :
137 0 : deq_fd_slot_hash_t_push_head( hashes, slot_hash );
138 0 : }
139 :
140 0 : fd_sysvar_slot_hashes_write( bank, funk, xid, capture_ctx, slot_hashes_global );
141 0 : fd_sysvar_slot_hashes_leave( slot_hashes_global, hashes );
142 0 : } FD_SPAD_FRAME_END;
143 0 : }
144 :
145 : fd_slot_hashes_global_t *
146 : fd_sysvar_slot_hashes_read( fd_funk_t * funk,
147 : fd_funk_txn_xid_t const * xid,
148 0 : fd_spad_t * spad ) {
149 0 : FD_TXN_ACCOUNT_DECL( rec );
150 0 : int err = fd_txn_account_init_from_funk_readonly( rec, (fd_pubkey_t const *)&fd_sysvar_slot_hashes_id, funk, xid );
151 0 : if( FD_UNLIKELY( err!=FD_ACC_MGR_SUCCESS ) ) {
152 0 : return NULL;
153 0 : }
154 :
155 : /* This check is needed as a quirk of the fuzzer. If a sysvar account
156 : exists in the accounts database, but doesn't have any lamports,
157 : this means that the account does not exist. This wouldn't happen
158 : in a real execution environment. */
159 0 : if( FD_UNLIKELY( fd_txn_account_get_lamports( rec )==0 ) ) {
160 0 : return NULL;
161 0 : }
162 :
163 0 : fd_bincode_decode_ctx_t decode = {
164 0 : .data = fd_txn_account_get_data( rec ),
165 0 : .dataend = fd_txn_account_get_data( rec ) + fd_txn_account_get_data_len( rec )
166 0 : };
167 :
168 0 : ulong total_sz = 0UL;
169 0 : err = fd_slot_hashes_decode_footprint( &decode, &total_sz );
170 0 : if( FD_UNLIKELY( err ) ) {
171 0 : return NULL;
172 0 : }
173 :
174 0 : uchar * mem = fd_spad_alloc( spad, fd_slot_hashes_align(), total_sz );
175 :
176 0 : if( FD_UNLIKELY( !mem ) ) {
177 0 : FD_LOG_ERR(( "Unable to allocate memory for slot hashes" ));
178 0 : }
179 :
180 0 : return fd_slot_hashes_decode_global( mem, &decode );
181 0 : }
|