Line data Source code
1 : /* The forktest topology exercises offline replay with full consensus.
2 : Constructed using a full topology which is pruned down. */
3 :
4 : #define _GNU_SOURCE
5 : #include "../../../firedancer/topology.h"
6 : #include "../../../shared/fd_action.h"
7 : #include "../../../shared/commands/configure/configure.h"
8 : #include "../../../shared/commands/run/run.h"
9 : #include "../../../shared/commands/watch/watch.h"
10 : #include "../../../../ballet/lthash/fd_lthash.h"
11 : #include "../../../../discof/replay/fd_execrp.h"
12 : #include "../../../../discof/genesis/fd_genesi_tile.h"
13 : #include "../../../../discof/tower/fd_tower_tile.h"
14 : #include "../../../../discof/replay/fd_replay_tile.h"
15 : #include "../../../../disco/shred/fd_shred_tile.h"
16 : #include "../../../../disco/shred/fd_rnonce_ss.h"
17 : #include "../../../../disco/net/fd_net_tile.h"
18 : #include "../../../../disco/pack/fd_pack_cost.h"
19 : #include "../../../../disco/topo/fd_topob.h"
20 : #include "../../../../disco/topo/fd_cpu_topo.h"
21 : #include "../../../../util/pod/fd_pod_format.h"
22 : #include "../../../../util/tile/fd_tile_private.h"
23 : #include "../../../../discof/restore/utils/fd_ssctrl.h"
24 : #include "../../../../discof/restore/utils/fd_ssmsg.h"
25 : #include "../../../../flamenco/progcache/fd_progcache_admin.h"
26 : #include "../../../../flamenco/runtime/fd_acc_pool.h"
27 : #include "../../../../flamenco/accdb/fd_accdb_lineage.h"
28 : #include "../../../../ballet/shred/fd_shred.h"
29 : #include "../../../../discof/backtest/fd_backtest_src.h"
30 :
31 : #include <errno.h>
32 : #include <unistd.h> /* pause(2) */
33 : #include <fcntl.h>
34 :
35 : extern fd_topo_obj_callbacks_t * CALLBACKS[];
36 : fd_topo_run_tile_t fdctl_tile_run( fd_topo_tile_t const * tile );
37 :
38 : extern configure_stage_t fd_cfg_stage_keys;
39 :
40 : static args_t
41 0 : configure_args( void ) {
42 0 : args_t args = {
43 0 : .configure.command = CONFIGURE_CMD_INIT,
44 0 : };
45 :
46 0 : ulong stage_idx = 0UL;
47 0 : args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_hugetlbfs;
48 0 : args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_snapshots;
49 0 : args.configure.stages[ stage_idx++ ] = &fd_cfg_stage_keys;
50 0 : args.configure.stages[ stage_idx++ ] = NULL;
51 :
52 0 : return args;
53 0 : }
54 :
55 : static void
56 : forktest_perm( args_t * args FD_PARAM_UNUSED,
57 : fd_cap_chk_t * chk,
58 0 : config_t const * config ) {
59 0 : args_t c_args = configure_args();
60 0 : configure_cmd_perm( &c_args, chk, config );
61 0 : run_cmd_perm( NULL, chk, config );
62 0 : }
63 :
64 : static ushort
65 0 : forktest_recover_expected_shred_version( config_t const * config ) {
66 0 : fd_backtest_src_opts_t opts = {
67 0 : .path = config->firedancer.development.ledger_input.path,
68 0 : .format = config->firedancer.development.ledger_input.format,
69 0 : };
70 :
71 0 : uchar buf[ FD_SHRED_MAX_SZ ];
72 0 : ulong shred_sz = fd_backtest_src_first_shred( &opts, buf, sizeof(buf) );
73 0 : if( FD_UNLIKELY( !shred_sz ) ) FD_LOG_ERR(( "unable to recover shred version from `%s`", opts.path ));
74 :
75 0 : fd_shred_t const * shred = fd_shred_parse( buf, shred_sz );
76 0 : if( FD_UNLIKELY( !shred ) ) FD_LOG_ERR(( "unable to parse first shred from `%s`", opts.path ));
77 :
78 0 : FD_LOG_INFO(( "Recovered expected shred version %hu", shred->version ));
79 0 : return shred->version;
80 0 : }
81 :
82 : static void
83 0 : forktest_topo( config_t * config ) {
84 :
85 0 : if( FD_UNLIKELY( !config->consensus.expected_shred_version ) ) {
86 0 : config->consensus.expected_shred_version = forktest_recover_expected_shred_version( config );
87 0 : }
88 :
89 0 : config->development.sandbox = 0;
90 0 : config->development.no_clone = 1;
91 :
92 0 : ulong shred_tile_cnt = config->layout.shred_tile_count;
93 :
94 0 : ulong execrp_tile_cnt = config->firedancer.layout.execrp_tile_count;
95 0 : ulong sign_tile_cnt = config->firedancer.layout.sign_tile_count;
96 0 : ulong lta_tile_cnt = config->firedancer.layout.snapshot_hash_tile_count;
97 :
98 0 : int snapshots_enabled = !!config->gossip.entrypoints_cnt;
99 0 : int snapshot_lthash_disabled = config->development.snapshots.disable_lthash_verification;
100 :
101 0 : fd_topo_t * topo = fd_topob_new( &config->topo, config->name );
102 :
103 0 : topo->max_page_size = fd_cstr_to_shmem_page_sz( config->hugetlbfs.max_page_size );
104 0 : topo->gigantic_page_threshold = config->hugetlbfs.gigantic_page_threshold_mib << 20;
105 :
106 : /* topo, name */
107 0 : fd_topob_wksp( topo, "metric" );
108 0 : fd_topob_wksp( topo, "shred" );
109 0 : fd_topob_wksp( topo, "replay" );
110 0 : fd_topob_wksp( topo, "execrp" );
111 0 : fd_topob_wksp( topo, "tower" );
112 0 : fd_topob_wksp( topo, "sign" )->core_dump_level = FD_TOPO_CORE_DUMP_LEVEL_NEVER;
113 :
114 0 : fd_topob_wksp( topo, "metric_in" );
115 :
116 0 : fd_topob_wksp( topo, "net_shred" );
117 :
118 0 : fd_topob_wksp( topo, "gossip_out" );
119 :
120 0 : fd_topob_wksp( topo, "shred_out" );
121 0 : fd_topob_wksp( topo, "replay_epoch" );
122 0 : fd_topob_wksp( topo, "replay_execrp" );
123 0 : fd_topob_wksp( topo, "replay_out" );
124 0 : fd_topob_wksp( topo, "tower_out" );
125 :
126 0 : fd_topob_wksp( topo, "funk" )->core_dump_level = FD_TOPO_CORE_DUMP_LEVEL_FULL;
127 0 : fd_topob_wksp( topo, "funk_locks" )->core_dump_level = FD_TOPO_CORE_DUMP_LEVEL_FULL;
128 0 : fd_topob_wksp( topo, "progcache" );
129 0 : fd_topob_wksp( topo, "fec_sets" );
130 0 : fd_topob_wksp( topo, "txncache" );
131 0 : fd_topob_wksp( topo, "banks" );
132 0 : fd_topob_wksp( topo, "store" )->core_dump_level = FD_TOPO_CORE_DUMP_LEVEL_FULL;
133 0 : fd_topob_wksp( topo, "rnonce" );
134 :
135 0 : fd_topob_wksp( topo, "shred_sign" );
136 0 : fd_topob_wksp( topo, "sign_shred" );
137 :
138 0 : if( FD_UNLIKELY( !snapshots_enabled ) ) {
139 0 : fd_topob_wksp( topo, "genesi" );
140 0 : fd_topob_wksp( topo, "genesi_out" );
141 0 : }
142 :
143 0 : fd_topob_wksp( topo, "execrp_replay" );
144 :
145 0 : if( FD_LIKELY( snapshots_enabled ) ) {
146 0 : fd_topob_wksp( topo, "snapct" );
147 0 : fd_topob_wksp( topo, "snapld" );
148 0 : fd_topob_wksp( topo, "snapdc" );
149 0 : fd_topob_wksp( topo, "snapin" );
150 0 : fd_topob_wksp( topo, "snapct_ld" );
151 0 : fd_topob_wksp( topo, "snapld_dc" );
152 0 : fd_topob_wksp( topo, "snapdc_in" );
153 0 : if( snapshot_lthash_disabled ) {
154 0 : fd_topob_wksp( topo, "snapin_ct" );
155 0 : } else {
156 0 : fd_topob_wksp( topo, "snapla" );
157 0 : fd_topob_wksp( topo, "snapls" );
158 0 : fd_topob_wksp( topo, "snapla_ls" );
159 0 : fd_topob_wksp( topo, "snapin_ls" );
160 0 : fd_topob_wksp( topo, "snapls_ct" );
161 0 : }
162 :
163 0 : fd_topob_wksp( topo, "snapin_manif" );
164 0 : fd_topob_wksp( topo, "snapct_repr" );
165 0 : }
166 :
167 0 : fd_topob_wksp( topo, "forkt" );
168 :
169 0 : #define FOR(cnt) for( ulong i=0UL; i<cnt; i++ )
170 :
171 0 : ulong shred_depth = 65536UL; /* from fdctl/topology.c shred_store link. MAKE SURE TO KEEP IN SYNC. */
172 :
173 : /* topo, link_name, wksp_name, depth, mtu, burst */
174 0 : FOR(shred_tile_cnt) fd_topob_link( topo, "shred_net", "net_shred", 32768UL, FD_NET_MTU, 1UL );
175 :
176 0 : if( FD_LIKELY( snapshots_enabled ) ) {
177 : /* TODO: Revisit the depths of all the snapshot links */
178 0 : /**/ fd_topob_link( topo, "snapct_ld", "snapct_ld", 128UL, sizeof(fd_ssctrl_init_t), 1UL );
179 0 : /**/ fd_topob_link( topo, "snapld_dc", "snapld_dc", 16384UL, USHORT_MAX, 1UL );
180 0 : /**/ fd_topob_link( topo, "snapdc_in", "snapdc_in", 16384UL, USHORT_MAX, 1UL );
181 :
182 : /**/ fd_topob_link( topo, "snapin_manif", "snapin_manif", 4UL, sizeof(fd_snapshot_manifest_t),1UL );
183 0 : /**/ fd_topob_link( topo, "snapct_repr", "snapct_repr", 128UL, 0UL, 1UL )->permit_no_consumers = 1; /* TODO: wire in repair later */
184 :
185 0 : if( snapshot_lthash_disabled ) {
186 0 : /**/ fd_topob_link( topo, "snapin_ct", "snapin_ct", 128UL, 0UL, 1UL );
187 0 : } else {
188 0 : FOR(lta_tile_cnt) fd_topob_link( topo, "snapla_ls", "snapla_ls", 128UL, sizeof(fd_lthash_value_t), 1UL );
189 0 : /**/ fd_topob_link( topo, "snapin_ls", "snapin_ls", 256UL, sizeof(fd_snapshot_full_account_t), 1UL );
190 0 : /**/ fd_topob_link( topo, "snapls_ct", "snapls_ct", 128UL, 0UL, 1UL );
191 0 : }
192 0 : }
193 :
194 0 : if( FD_UNLIKELY( !snapshots_enabled ) ) {
195 0 : /**/ fd_topob_link( topo, "genesi_out", "genesi_out", 1UL, FD_GENESIS_TILE_MTU, 1UL );
196 0 : }
197 :
198 : /**/ fd_topob_link( topo, "gossip_out", "gossip_out", 65536UL*4UL, sizeof(fd_gossip_update_message_t), 1UL ); /* TODO: Unclear where this depth comes from ... fix */
199 :
200 0 : /**/ fd_topob_link( topo, "replay_epoch", "replay_epoch", 128UL, FD_EPOCH_OUT_MTU, 1UL ); /* TODO: This should be 2 but requires fixing STEM_BURST */
201 0 : /**/ fd_topob_link( topo, "replay_out", "replay_out", 65536UL, sizeof(fd_replay_message_t), 1UL );
202 0 : fd_topob_link( topo, "replay_execrp", "replay_execrp", 16384UL, sizeof(fd_execrp_task_msg_t), 1UL );
203 :
204 0 : FOR(shred_tile_cnt) fd_topob_link( topo, "shred_sign", "shred_sign", 128UL, 32UL, 1UL );
205 0 : FOR(shred_tile_cnt) fd_topob_link( topo, "sign_shred", "sign_shred", 128UL, sizeof(fd_ed25519_sig_t), 1UL );
206 0 : FOR(shred_tile_cnt) fd_topob_link( topo, "shred_out", "shred_out", shred_depth, sizeof(fd_shred_message_t), 3UL ); /* TODO: Pretty sure burst of 3 is incorrect here */
207 0 : /**/ fd_topob_link( topo, "tower_out", "tower_out", 16384UL, sizeof(fd_tower_msg_t), 2UL ); /* conf + slot_done. see explanation in fd_tower_tile.h for link_depth */
208 :
209 0 : FOR(execrp_tile_cnt) fd_topob_link( topo, "execrp_replay", "execrp_replay", 16384UL, sizeof(fd_execrp_task_done_msg_t), 1UL );
210 :
211 0 : ushort parsed_tile_to_cpu[ FD_TILE_MAX ];
212 : /* Unassigned tiles will be floating, unless auto topology is enabled. */
213 0 : for( ulong i=0UL; i<FD_TILE_MAX; i++ ) parsed_tile_to_cpu[ i ] = USHORT_MAX;
214 :
215 0 : int is_auto_affinity = !strcmp( config->firedancer.development.forktest.affinity, "auto" );
216 :
217 0 : fd_topo_cpus_t cpus[1];
218 0 : fd_topo_cpus_init( cpus );
219 :
220 0 : ulong affinity_tile_cnt = 0UL;
221 0 : if( FD_LIKELY( !is_auto_affinity ) ) affinity_tile_cnt = fd_tile_private_cpus_parse( config->firedancer.development.forktest.affinity, parsed_tile_to_cpu );
222 :
223 0 : ulong tile_to_cpu[ FD_TILE_MAX ] = {0};
224 0 : for( ulong i=0UL; i<affinity_tile_cnt; i++ ) {
225 0 : if( FD_UNLIKELY( parsed_tile_to_cpu[ i ]!=USHORT_MAX && parsed_tile_to_cpu[ i ]>=cpus->cpu_cnt ) )
226 0 : FD_LOG_ERR(( "The CPU affinity string in the configuration file under [development.forktest.affinity] specifies a CPU index of %hu, but the system "
227 0 : "only has %lu CPUs. You should either change the CPU allocations in the affinity string, or increase the number of CPUs "
228 0 : "in the system.",
229 0 : parsed_tile_to_cpu[ i ], cpus->cpu_cnt ));
230 0 : tile_to_cpu[ i ] = fd_ulong_if( parsed_tile_to_cpu[ i ]==USHORT_MAX, ULONG_MAX, (ulong)parsed_tile_to_cpu[ i ] );
231 0 : }
232 :
233 0 : fd_topob_link( topo, "net_shred", "net_shred", config->net.ingress_buffer_size, FD_NET_MTU, 1UL );
234 :
235 : /* topo, tile_name, tile_wksp, metrics_wksp, cpu_idx, is_agave, uses_id_keyswitch, uses_av_keyswitch */
236 : /**/ fd_topob_tile( topo, "metric", "metric", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0, 0 );
237 :
238 0 : if( FD_LIKELY( snapshots_enabled ) ) {
239 0 : /**/ fd_topob_tile( topo, "snapct", "snapct", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0, 0 )->allow_shutdown = 1;
240 0 : /**/ fd_topob_tile( topo, "snapld", "snapld", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0, 0 )->allow_shutdown = 1;
241 0 : /**/ fd_topob_tile( topo, "snapdc", "snapdc", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0, 0 )->allow_shutdown = 1;
242 0 : /**/ fd_topob_tile( topo, "snapin", "snapin", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0, 0 )->allow_shutdown = 1;
243 :
244 0 : if( snapshot_lthash_disabled ) {
245 : /* nothing to do here */
246 0 : } else {
247 0 : FOR(lta_tile_cnt) fd_topob_tile( topo, "snapla", "snapla", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0, 0 )->allow_shutdown = 1;
248 0 : /**/ fd_topob_tile( topo, "snapls", "snapls", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0, 0 )->allow_shutdown = 1;
249 0 : }
250 0 : }
251 :
252 0 : if( FD_UNLIKELY( !snapshots_enabled ) ) {
253 0 : /**/ fd_topob_tile( topo, "genesi", "genesi", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0, 0 )->allow_shutdown = 1;
254 0 : }
255 :
256 0 : FOR(shred_tile_cnt) fd_topob_tile( topo, "shred", "shred", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 1, 0 );
257 0 : /**/ fd_topob_tile( topo, "replay", "replay", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 1, 0 );
258 0 : FOR(execrp_tile_cnt) fd_topob_tile( topo, "execrp", "execrp", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0, 0 );
259 0 : /**/ fd_topob_tile( topo, "tower", "tower", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 1, 1 );
260 :
261 0 : FOR(sign_tile_cnt) fd_topob_tile( topo, "sign", "sign", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 1, 1 );
262 :
263 0 : fd_topob_tile( topo, "forkt", "forkt", "metric_in", tile_to_cpu[ topo->tile_cnt ], 0, 0, 0 );
264 :
265 : /* topo, tile_name, tile_kind_id, fseq_wksp, link_name, link_kind_id, reliable, polled */
266 0 : FOR(shred_tile_cnt) fd_topob_tile_in ( topo, "shred", i, "metric_in", "net_shred", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
267 0 : FOR(shred_tile_cnt) fd_topos_tile_in_net( topo, "metric_in", "shred_net", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED ); /* No reliable consumers of networking fragments, may be dropped or overrun */
268 :
269 0 : if( FD_UNLIKELY( !snapshots_enabled ) ) {
270 0 : /**/ fd_topob_tile_out( topo, "genesi", 0UL, "genesi_out", 0UL );
271 0 : }
272 :
273 0 : /**/ fd_topob_tile_in ( topo, "forkt", 0UL, "metric_in", "replay_epoch", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
274 0 : /**/ fd_topob_tile_out( topo, "forkt", 0UL, "gossip_out", 0UL );
275 0 : /**/ fd_topob_tile_in ( topo, "forkt", 0UL, "metric_in", "tower_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
276 0 : /**/ fd_topob_tile_in ( topo, "forkt", 0UL, "metric_in", "replay_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
277 :
278 0 : if( FD_LIKELY( snapshots_enabled ) ) {
279 0 : fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
280 0 : fd_topob_tile_out( topo, "snapct", 0UL, "snapct_ld", 0UL );
281 0 : fd_topob_tile_out( topo, "snapct", 0UL, "snapct_repr", 0UL );
282 :
283 0 : if( snapshot_lthash_disabled ) {
284 0 : /**/ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ct", 0UL );
285 0 : /**/ fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapin_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
286 0 : } else {
287 0 : /**/ fd_topob_tile_out( topo, "snapin", 0UL, "snapin_ls", 0UL );
288 0 : FOR(lta_tile_cnt) fd_topob_tile_in( topo, "snapla", i, "metric_in", "snapdc_in", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
289 0 : FOR(lta_tile_cnt) fd_topob_tile_out( topo, "snapla", i, "snapla_ls", i );
290 0 : /**/ fd_topob_tile_in( topo, "snapls", 0UL, "metric_in", "snapin_ls", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
291 0 : FOR(lta_tile_cnt) fd_topob_tile_in( topo, "snapls", 0UL, "metric_in", "snapla_ls", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
292 0 : /**/ fd_topob_tile_out( topo, "snapls", 0UL, "snapls_ct", 0UL );
293 0 : /**/ fd_topob_tile_in ( topo, "snapct", 0UL, "metric_in", "snapls_ct", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
294 0 : }
295 :
296 0 : /**/ fd_topob_tile_in ( topo, "snapld", 0UL, "metric_in", "snapct_ld", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
297 0 : /**/ fd_topob_tile_out( topo, "snapld", 0UL, "snapld_dc", 0UL );
298 :
299 0 : /**/ fd_topob_tile_in ( topo, "snapdc", 0UL, "metric_in", "snapld_dc", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
300 0 : /**/ fd_topob_tile_out( topo, "snapdc", 0UL, "snapdc_in", 0UL );
301 :
302 0 : fd_topob_tile_in ( topo, "snapin", 0UL, "metric_in", "snapdc_in", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
303 0 : fd_topob_tile_out( topo, "snapin", 0UL, "snapin_manif", 0UL );
304 0 : }
305 :
306 0 : if( FD_UNLIKELY( !snapshots_enabled ) ) {
307 0 : /**/ fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "genesi_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
308 0 : }
309 0 : /**/ fd_topob_tile_out( topo, "replay", 0UL, "replay_out", 0UL );
310 0 : /**/ fd_topob_tile_out( topo, "replay", 0UL, "replay_epoch", 0UL );
311 0 : /**/ fd_topob_tile_out( topo, "replay", 0UL, "replay_execrp", 0UL );
312 0 : FOR(execrp_tile_cnt) fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "execrp_replay", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
313 0 : /**/ fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "tower_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
314 0 : /**/ fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "gossip_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
315 0 : if( FD_LIKELY( snapshots_enabled ) ) {
316 0 : fd_topob_tile_in ( topo, "replay", 0UL, "metric_in", "snapin_manif", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
317 0 : }
318 :
319 0 : FOR(execrp_tile_cnt) fd_topob_tile_in ( topo, "execrp", i, "metric_in", "replay_execrp", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
320 0 : FOR(execrp_tile_cnt) fd_topob_tile_out( topo, "execrp", i, "execrp_replay", i );
321 :
322 0 : /**/ fd_topob_tile_in ( topo, "tower", 0UL, "metric_in", "replay_epoch", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
323 0 : /**/ fd_topob_tile_in ( topo, "tower", 0UL, "metric_in", "gossip_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
324 0 : /**/ fd_topob_tile_in ( topo, "tower", 0UL, "metric_in", "replay_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
325 0 : FOR(shred_tile_cnt) fd_topob_tile_in( topo, "tower", 0UL, "metric_in", "shred_out", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
326 0 : FOR(shred_tile_cnt) fd_topob_tile_in( topo, "replay", 0UL, "metric_in", "shred_out", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
327 0 : /**/ fd_topob_tile_out( topo, "tower", 0UL, "tower_out", 0UL );
328 :
329 0 : FOR(shred_tile_cnt) fd_topob_tile_in ( topo, "shred", i, "metric_in", "replay_epoch", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
330 0 : FOR(shred_tile_cnt) fd_topob_tile_in ( topo, "shred", i, "metric_in", "gossip_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
331 0 : FOR(shred_tile_cnt) fd_topob_tile_out( topo, "shred", i, "shred_out", i );
332 0 : FOR(shred_tile_cnt) fd_topob_tile_in ( topo, "shred", i, "metric_in", "tower_out", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
333 0 : FOR(shred_tile_cnt) fd_topob_tile_out( topo, "shred", i, "shred_net", i );
334 :
335 : /* topo, tile_name, tile_kind_id, fseq_wksp, link_name, link_kind_id, reliable, polled */
336 :
337 0 : for( ulong i=0UL; i<shred_tile_cnt; i++ ) {
338 0 : /**/ fd_topob_tile_in ( topo, "sign", 0UL, "metric_in", "shred_sign", i, FD_TOPOB_UNRELIABLE, FD_TOPOB_POLLED );
339 0 : /**/ fd_topob_tile_out( topo, "shred", i, "shred_sign", i );
340 0 : /**/ fd_topob_tile_in ( topo, "shred", i, "metric_in", "sign_shred", i, FD_TOPOB_UNRELIABLE, FD_TOPOB_UNPOLLED );
341 0 : /**/ fd_topob_tile_out( topo, "sign", 0UL, "sign_shred", i );
342 0 : }
343 :
344 0 : fd_topob_tile_out( topo, "forkt", 0UL, "net_shred", 0UL );
345 0 : FOR(shred_tile_cnt) fd_topob_tile_in( topo, "forkt", 0UL, "metric_in", "shred_net", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
346 :
347 0 : if( FD_LIKELY( !is_auto_affinity ) ) {
348 0 : if( FD_UNLIKELY( affinity_tile_cnt<topo->tile_cnt ) )
349 0 : FD_LOG_ERR(( "The topology you are using has %lu tiles, but the CPU affinity specified in the config tile as [development.forktest.affinity] only provides for %lu cores. "
350 0 : "You should either increase the number of cores dedicated to Firedancer in the affinity string, or decrease the number of cores needed by reducing "
351 0 : "the total tile count. You can reduce the tile count by decreasing individual tile counts in the [layout] section of the configuration file.",
352 0 : topo->tile_cnt, affinity_tile_cnt ));
353 0 : if( FD_UNLIKELY( affinity_tile_cnt>topo->tile_cnt ) )
354 0 : FD_LOG_WARNING(( "The topology you are using has %lu tiles, but the CPU affinity specified in the config tile as [development.forktest.affinity] provides for %lu cores. "
355 0 : "Not all cores in the affinity will be used by Firedancer. You may wish to increase the number of tiles in the system by increasing "
356 0 : "individual tile counts in the [layout] section of the configuration file.",
357 0 : topo->tile_cnt, affinity_tile_cnt ));
358 0 : } else {
359 0 : ushort blocklist_cores[ FD_TILE_MAX ];
360 0 : topo->blocklist_cores_cnt = fd_tile_private_cpus_parse( config->layout.blocklist_cores, blocklist_cores );
361 0 : if( FD_UNLIKELY( topo->blocklist_cores_cnt>FD_TILE_MAX ) ) {
362 0 : FD_LOG_ERR(( "The CPU string in the configuration file under [layout.blocklist_cores] specifies more CPUs than Firedancer can use. "
363 0 : "You should reduce the number of CPUs in the excluded cores string." ));
364 0 : }
365 :
366 0 : for( ulong i=0UL; i<topo->blocklist_cores_cnt; i++ ) {
367 : /* Since we use fd_tile_private_cpus_parse() like for affinity, the user
368 : may input a string containing `f`. That's parsed correctly, but it's
369 : meaningless for blocklisted cores, so we reject it here. */
370 0 : if( FD_UNLIKELY( blocklist_cores[ i ]==USHORT_MAX ) ) {
371 0 : FD_LOG_ERR(( "The CPU string in the configuration file under [layout.blocklist_cores] contains invalid values: `f`. "
372 0 : "You should fix the excluded cores string." ));
373 0 : }
374 0 : topo->blocklist_cores_cpu_idx[ i ] = blocklist_cores[ i ];
375 0 : }
376 0 : }
377 :
378 0 : if( FD_UNLIKELY( is_auto_affinity ) ) fd_topob_auto_layout( topo, 0 );
379 :
380 : /* Repair and shred share a secret they use to generate the nonces.
381 : It's not super security sensitive, but for good hygiene, we make it
382 : an object. */
383 0 : if( 1 /* just restrict the scope for these variables in this big function */ ) {
384 0 : fd_topo_obj_t * rnonce_ss_obj = fd_topob_obj( topo, "rnonce_ss", "rnonce" );
385 0 : for( ulong i=0UL; i<shred_tile_cnt; i++ ) {
386 0 : fd_topo_tile_t * shred_tile = &topo->tiles[ fd_topo_find_tile( topo, "shred", i ) ];
387 0 : fd_topob_tile_uses( topo, shred_tile, rnonce_ss_obj, FD_SHMEM_JOIN_MODE_READ_ONLY );
388 0 : }
389 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, rnonce_ss_obj->id, "rnonce_ss" ) );
390 0 : }
391 :
392 0 : setup_topo_funk( topo,
393 0 : config->firedancer.accounts.max_accounts,
394 0 : config->firedancer.runtime.max_live_slots,
395 0 : config->firedancer.accounts.file_size_gib );
396 0 : ulong funk_obj_id; FD_TEST( (funk_obj_id = fd_pod_query_ulong( topo->props, "funk", ULONG_MAX ))!=ULONG_MAX );
397 0 : ulong funk_locks_obj_id; FD_TEST( (funk_locks_obj_id = fd_pod_query_ulong( topo->props, "funk_locks", ULONG_MAX ))!=ULONG_MAX );
398 0 : fd_topo_obj_t * funk_obj = &topo->objs[ funk_obj_id ];
399 0 : fd_topo_obj_t * funk_locks_obj = &topo->objs[ funk_locks_obj_id ];
400 :
401 0 : /**/ fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "replay", 0UL ) ], funk_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
402 0 : /**/ fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "tower", 0UL ) ], funk_obj, FD_SHMEM_JOIN_MODE_READ_ONLY );
403 0 : FOR(execrp_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], funk_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
404 :
405 0 : /**/ fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "replay", 0UL ) ], funk_locks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
406 0 : /**/ fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "tower", 0UL ) ], funk_locks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
407 0 : FOR(execrp_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], funk_locks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
408 :
409 0 : fd_topo_obj_t * banks_obj = setup_topo_banks( topo, "banks", config->firedancer.runtime.max_live_slots, config->firedancer.runtime.max_fork_width, config->development.bench.larger_max_cost_per_block );
410 0 : /**/ fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "replay", 0UL ) ], banks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
411 0 : /**/ fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "tower", 0UL ) ], banks_obj, FD_SHMEM_JOIN_MODE_READ_ONLY );
412 0 : FOR(execrp_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], banks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
413 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, banks_obj->id, "banks" ) );
414 :
415 0 : if( FD_UNLIKELY( config->firedancer.runtime.concurrent_account_limit<FD_ACC_POOL_MIN_ACCOUNT_CNT_PER_TX ) ) {
416 0 : FD_LOG_ERR(( "concurrent_account_limit is less than the minimum required for transaction execution: %lu < %lu", config->firedancer.runtime.concurrent_account_limit, FD_ACC_POOL_MIN_ACCOUNT_CNT_PER_TX ));
417 0 : }
418 0 : if( FD_UNLIKELY( config->firedancer.runtime.max_live_slots<32UL ) ) {
419 0 : FD_LOG_ERR(( "max_live_slots must be >= 32 in order to support tower rooting" ));
420 0 : }
421 0 : if( FD_UNLIKELY( config->firedancer.runtime.max_live_slots>FD_ACCDB_MAX_DEPTH_MAX ) ) {
422 0 : FD_LOG_ERR(( "max_live_slots (%lu) exceeds compile-time limit FD_ACCDB_MAX_DEPTH_MAX (%lu)",
423 0 : config->firedancer.runtime.max_live_slots, FD_ACCDB_MAX_DEPTH_MAX ));
424 0 : }
425 :
426 0 : fd_topo_obj_t * acc_pool_obj = setup_topo_acc_pool( topo, config->firedancer.runtime.concurrent_account_limit );
427 0 : FOR(execrp_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], acc_pool_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
428 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, acc_pool_obj->id, "acc_pool" ) );
429 :
430 0 : setup_topo_progcache( topo, "progcache",
431 0 : fd_progcache_est_rec_max( config->firedancer.runtime.program_cache.heap_size_mib<<20,
432 0 : config->firedancer.runtime.program_cache.mean_cache_entry_size ),
433 0 : config->firedancer.runtime.max_live_slots,
434 0 : config->firedancer.runtime.program_cache.heap_size_mib<<20 );
435 0 : ulong progcache_obj_id; FD_TEST( (progcache_obj_id = fd_pod_query_ulong( topo->props, "progcache", ULONG_MAX ))!=ULONG_MAX );
436 0 : fd_topo_obj_t * progcache_obj = &topo->objs[ progcache_obj_id ];
437 :
438 0 : /**/ fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "replay", 0UL ) ], progcache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
439 0 : FOR(execrp_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], progcache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
440 :
441 0 : ulong fec_set_cnt = 2UL*shred_depth + config->tiles.shred.max_pending_shred_sets + 6UL;
442 0 : ulong fec_sets_sz = fec_set_cnt*sizeof(fd_fec_set_t); /* mirrors # of dcache entires in frankendancer */
443 0 : fd_topo_obj_t * fec_sets_obj = setup_topo_fec_sets( topo, "fec_sets", shred_tile_cnt*fec_sets_sz );
444 0 : for( ulong i=0UL; i<shred_tile_cnt; i++ ) {
445 0 : fd_topo_tile_t * shred_tile = &topo->tiles[ fd_topo_find_tile( topo, "shred", i ) ];
446 0 : fd_topob_tile_uses( topo, shred_tile, fec_sets_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
447 0 : }
448 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, fec_sets_obj->id, "fec_sets" ) );
449 :
450 0 : ulong store_fec_max = config->firedancer.runtime.max_live_slots * FD_FEC_BLK_MAX + (shred_depth * shred_tile_cnt) + 1;
451 :
452 0 : ulong store_fec_data_max = fd_ulong_if( config->firedancer.runtime.fixed_fec_sets, 31840UL, 63985UL );
453 0 : fd_topo_obj_t * store_obj = setup_topo_store( topo, "store", store_fec_max, (uint)shred_tile_cnt, store_fec_data_max );
454 0 : FOR(shred_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "shred", i ) ], store_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
455 0 : fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "replay", 0UL ) ], store_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
456 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, store_obj->id, "store" ) );
457 :
458 0 : fd_topo_obj_t * txncache_obj = setup_topo_txncache( topo, "txncache", config->firedancer.runtime.max_live_slots, FD_PACK_MAX_TXNCACHE_TXN_PER_SLOT );
459 0 : fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "replay", 0UL ) ], txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
460 0 : if( FD_LIKELY( snapshots_enabled ) ) {
461 0 : fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapin", 0UL ) ], txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
462 0 : }
463 0 : FOR(execrp_tile_cnt) fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "execrp", i ) ], txncache_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
464 0 : FD_TEST( fd_pod_insertf_ulong( topo->props, txncache_obj->id, "txncache" ) );
465 :
466 0 : if( FD_UNLIKELY( !snapshots_enabled ) ) {
467 0 : fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "genesi", 0UL ) ], funk_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
468 0 : fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "genesi", 0UL ) ], funk_locks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
469 0 : }
470 0 : if( FD_LIKELY( snapshots_enabled ) ) {
471 0 : fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapin", 0UL ) ], funk_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
472 0 : fd_topob_tile_uses( topo, &topo->tiles[ fd_topo_find_tile( topo, "snapin", 0UL ) ], funk_locks_obj, FD_SHMEM_JOIN_MODE_READ_WRITE );
473 0 : }
474 :
475 0 : fd_pod_insert_int( topo->props, "sandbox", config->development.sandbox ? 1 : 0 );
476 :
477 0 : for( ulong i=0UL; i<topo->tile_cnt; i++ ) {
478 0 : fd_topo_configure_tile( &topo->tiles[ i ], config );
479 0 : }
480 :
481 0 : fd_topob_finish( topo, CALLBACKS );
482 0 : }
483 :
484 : static void
485 : forktest_cmd_args( int * pargc,
486 : char *** pargv,
487 0 : args_t * args ) {
488 0 : args->forktest.no_watch = fd_env_strip_cmdline_contains( pargc, pargv, "--no-watch" );
489 0 : }
490 :
491 : static void
492 : forktest_fn( args_t * args,
493 0 : config_t * config ) {
494 0 : args_t c_args = configure_args();
495 0 : configure_cmd_fn( &c_args, config );
496 :
497 0 : initialize_workspaces( config );
498 0 : initialize_stacks( config );
499 :
500 0 : fd_topo_join_workspaces( &config->topo, FD_SHMEM_JOIN_MODE_READ_WRITE, FD_TOPO_CORE_DUMP_LEVEL_DISABLED );
501 0 : fd_topo_fill( &config->topo );
502 :
503 0 : ulong rnonce_ss_id = fd_pod_queryf_ulong( config->topo.props, ULONG_MAX, "rnonce_ss" );
504 0 : FD_TEST( rnonce_ss_id!=ULONG_MAX );
505 0 : void * shared_rnonce = fd_topo_obj_laddr( &config->topo, rnonce_ss_id );
506 0 : ulong * nonce_initialized = (ulong *)(sizeof(fd_rnonce_ss_t)+(uchar *)shared_rnonce);
507 0 : FD_TEST( fd_rng_secure( shared_rnonce, sizeof(fd_rnonce_ss_t) ) );
508 0 : FD_COMPILER_MFENCE();
509 0 : FD_VOLATILE( *nonce_initialized ) = 1UL;
510 :
511 0 : args_t watch_args;
512 0 : int pipefd[2];
513 0 : if( !args->forktest.no_watch ) {
514 0 : if( FD_UNLIKELY( pipe2( pipefd, O_NONBLOCK ) ) ) FD_LOG_ERR(( "pipe2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
515 :
516 0 : watch_args.watch.drain_output_fd = pipefd[0];
517 0 : if( FD_UNLIKELY( -1==dup2( pipefd[1], STDERR_FILENO ) ) ) FD_LOG_ERR(( "dup2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
518 0 : if( FD_UNLIKELY( -1==close( pipefd[1] ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
519 0 : }
520 :
521 0 : fd_topo_run_single_process( &config->topo, 2, config->uid, config->gid, fdctl_tile_run );
522 0 : if( args->forktest.no_watch ) {
523 0 : for(;;) pause();
524 0 : } else {
525 0 : watch_cmd_fn( &watch_args, config );
526 0 : }
527 0 : }
528 :
529 : action_t fd_action_forktest = {
530 : .name = "forktest",
531 : .args = forktest_cmd_args,
532 : .fn = forktest_fn,
533 : .perm = forktest_perm,
534 : .topo = forktest_topo,
535 : .is_local_cluster = 1
536 : };
|