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 : ulong blockhash_unknown;
72 : } metrics;
73 :
74 : fd_resolv_in_ctx_t in[ 64UL ];
75 :
76 : fd_wksp_t * out_mem;
77 : ulong out_chunk0;
78 : ulong out_wmark;
79 : ulong out_chunk;
80 : } fd_resolv_ctx_t;
81 :
82 : FD_FN_CONST static inline ulong
83 3 : scratch_align( void ) {
84 3 : return alignof( fd_resolv_ctx_t );
85 3 : }
86 :
87 : FD_FN_PURE static inline ulong
88 3 : scratch_footprint( fd_topo_tile_t const * tile ) {
89 3 : (void)tile;
90 3 : ulong l = FD_LAYOUT_INIT;
91 3 : l = FD_LAYOUT_APPEND( l, alignof( fd_resolv_ctx_t ), sizeof( fd_resolv_ctx_t ) );
92 3 : l = FD_LAYOUT_APPEND( l, map_align(), map_footprint() );
93 3 : return FD_LAYOUT_FINI( l, scratch_align() );
94 3 : }
95 :
96 : extern void fd_ext_bank_release( void const * bank );
97 :
98 : static ulong _fd_ext_resolv_tile_cnt;
99 :
100 : ulong
101 0 : fd_ext_resolv_tile_cnt( void ) {
102 0 : while( !_fd_ext_resolv_tile_cnt ) {}
103 0 : return _fd_ext_resolv_tile_cnt;
104 0 : }
105 :
106 : static inline void
107 0 : metrics_write( fd_resolv_ctx_t * ctx ) {
108 0 : FD_MCNT_SET( RESOLV, BLOCKHASH_EXPIRED, ctx->metrics.blockhash_expired );
109 0 : FD_MCNT_SET( RESOLV, BLOCKHASH_UNKNOWN, ctx->metrics.blockhash_unknown );
110 0 : FD_MCNT_ENUM_COPY( RESOLV, LUT_RESOLVED, ctx->metrics.lut );
111 0 : }
112 :
113 : static int
114 : before_frag( fd_resolv_ctx_t * ctx,
115 : ulong in_idx,
116 : ulong seq,
117 0 : ulong sig ) {
118 0 : (void)in_idx;
119 0 : (void)sig;
120 :
121 0 : if( FD_UNLIKELY( ctx->in[in_idx].kind==FD_RESOLV_IN_KIND_BANK ) ) return 0;
122 :
123 0 : return (seq % ctx->round_robin_cnt) != ctx->round_robin_idx;
124 0 : }
125 :
126 : static inline void
127 : during_frag( fd_resolv_ctx_t * ctx,
128 : ulong in_idx,
129 : ulong seq,
130 : ulong sig,
131 : ulong chunk,
132 0 : ulong sz ) {
133 0 : (void)seq;
134 0 : (void)sig;
135 :
136 0 : if( FD_UNLIKELY( chunk<ctx->in[ in_idx ].chunk0 || chunk>ctx->in[ in_idx ].wmark || sz>ctx->in[ in_idx ].mtu ) )
137 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 ));
138 :
139 0 : switch( ctx->in[in_idx].kind ) {
140 0 : case FD_RESOLV_IN_KIND_BANK:
141 0 : fd_memcpy( ctx->_bank_msg, fd_chunk_to_laddr_const( ctx->in[in_idx].mem, chunk ), sz );
142 0 : break;
143 0 : case FD_RESOLV_IN_KIND_FRAGMENT: {
144 0 : uchar * src = (uchar *)fd_chunk_to_laddr( ctx->in[in_idx].mem, chunk );
145 0 : uchar * dst = (uchar *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
146 0 : fd_memcpy( dst, src, sz );
147 0 : break;
148 0 : }
149 0 : default:
150 0 : FD_LOG_ERR(( "unknown in kind %d", ctx->in[in_idx].kind ));
151 0 : }
152 0 : }
153 :
154 : static inline void
155 : after_frag( fd_resolv_ctx_t * ctx,
156 : ulong in_idx,
157 : ulong seq,
158 : ulong sig,
159 : ulong sz,
160 : ulong tsorig,
161 0 : fd_stem_context_t * stem ) {
162 0 : (void)seq;
163 0 : (void)sig;
164 0 : (void)sz;
165 :
166 0 : if( FD_UNLIKELY( ctx->in[in_idx].kind==FD_RESOLV_IN_KIND_BANK ) ) {
167 0 : switch( sig ) {
168 0 : case 0: {
169 0 : fd_rooted_bank_t * frag = (fd_rooted_bank_t *)ctx->_bank_msg;
170 0 : if( FD_LIKELY( ctx->root_bank ) ) fd_ext_bank_release( ctx->root_bank );
171 :
172 0 : ctx->root_bank = frag->bank;
173 0 : ctx->root_slot = frag->slot;
174 0 : break;
175 0 : }
176 0 : case 1: {
177 0 : fd_completed_bank_t * frag = (fd_completed_bank_t *)ctx->_bank_msg;
178 :
179 0 : blockhash_map_t * entry = map_query( ctx->blockhash_map, ctx->blockhash_ring[ ctx->blockhash_ring_idx%4096UL ], NULL );
180 0 : if( FD_LIKELY( entry ) ) map_remove( ctx->blockhash_map, entry );
181 :
182 0 : memcpy( ctx->blockhash_ring[ ctx->blockhash_ring_idx%4096UL ].b, frag->hash, 32UL );
183 0 : ctx->blockhash_ring_idx++;
184 :
185 0 : blockhash_map_t * blockhash = map_insert( ctx->blockhash_map, *(blockhash_t *)frag->hash );
186 0 : blockhash->slot = frag->slot;
187 :
188 0 : ctx->completed_slot = frag->slot;
189 0 : break;
190 0 : }
191 0 : default:
192 0 : FD_LOG_ERR(( "unknown sig %lu", sig ));
193 0 : }
194 0 : return;
195 0 : }
196 :
197 0 : fd_txn_m_t * txnm = (fd_txn_m_t *)fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
198 0 : fd_txn_t const * txnt = fd_txn_m_txn_t( txnm );
199 :
200 : /* If we can't find the recent blockhash ... it means one of three things,
201 :
202 : (1) It's really old (more than 28 minutes) or just non-existent.
203 : (2) It's really new (we haven't seen the bank yet).
204 : (3) It's a durable nonce transaction (just let it pass).
205 :
206 : We want to assume case (2) for now, because we don't want to drop
207 : early incoming votes and things if we don't yet know the bank. If
208 : there's a lot of spam coming in with old blockhashes, we can
209 : introduce a holding area here to keep them until we know if they
210 : are valid or not. */
211 :
212 0 : txnm->reference_slot = ctx->completed_slot;
213 0 : blockhash_map_t const * blockhash = map_query_const( ctx->blockhash_map, *(blockhash_t*)( fd_txn_m_payload( txnm )+txnt->recent_blockhash_off ), NULL );
214 0 : if( FD_LIKELY( blockhash ) ) {
215 0 : txnm->reference_slot = blockhash->slot;
216 0 : if( FD_UNLIKELY( txnm->reference_slot+151UL<ctx->completed_slot ) ) {
217 0 : ctx->metrics.blockhash_expired++;
218 0 : return;
219 0 : }
220 0 : } else {
221 0 : ctx->metrics.blockhash_unknown++;
222 0 : }
223 :
224 0 : if( FD_UNLIKELY( txnt->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 : int result = fd_bank_abi_resolve_address_lookup_tables( ctx->root_bank, 0, ctx->root_slot, txnt, fd_txn_m_payload( txnm ), fd_txn_m_alut( txnm ) );
231 : /* result is in [-5, 0]. We want to map -5 to 0, -4 to 1, etc. */
232 0 : ctx->metrics.lut[ (ulong)((long)FD_METRICS_COUNTER_RESOLV_LUT_RESOLVED_CNT+result-1L) ]++;
233 :
234 0 : if( FD_UNLIKELY( result!=FD_BANK_ABI_TXN_INIT_SUCCESS ) ) return;
235 0 : }
236 :
237 0 : ulong realized_sz = fd_txn_m_realized_footprint( txnm, 1 );
238 0 : ulong tspub = fd_frag_meta_ts_comp( fd_tickcount() );
239 0 : fd_stem_publish( stem, 0UL, txnm->reference_slot, ctx->out_chunk, realized_sz, 0UL, tsorig, tspub );
240 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, realized_sz, ctx->out_chunk0, ctx->out_wmark );
241 0 : }
242 :
243 : static void
244 : unprivileged_init( fd_topo_t * topo,
245 0 : fd_topo_tile_t * tile ) {
246 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
247 :
248 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
249 0 : fd_resolv_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_resolv_ctx_t ), sizeof( fd_resolv_ctx_t ) );
250 :
251 0 : ctx->round_robin_cnt = fd_topo_tile_name_cnt( topo, tile->name );
252 0 : ctx->round_robin_idx = tile->kind_id;
253 :
254 0 : ctx->completed_slot = 0UL;
255 0 : ctx->blockhash_ring_idx = 0UL;
256 :
257 0 : if( FD_LIKELY( !tile->kind_id ) ) _fd_ext_resolv_tile_cnt = ctx->round_robin_cnt;
258 :
259 0 : ctx->root_bank = NULL;
260 :
261 0 : memset( ctx->blockhash_ring, 0, sizeof( ctx->blockhash_ring ) );
262 0 : memset( &ctx->metrics, 0, sizeof( ctx->metrics ) );
263 :
264 0 : ctx->blockhash_map = map_join( map_new( FD_SCRATCH_ALLOC_APPEND( l, map_align(), map_footprint() ) ) );
265 0 : FD_TEST( ctx->blockhash_map );
266 :
267 0 : FD_TEST( tile->in_cnt<=sizeof( ctx->in )/sizeof( ctx->in[ 0 ] ) );
268 0 : for( ulong i=0UL; i<tile->in_cnt; i++ ) {
269 0 : fd_topo_link_t * link = &topo->links[ tile->in_link_id[ i ] ];
270 0 : fd_topo_wksp_t * link_wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ];
271 :
272 0 : if( FD_LIKELY( !strcmp( link->name, "replay_resol" ) ) ) ctx->in[i].kind = FD_RESOLV_IN_KIND_BANK;
273 0 : else ctx->in[i].kind = FD_RESOLV_IN_KIND_FRAGMENT;
274 :
275 0 : ctx->in[i].mem = link_wksp->wksp;
276 0 : ctx->in[i].chunk0 = fd_dcache_compact_chunk0( ctx->in[i].mem, link->dcache );
277 0 : ctx->in[i].wmark = fd_dcache_compact_wmark ( ctx->in[i].mem, link->dcache, link->mtu );
278 0 : ctx->in[i].mtu = link->mtu;
279 0 : }
280 :
281 0 : ctx->out_mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
282 0 : ctx->out_chunk0 = fd_dcache_compact_chunk0( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
283 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 );
284 0 : ctx->out_chunk = ctx->out_chunk0;
285 :
286 0 : ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, 1UL );
287 0 : if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
288 0 : FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
289 0 : }
290 :
291 0 : #define STEM_BURST (1UL)
292 :
293 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_resolv_ctx_t
294 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_resolv_ctx_t)
295 :
296 0 : #define STEM_CALLBACK_METRICS_WRITE metrics_write
297 0 : #define STEM_CALLBACK_BEFORE_FRAG before_frag
298 0 : #define STEM_CALLBACK_DURING_FRAG during_frag
299 0 : #define STEM_CALLBACK_AFTER_FRAG after_frag
300 :
301 : #include "../../../../disco/stem/fd_stem.c"
302 :
303 : fd_topo_run_tile_t fd_tile_resolv = {
304 : .name = "resolv",
305 : .populate_allowed_seccomp = NULL,
306 : .populate_allowed_fds = NULL,
307 : .scratch_align = scratch_align,
308 : .scratch_footprint = scratch_footprint,
309 : .unprivileged_init = unprivileged_init,
310 : .run = stem_run,
311 : };
|