Line data Source code
1 : #include "../../../../disco/tiles.h"
2 :
3 : #include "../../../../disco/metrics/fd_metrics.h"
4 : #include "../../../../disco/bank/fd_bank_abi.h"
5 : #include "../../../../flamenco/types/fd_types.h"
6 : #include "../../../../flamenco/runtime/fd_system_ids.h"
7 :
8 0 : #define FD_RESOLV_IN_KIND_FRAGMENT (0)
9 0 : #define FD_RESOLV_IN_KIND_BANK (1)
10 :
11 : struct blockhash {
12 : uchar b[ 32 ];
13 : };
14 :
15 : typedef struct blockhash blockhash_t;
16 :
17 : struct blockhash_map {
18 : blockhash_t key;
19 : ulong slot;
20 : };
21 :
22 : typedef struct blockhash_map blockhash_map_t;
23 :
24 : static const blockhash_t null_blockhash = { 0 };
25 :
26 : #define MAP_NAME map
27 0 : #define MAP_T blockhash_map_t
28 0 : #define MAP_KEY_T blockhash_t
29 0 : #define MAP_LG_SLOT_CNT 13UL
30 0 : #define MAP_KEY_NULL null_blockhash
31 : #if FD_HAS_AVX
32 0 : # define MAP_KEY_INVAL(k) _mm256_testz_si256( wb_ldu( (k).b ), wb_ldu( (k).b ) )
33 : #else
34 : # define MAP_KEY_INVAL(k) MAP_KEY_EQUAL(k, null_blockhash)
35 : #endif
36 0 : #define MAP_KEY_EQUAL(k0,k1) (!memcmp((k0).b,(k1).b, 32UL))
37 : #define MAP_MEMOIZE 0
38 : #define MAP_KEY_EQUAL_IS_SLOW 1
39 0 : #define MAP_KEY_HASH(key) fd_uint_load_4( (key).b )
40 : #define MAP_QUERY_OPT 1
41 :
42 : #include "../../../../util/tmpl/fd_map.c"
43 :
44 : typedef struct {
45 : int kind;
46 :
47 : fd_wksp_t * mem;
48 : ulong chunk0;
49 : ulong wmark;
50 : ulong mtu;
51 : } fd_resolv_in_ctx_t;
52 :
53 : typedef struct {
54 : ulong round_robin_idx;
55 : ulong round_robin_cnt;
56 :
57 : void * root_bank;
58 : ulong root_slot;
59 :
60 : blockhash_map_t * blockhash_map;
61 :
62 : ulong completed_slot;
63 : ulong blockhash_ring_idx;
64 : blockhash_t blockhash_ring[ 4096 ];
65 :
66 : uchar _bank_msg[ sizeof(fd_completed_bank_t) ];
67 :
68 : struct {
69 : ulong lut[ FD_METRICS_COUNTER_RESOLV_LUT_RESOLVED_CNT ];
70 : ulong blockhash_expired;
71 : } metrics;
72 :
73 : fd_resolv_in_ctx_t in[ 64UL ];
74 :
75 : fd_wksp_t * out_mem;
76 : ulong out_chunk0;
77 : ulong out_wmark;
78 : ulong out_chunk;
79 : } fd_resolv_ctx_t;
80 :
81 : FD_FN_CONST static inline ulong
82 3 : scratch_align( void ) {
83 3 : return alignof( fd_resolv_ctx_t );
84 3 : }
85 :
86 : FD_FN_PURE static inline ulong
87 3 : scratch_footprint( fd_topo_tile_t const * tile ) {
88 3 : (void)tile;
89 3 : ulong l = FD_LAYOUT_INIT;
90 3 : l = FD_LAYOUT_APPEND( l, alignof( fd_resolv_ctx_t ), sizeof( fd_resolv_ctx_t ) );
91 3 : l = FD_LAYOUT_APPEND( l, map_align(), map_footprint() );
92 3 : return FD_LAYOUT_FINI( l, scratch_align() );
93 3 : }
94 :
95 : extern void fd_ext_bank_release( void const * bank );
96 :
97 : static ulong _fd_ext_resolv_tile_cnt;
98 :
99 : ulong
100 0 : fd_ext_resolv_tile_cnt( void ) {
101 0 : while( !_fd_ext_resolv_tile_cnt ) {}
102 0 : return _fd_ext_resolv_tile_cnt;
103 0 : }
104 :
105 : static inline void
106 0 : metrics_write( fd_resolv_ctx_t * ctx ) {
107 0 : FD_MCNT_SET( RESOLV, BLOCKHASH_EXPIRED, ctx->metrics.blockhash_expired );
108 0 : FD_MCNT_ENUM_COPY( RESOLV, LUT_RESOLVED, ctx->metrics.lut );
109 0 : }
110 :
111 : static int
112 : before_frag( fd_resolv_ctx_t * ctx,
113 : ulong in_idx,
114 : ulong seq,
115 0 : ulong sig ) {
116 0 : (void)in_idx;
117 0 : (void)sig;
118 :
119 0 : if( FD_UNLIKELY( ctx->in[in_idx].kind==FD_RESOLV_IN_KIND_BANK ) ) return 0;
120 :
121 0 : return (seq % ctx->round_robin_cnt) != ctx->round_robin_idx;
122 0 : }
123 :
124 : static inline void
125 : during_frag( fd_resolv_ctx_t * ctx,
126 : ulong in_idx,
127 : ulong seq,
128 : ulong sig,
129 : ulong chunk,
130 0 : ulong sz ) {
131 0 : (void)seq;
132 0 : (void)sig;
133 :
134 0 : if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) )
135 0 : FD_LOG_ERR(( "chunk %lu %lu corrupt, not in range [%lu,%lu]", chunk, sz, ctx->in[ in_idx ].chunk0, ctx->in[ in_idx ].wmark ));
136 :
137 0 : switch( ctx->in[in_idx].kind ) {
138 0 : case FD_RESOLV_IN_KIND_BANK:
139 0 : fd_memcpy( ctx->_bank_msg, fd_chunk_to_laddr_const( ctx->in[in_idx].mem, chunk ), sz );
140 0 : break;
141 0 : case FD_RESOLV_IN_KIND_FRAGMENT: {
142 0 : uchar * src = (uchar *)fd_chunk_to_laddr( ctx->in[in_idx].mem, chunk );
143 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
144 0 : fd_memcpy( dst, src, sz );
145 0 : break;
146 0 : }
147 0 : default:
148 0 : FD_LOG_ERR(( "unknown in kind %d", ctx->in[in_idx].kind ));
149 0 : }
150 0 : }
151 :
152 : static inline void
153 : after_frag( fd_resolv_ctx_t * ctx,
154 : ulong in_idx,
155 : ulong seq,
156 : ulong sig,
157 : ulong chunk,
158 : ulong sz,
159 : ulong tsorig,
160 0 : fd_stem_context_t * stem ) {
161 0 : (void)seq;
162 0 : (void)sig;
163 0 : (void)chunk;
164 :
165 0 : if( FD_UNLIKELY( ctx->in[in_idx].kind==FD_RESOLV_IN_KIND_BANK ) ) {
166 0 : switch( sig ) {
167 0 : case 0: {
168 0 : fd_rooted_bank_t * frag = (fd_rooted_bank_t *)ctx->_bank_msg;
169 0 : if( FD_LIKELY( ctx->root_bank ) ) fd_ext_bank_release( ctx->root_bank );
170 :
171 0 : ctx->root_bank = frag->bank;
172 0 : ctx->root_slot = frag->slot;
173 0 : break;
174 0 : }
175 0 : case 1: {
176 0 : fd_completed_bank_t * frag = (fd_completed_bank_t *)ctx->_bank_msg;
177 :
178 0 : blockhash_map_t * entry = map_query( ctx->blockhash_map, ctx->blockhash_ring[ ctx->blockhash_ring_idx%4096UL ], NULL );
179 0 : if( FD_LIKELY( entry ) ) map_remove( ctx->blockhash_map, entry );
180 :
181 0 : memcpy( ctx->blockhash_ring[ ctx->blockhash_ring_idx%4096UL ].b, frag->hash, 32UL );
182 0 : ctx->blockhash_ring_idx++;
183 :
184 0 : blockhash_map_t * blockhash = map_insert( ctx->blockhash_map, *(blockhash_t *)frag->hash );
185 0 : blockhash->slot = frag->slot;
186 :
187 0 : ctx->completed_slot = frag->slot;
188 0 : break;
189 0 : }
190 0 : default:
191 0 : FD_LOG_ERR(( "unknown sig %lu", sig ));
192 0 : }
193 0 : return;
194 0 : }
195 :
196 0 : uchar const * dcache_entry = fd_chunk_to_laddr_const( ctx->out_mem, ctx->out_chunk );
197 :
198 0 : ulong payload_sz = *(ushort*)(dcache_entry + sz - sizeof(ushort));
199 0 : uchar const * payload = dcache_entry;
200 0 : fd_txn_t const * txn = (fd_txn_t const *)( dcache_entry + fd_ulong_align_up( payload_sz, 2UL ) );
201 :
202 : /* If we can't find the recent blockhash ... it means one of three things,
203 :
204 : (1) It's really old (more than 28 minutes) or just non-existent.
205 : (2) It's really new (we haven't seen the bank yet).
206 : (3) It's a durable nonce transaction (just let it pass).
207 :
208 : We want to assume case (2) for now, because we don't want to drop
209 : early incoming votes and things if we don't yet know the bank. If
210 : there's a lot of spam coming in with old blockhashes, we can
211 : introduce a holding area here to keep them until we know if they
212 : are valid or not. */
213 :
214 0 : ulong reference_slot = ctx->completed_slot;
215 0 : blockhash_map_t const * blockhash = map_query_const( ctx->blockhash_map, *(blockhash_t*)( payload+txn->recent_blockhash_off ), NULL );
216 0 : if( FD_LIKELY( blockhash ) ) {
217 0 : reference_slot = blockhash->slot;
218 0 : if( FD_UNLIKELY( reference_slot+151UL<ctx->completed_slot ) ) {
219 0 : ctx->metrics.blockhash_expired++;
220 0 : return;
221 0 : }
222 0 : }
223 :
224 0 : if( FD_UNLIKELY( txn->addr_table_adtl_cnt ) ) {
225 0 : if( FD_UNLIKELY( !ctx->root_bank ) ) {
226 0 : FD_MCNT_INC( RESOLV, NO_BANK_DROP, 1 );
227 0 : return;
228 0 : }
229 :
230 0 : ulong txn_t_sz = fd_ulong_align_up( fd_ulong_align_up( payload_sz, 2UL ) + fd_txn_footprint( txn->instr_cnt, txn->addr_table_lookup_cnt ), 32UL );
231 0 : fd_acct_addr_t * lut_accts = (fd_acct_addr_t*)(dcache_entry+txn_t_sz);
232 0 : ushort * next_payload_sz = (ushort*)(dcache_entry+txn_t_sz+txn->addr_table_adtl_cnt*sizeof(fd_acct_addr_t));
233 0 : int result = fd_bank_abi_resolve_address_lookup_tables( ctx->root_bank, 0, ctx->root_slot, txn, payload, lut_accts );
234 : /* result is in [-5, 0]. We want to map -5 to 0, -4 to 1, etc. */
235 0 : ctx->metrics.lut[ (ulong)((long)FD_METRICS_COUNTER_RESOLV_LUT_RESOLVED_CNT+result-1L) ]++;
236 :
237 0 : if( FD_UNLIKELY( result!=FD_BANK_ABI_TXN_INIT_SUCCESS ) ) return;
238 :
239 0 : *next_payload_sz = (ushort)payload_sz;
240 0 : sz = txn_t_sz+txn->addr_table_adtl_cnt*sizeof(fd_acct_addr_t)+sizeof(ushort);
241 0 : }
242 :
243 0 : fd_stem_publish( stem, 0UL, reference_slot, ctx->out_chunk, sz, 0UL, tsorig, 0UL );
244 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, sz, ctx->out_chunk0, ctx->out_wmark );
245 0 : }
246 :
247 : static void
248 : unprivileged_init( fd_topo_t * topo,
249 0 : fd_topo_tile_t * tile ) {
250 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
251 :
252 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
253 0 : fd_resolv_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_resolv_ctx_t ), sizeof( fd_resolv_ctx_t ) );
254 :
255 0 : ctx->round_robin_cnt = fd_topo_tile_name_cnt( topo, tile->name );
256 0 : ctx->round_robin_idx = tile->kind_id;
257 :
258 0 : ctx->completed_slot = 0UL;
259 0 : ctx->blockhash_ring_idx = 0UL;
260 :
261 0 : if( FD_LIKELY( !tile->kind_id ) ) _fd_ext_resolv_tile_cnt = ctx->round_robin_cnt;
262 :
263 0 : ctx->root_bank = NULL;
264 :
265 0 : memset( ctx->blockhash_ring, 0, sizeof( ctx->blockhash_ring ) );
266 0 : memset( &ctx->metrics, 0, sizeof( ctx->metrics ) );
267 :
268 0 : ctx->blockhash_map = map_join( map_new( FD_SCRATCH_ALLOC_APPEND( l, map_align(), map_footprint() ) ) );
269 0 : FD_TEST( ctx->blockhash_map );
270 :
271 0 : FD_TEST( tile->in_cnt<=sizeof( ctx->in )/sizeof( ctx->in[ 0 ] ) );
272 0 : for( ulong i=0UL; i<tile->in_cnt; i++ ) {
273 0 : fd_topo_link_t * link = &topo->links[ tile->in_link_id[ i ] ];
274 0 : fd_topo_wksp_t * link_wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ];
275 :
276 0 : if( FD_LIKELY( !strcmp( link->name, "replay_resol" ) ) ) ctx->in[i].kind = FD_RESOLV_IN_KIND_BANK;
277 0 : else ctx->in[i].kind = FD_RESOLV_IN_KIND_FRAGMENT;
278 :
279 0 : ctx->in[i].mem = link_wksp->wksp;
280 0 : ctx->in[i].chunk0 = fd_dcache_compact_chunk0( ctx->in[i].mem, link->dcache );
281 0 : ctx->in[i].wmark = fd_dcache_compact_wmark ( ctx->in[i].mem, link->dcache, link->mtu );
282 0 : ctx->in[i].mtu = link->mtu;
283 0 : }
284 :
285 0 : ctx->out_mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
286 0 : ctx->out_chunk0 = fd_dcache_compact_chunk0( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
287 0 : ctx->out_wmark = fd_dcache_compact_wmark ( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache, topo->links[ tile->out_link_id[ 0 ] ].mtu );
288 0 : ctx->out_chunk = ctx->out_chunk0;
289 :
290 0 : ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, 1UL );
291 0 : if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
292 0 : FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
293 0 : }
294 :
295 0 : #define STEM_BURST (1UL)
296 :
297 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_resolv_ctx_t
298 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_resolv_ctx_t)
299 :
300 0 : #define STEM_CALLBACK_METRICS_WRITE metrics_write
301 0 : #define STEM_CALLBACK_BEFORE_FRAG before_frag
302 0 : #define STEM_CALLBACK_DURING_FRAG during_frag
303 0 : #define STEM_CALLBACK_AFTER_FRAG after_frag
304 :
305 : #include "../../../../disco/stem/fd_stem.c"
306 :
307 : fd_topo_run_tile_t fd_tile_resolv = {
308 : .name = "resolv",
309 : .populate_allowed_seccomp = NULL,
310 : .populate_allowed_fds = NULL,
311 : .scratch_align = scratch_align,
312 : .scratch_footprint = scratch_footprint,
313 : .unprivileged_init = unprivileged_init,
314 : .run = stem_run,
315 : };
|