Line data Source code
1 : #include "fd_rserve.h"
2 : #include "../../ballet/sha256/fd_sha256.h"
3 :
4 : static void
5 : fd_rserve_rotation_secret( uchar out[ 32 ],
6 : uchar const master[ 32 ],
7 0 : ulong idx ) {
8 0 : uchar buf[ 40 ];
9 0 : memcpy( buf, master, 32UL );
10 0 : FD_STORE( ulong, buf+32UL, idx );
11 0 : fd_sha256_hash( buf, sizeof(buf), out );
12 0 : }
13 :
14 : static void
15 : fd_rserve_token_compute( uchar token[ 32 ],
16 : uchar const secret[ 32 ],
17 : fd_pubkey_t const * from,
18 : uint ip4,
19 0 : ushort port ) {
20 0 : memcpy( token, "SOLANA_PING_PONG", 16UL );
21 :
22 0 : uchar buf[ 32UL + 32UL + 4UL + 2UL ];
23 0 : memcpy( buf, secret, 32UL );
24 0 : memcpy( buf+32UL, from->uc, 32UL );
25 0 : FD_STORE( uint, buf+64UL, ip4 );
26 0 : FD_STORE( ushort, buf+68UL, port );
27 :
28 0 : uchar mac[ 32 ];
29 0 : fd_sha256_hash( buf, sizeof(buf), mac );
30 0 : memcpy( token+16UL, mac, 16UL );
31 0 : }
32 :
33 : ulong
34 0 : fd_rserve_footprint( ulong ping_cache_entries ) {
35 0 : if( FD_UNLIKELY( !ping_cache_entries ) ) return 0UL;
36 :
37 0 : ulong ping_max = fd_ulong_pow2_up( ping_cache_entries );
38 :
39 0 : ulong l = FD_LAYOUT_INIT;
40 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_rserve_t), sizeof(fd_rserve_t) );
41 0 : l = FD_LAYOUT_APPEND( l, ping_pool_align(), ping_pool_footprint( ping_max ) );
42 0 : l = FD_LAYOUT_APPEND( l, ping_map_align(), ping_map_footprint( ping_map_chain_cnt_est( ping_max ) ) );
43 0 : l = FD_LAYOUT_APPEND( l, ping_dlist_align(), ping_dlist_footprint() );
44 0 : return FD_LAYOUT_FINI( l, fd_rserve_align() );
45 0 : }
46 :
47 : void *
48 : fd_rserve_new( void * shmem,
49 : ulong ping_cache_entries,
50 : ulong seed,
51 0 : uchar const secret[ 32 ] ) {
52 0 : if( FD_UNLIKELY( !shmem ) ) {
53 0 : FD_LOG_WARNING(( "NULL mem" ));
54 0 : return NULL;
55 0 : }
56 :
57 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_rserve_align() ) ) ) {
58 0 : FD_LOG_WARNING(( "misaligned mem" ));
59 0 : return NULL;
60 0 : }
61 :
62 0 : ulong footprint = fd_rserve_footprint( ping_cache_entries );
63 0 : if( FD_UNLIKELY( !footprint ) ) {
64 0 : FD_LOG_WARNING(( "bad ping cache size (%lu)", ping_cache_entries ));
65 0 : return NULL;
66 0 : }
67 :
68 0 : ulong ping_max = fd_ulong_pow2_up( ping_cache_entries );
69 :
70 0 : FD_SCRATCH_ALLOC_INIT( l, shmem );
71 0 : void * rserve_mem = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_rserve_t), sizeof(fd_rserve_t) );
72 0 : void * ping_pool_mem = FD_SCRATCH_ALLOC_APPEND( l, ping_pool_align(), ping_pool_footprint( ping_max ) );
73 0 : void * ping_map_mem = FD_SCRATCH_ALLOC_APPEND( l, ping_map_align(), ping_map_footprint( ping_map_chain_cnt_est( ping_max ) ) );
74 0 : void * ping_dlist_mem = FD_SCRATCH_ALLOC_APPEND( l, ping_dlist_align(), ping_dlist_footprint() );
75 :
76 0 : fd_rserve_t * rserve = (fd_rserve_t *)rserve_mem;
77 0 : ping_pool_new( ping_pool_mem, ping_max );
78 0 : rserve->ping_pool = fd_type_pun( ping_pool_mem );
79 0 : rserve->ping_map = ping_map_join ( ping_map_new ( ping_map_mem, ping_map_chain_cnt_est( ping_max ), seed ) );
80 0 : rserve->ping_dlist = ping_dlist_join( ping_dlist_new( ping_dlist_mem ) );
81 :
82 : /* Initialize rotating token secrets. */
83 0 : rserve->seed = seed;
84 0 : rserve->token_idx = 0UL;
85 0 : rserve->last_rotate_ts = 0UL;
86 0 : memcpy( rserve->secret_master, secret, 32UL );
87 0 : fd_rserve_rotation_secret( rserve->secret_cur, rserve->secret_master, 0UL );
88 0 : memcpy( rserve->secret_prev, rserve->secret_cur, 32UL );
89 :
90 0 : FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_rserve_align() )==(ulong)shmem + footprint );
91 :
92 0 : return shmem;
93 0 : }
94 :
95 : fd_rserve_t *
96 0 : fd_rserve_join( void * shrserve ) {
97 0 : if( FD_UNLIKELY( !shrserve ) ) {
98 0 : FD_LOG_WARNING(( "NULL rserve" ));
99 0 : return NULL;
100 0 : }
101 :
102 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shrserve, fd_rserve_align() ) ) ) {
103 0 : FD_LOG_WARNING(( "misaligned rserve" ));
104 0 : return NULL;
105 0 : }
106 :
107 0 : fd_rserve_t * rserve = (fd_rserve_t *)shrserve;
108 0 : rserve->ping_pool = ping_pool_join ( rserve->ping_pool );
109 0 : rserve->ping_map = ping_map_join ( rserve->ping_map );
110 0 : rserve->ping_dlist = ping_dlist_join( rserve->ping_dlist );
111 :
112 0 : return (fd_rserve_t *)rserve;
113 0 : }
114 :
115 : void *
116 0 : fd_rserve_leave( fd_rserve_t const * rserve ) {
117 0 : if( FD_UNLIKELY( !rserve ) ) {
118 0 : FD_LOG_WARNING(( "NULL rserve" ));
119 0 : return NULL;
120 0 : }
121 :
122 0 : return (void *)rserve;
123 0 : }
124 :
125 : void *
126 0 : fd_rserve_delete( void * rserve ) {
127 0 : if( FD_UNLIKELY( !rserve ) ) {
128 0 : FD_LOG_WARNING(( "NULL rserve" ));
129 0 : return NULL;
130 0 : }
131 :
132 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)rserve, fd_rserve_align() ) ) ) {
133 0 : FD_LOG_WARNING(( "misaligned rserve" ));
134 0 : return NULL;
135 0 : }
136 :
137 0 : return rserve;
138 0 : }
139 :
140 : void
141 : fd_rserve_ping_token( fd_rserve_t const * rserve,
142 : uchar token[ 32 ],
143 : fd_pubkey_t const * from,
144 : uint ip4,
145 0 : ushort port ) {
146 0 : fd_rserve_token_compute( token, rserve->secret_cur, from, ip4, port );
147 0 : }
148 :
149 : static int
150 : fd_rserve_secret_pong_verify( uchar const secret[ 32 ],
151 : uchar const * pong_hash,
152 : fd_pubkey_t const * from,
153 : uint ip4,
154 0 : ushort port ) {
155 0 : uchar token [ 32 ];
156 0 : uchar preimage[ 16UL+32UL ];
157 0 : uchar expected[ 32 ];
158 :
159 0 : fd_rserve_token_compute( token, secret, from, ip4, port );
160 0 : memcpy( preimage, "SOLANA_PING_PONG", 16UL );
161 0 : memcpy( preimage+16UL, token, 32UL );
162 0 : fd_sha256_hash( preimage, sizeof(preimage), expected );
163 0 : return !memcmp( expected, pong_hash, 32UL );
164 0 : }
165 :
166 : int
167 : fd_rserve_pong_token_verify( fd_rserve_t const * rserve,
168 : uchar const * pong_hash,
169 : fd_pubkey_t const * from,
170 : uint ip4,
171 0 : ushort port ) {
172 0 : if( FD_LIKELY( fd_rserve_secret_pong_verify( rserve->secret_cur, pong_hash, from, ip4, port ) ) ) return 1;
173 0 : if( FD_LIKELY( fd_rserve_secret_pong_verify( rserve->secret_prev, pong_hash, from, ip4, port ) ) ) return 1;
174 0 : return 0;
175 0 : }
176 :
177 : void
178 : fd_rserve_maybe_rotate( fd_rserve_t * rserve,
179 0 : ulong now_ns ) {
180 0 : if( FD_UNLIKELY( now_ns-rserve->last_rotate_ts > FD_RSERVE_TOKEN_ROTATE_NS ) ) {
181 0 : rserve->last_rotate_ts = now_ns;
182 0 : rserve->token_idx++;
183 0 : memcpy( rserve->secret_prev, rserve->secret_cur, 32UL );
184 0 : fd_rserve_rotation_secret( rserve->secret_cur, rserve->secret_master, rserve->token_idx );
185 0 : }
186 0 : }
187 :
188 : #define MAP_NAME fd_blockdb_map
189 0 : #define MAP_ELE_T fd_blockdb_blk_t
190 : #define MAP_KEY_T fd_blockdb_key_t
191 0 : #define MAP_KEY key
192 0 : #define MAP_IDX_T uint
193 0 : #define MAP_NEXT next
194 0 : #define MAP_PREV prev
195 0 : #define MAP_KEY_EQ(k0,k1) (!memcmp( (k0), (k1), sizeof(fd_blockdb_key_t) ))
196 0 : #define MAP_KEY_HASH(key,seed) fd_hash( (seed), (key), sizeof(fd_blockdb_key_t) )
197 : #define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
198 : #include "../../util/tmpl/fd_map_chain.c"
199 :
200 : ulong
201 0 : fd_blockdb_footprint( ulong ele_max ) {
202 0 : if( FD_UNLIKELY( !ele_max || ele_max>=UINT_MAX ) ) return 0UL;
203 :
204 0 : ulong l = FD_LAYOUT_INIT;
205 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_blockdb_t), sizeof(fd_blockdb_t) );
206 0 : l = FD_LAYOUT_APPEND( l, alignof(fd_blockdb_blk_t), ele_max*sizeof(fd_blockdb_blk_t) );
207 0 : l = FD_LAYOUT_APPEND( l, fd_blockdb_map_align(), fd_blockdb_map_footprint( fd_blockdb_map_chain_cnt_est( ele_max ) ) );
208 0 : l = FD_LAYOUT_APPEND( l, FD_BMTREE_COMMIT_ALIGN, FD_BMTREE_COMMIT_FOOTPRINT( FD_BLOCKDB_TREE_LAYER_MAX ) );
209 0 : return FD_LAYOUT_FINI( l, fd_blockdb_align() );
210 0 : }
211 :
212 : void *
213 : fd_blockdb_new( void * shmem,
214 : ulong ele_max,
215 0 : ulong seed ) {
216 0 : if( FD_UNLIKELY( !shmem ) ) {
217 0 : FD_LOG_WARNING(( "NULL mem" ));
218 0 : return NULL;
219 0 : }
220 :
221 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shmem, fd_blockdb_align() ) ) ) {
222 0 : FD_LOG_WARNING(( "misaligned mem" ));
223 0 : return NULL;
224 0 : }
225 :
226 0 : ulong footprint = fd_blockdb_footprint( ele_max );
227 0 : if( FD_UNLIKELY( !footprint ) ) {
228 0 : FD_LOG_WARNING(( "bad ele_max (%lu)", ele_max ));
229 0 : return NULL;
230 0 : }
231 :
232 0 : ulong chain_cnt = fd_blockdb_map_chain_cnt_est( ele_max );
233 :
234 0 : FD_SCRATCH_ALLOC_INIT( l, shmem );
235 0 : fd_blockdb_t * blockdb = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_blockdb_t), sizeof(fd_blockdb_t) );
236 0 : fd_blockdb_blk_t * ele = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_blockdb_blk_t), ele_max*sizeof(fd_blockdb_blk_t) );
237 0 : void * map = FD_SCRATCH_ALLOC_APPEND( l, fd_blockdb_map_align(), fd_blockdb_map_footprint( chain_cnt ) );
238 0 : void * tree = FD_SCRATCH_ALLOC_APPEND( l, FD_BMTREE_COMMIT_ALIGN, FD_BMTREE_COMMIT_FOOTPRINT( FD_BLOCKDB_TREE_LAYER_MAX ) );
239 0 : FD_TEST( FD_SCRATCH_ALLOC_FINI( l, fd_blockdb_align() )==(ulong)shmem + footprint );
240 0 : FD_TEST( fd_bmtree_depth( FD_FEC_BLK_MAX+1UL )==FD_BLOCKDB_TREE_LAYER_MAX );
241 :
242 0 : blockdb->ele_max = ele_max;
243 0 : blockdb->seq = 0UL;
244 0 : blockdb->ele = ele;
245 0 : blockdb->tree = tree;
246 0 : blockdb->map = fd_blockdb_map_new( map, chain_cnt, seed );
247 0 : if( FD_UNLIKELY( !blockdb->map ) ) return NULL;
248 :
249 0 : return shmem;
250 0 : }
251 :
252 : fd_blockdb_t *
253 0 : fd_blockdb_join( void * shblockdb ) {
254 0 : if( FD_UNLIKELY( !shblockdb ) ) {
255 0 : FD_LOG_WARNING(( "NULL blockdb" ));
256 0 : return NULL;
257 0 : }
258 :
259 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shblockdb, fd_blockdb_align() ) ) ) {
260 0 : FD_LOG_WARNING(( "misaligned blockdb" ));
261 0 : return NULL;
262 0 : }
263 :
264 0 : fd_blockdb_t * blockdb = (fd_blockdb_t *)shblockdb;
265 0 : blockdb->map = fd_blockdb_map_join( blockdb->map );
266 0 : if( FD_UNLIKELY( !blockdb->map ) ) return NULL;
267 :
268 0 : return blockdb;
269 0 : }
270 :
271 : void *
272 0 : fd_blockdb_leave( fd_blockdb_t const * blockdb ) {
273 0 : if( FD_UNLIKELY( !blockdb ) ) {
274 0 : FD_LOG_WARNING(( "NULL blockdb" ));
275 0 : return NULL;
276 0 : }
277 :
278 0 : return (void *)blockdb;
279 0 : }
280 :
281 : void *
282 0 : fd_blockdb_delete( void * shblockdb ) {
283 0 : if( FD_UNLIKELY( !shblockdb ) ) {
284 0 : FD_LOG_WARNING(( "NULL blockdb" ));
285 0 : return NULL;
286 0 : }
287 :
288 0 : if( FD_UNLIKELY( !fd_ulong_is_aligned( (ulong)shblockdb, fd_blockdb_align() ) ) ) {
289 0 : FD_LOG_WARNING(( "misaligned blockdb" ));
290 0 : return NULL;
291 0 : }
292 :
293 0 : return shblockdb;
294 0 : }
295 :
296 : fd_blockdb_blk_t *
297 : fd_blockdb_insert( fd_blockdb_t * blockdb,
298 : ulong slot,
299 : fd_hash_t const * block_id,
300 : ulong parent_slot,
301 : fd_hash_t const * parent_block_id,
302 : uint fec_set_cnt,
303 0 : uchar const * merkle_roots ) {
304 0 : if( FD_UNLIKELY( !fec_set_cnt || fec_set_cnt>FD_FEC_BLK_MAX ) ) return NULL;
305 :
306 0 : fd_blockdb_map_t * map = (fd_blockdb_map_t *)blockdb->map;
307 0 : fd_blockdb_key_t key = { .slot = slot, .block_id = *block_id };
308 :
309 0 : fd_blockdb_blk_t * blk = fd_blockdb_map_ele_query( map, &key, NULL, blockdb->ele );
310 0 : if( FD_LIKELY( !blk ) ) {
311 : /* Take the oldest slot, unlinking its previous occupant once the
312 : array has wrapped. */
313 0 : blk = blockdb->ele + (blockdb->seq % blockdb->ele_max);
314 0 : if( FD_LIKELY( blockdb->seq>=blockdb->ele_max ) ) fd_blockdb_map_ele_remove_fast( map, blk, blockdb->ele );
315 0 : blockdb->seq++;
316 0 : blk->key = key;
317 0 : fd_blockdb_map_ele_insert( map, blk, blockdb->ele );
318 0 : }
319 :
320 0 : blk->parent_slot = parent_slot;
321 0 : blk->parent_block_id = *parent_block_id;
322 0 : blk->fec_set_cnt = fec_set_cnt;
323 0 : fd_memcpy( blk->merkle_roots, merkle_roots, fec_set_cnt*FD_SHRED_MERKLE_NODE_SZ );
324 0 : return blk;
325 0 : }
326 :
327 : fd_blockdb_blk_t const *
328 : fd_blockdb_query( fd_blockdb_t const * blockdb,
329 : ulong slot,
330 0 : fd_hash_t const * block_id ) {
331 0 : fd_blockdb_key_t key = { .slot = slot, .block_id = *block_id };
332 0 : return fd_blockdb_map_ele_query_const( (fd_blockdb_map_t const *)blockdb->map, &key, NULL, blockdb->ele );
333 0 : }
334 :
335 : int
336 : fd_blockdb_proof( fd_blockdb_t * blockdb,
337 : fd_blockdb_blk_t const * blk,
338 : ulong leaf_idx,
339 0 : uchar proof[ FD_BLOCKDB_PROOF_NODE_MAX*FD_SHRED_MERKLE_NODE_SZ ] ) {
340 0 : if( FD_UNLIKELY( leaf_idx>blk->fec_set_cnt ) ) return -1;
341 :
342 0 : fd_bmtree_commit_t * tree = fd_bmtree_commit_init( blockdb->tree, FD_SHRED_MERKLE_NODE_SZ, FD_BMTREE_LONG_PREFIX_SZ, FD_BLOCKDB_TREE_LAYER_MAX );
343 :
344 : /* Only the first FD_SHRED_MERKLE_NODE_SZ bytes of a node are hashed,
345 : so a zero-padded root prefix is an exact leaf. */
346 0 : fd_bmtree_node_t leaf[1] = {0};
347 0 : for( uint k=0U; k<blk->fec_set_cnt; k++ ) {
348 0 : memcpy( leaf->hash, blk->merkle_roots[ k ], FD_SHRED_MERKLE_NODE_SZ );
349 0 : fd_bmtree_commit_append( tree, leaf, 1UL );
350 0 : }
351 :
352 0 : fd_sha256_t sha[1];
353 0 : fd_sha256_init ( sha );
354 0 : fd_sha256_append( sha, &blk->parent_slot, sizeof(ulong) );
355 0 : fd_sha256_append( sha, blk->parent_block_id.uc, sizeof(fd_hash_t) );
356 0 : fd_sha256_append( sha, &blk->fec_set_cnt, sizeof(uint) );
357 0 : fd_sha256_fini ( sha, leaf->hash );
358 0 : fd_bmtree_commit_append( tree, leaf, 1UL );
359 :
360 0 : fd_bmtree_commit_fini( tree );
361 0 : return fd_bmtree_get_proof( tree, proof, leaf_idx );
362 0 : }
|