Line data Source code
1 : #define _GNU_SOURCE
2 : #define FD_SCRATCH_USE_HANDHOLDING 1
3 : #include "../../fdctl/configure/configure.h"
4 :
5 : #include <math.h>
6 : #include <stdio.h>
7 : #include <unistd.h>
8 : #include <dirent.h>
9 : #include <sys/stat.h>
10 : #include <sys/wait.h>
11 :
12 : #include "../../../ballet/poh/fd_poh.h"
13 : #include "../../../disco/keyguard/fd_keyload.h"
14 : #include "../../../flamenco/features/fd_features.h"
15 : #include "../../../flamenco/genesis/fd_genesis_create.h"
16 : #include "../../../flamenco/types/fd_types_custom.h"
17 : #include "../../../flamenco/runtime/sysvar/fd_sysvar_clock.h"
18 : #include "../genesis_hash.h"
19 :
20 : #define NAME "genesis"
21 :
22 : /* default_enable_features is a table of features enabled by default */
23 :
24 : static void
25 0 : default_enable_features( fd_features_t * features ) {
26 0 : features->index_erasure_conflict_duplicate_proofs = 0UL;
27 0 : features->curve25519_restrict_msm_length = 0UL;
28 0 : features->commission_updates_only_allowed_in_first_half_of_epoch = 0UL;
29 0 : features->validate_fee_collector_account = 0UL;
30 0 : features->incremental_snapshot_only_incremental_hash_calculation = 0UL;
31 0 : features->timely_vote_credits = 0UL;
32 0 : features->apply_cost_tracker_during_replay = 0UL;
33 0 : features->reject_callx_r10 = 1UL;
34 0 : features->update_hashes_per_tick = 0UL;
35 0 : features->enable_partitioned_epoch_reward = 0UL;
36 0 : features->pico_inflation = 0UL;
37 0 : features->remaining_compute_units_syscall_enabled = 0UL;
38 0 : features->simplify_writable_program_account_check = 0UL;
39 0 : features->enable_bpf_loader_set_authority_checked_ix = 0UL;
40 0 : features->consume_blockstore_duplicate_proofs = 0UL;
41 0 : features->disable_deploy_of_alloc_free_syscall = 0UL;
42 0 : features->disable_bpf_loader_instructions = 0UL;
43 0 : features->full_inflation_enable = 0UL;
44 0 : features->vote_state_add_vote_latency = 0UL;
45 0 : features->curve25519_syscall_enabled = 0UL;
46 0 : features->error_on_syscall_bpf_function_hash_collisions = 0UL;
47 0 : features->update_hashes_per_tick3 = 0UL;
48 0 : features->update_hashes_per_tick4 = 0UL;
49 0 : features->enable_bpf_loader_extend_program_ix = 0UL;
50 0 : features->enable_program_runtime_v2_and_loader_v4 = 0UL;
51 0 : features->increase_tx_account_lock_limit = 0UL;
52 0 : features->stake_raise_minimum_delegation_to_1_sol = 0UL;
53 0 : features->enable_alt_bn128_syscall = 0UL;
54 0 : features->revise_turbine_epoch_stakes = 0UL;
55 0 : features->clean_up_delegation_errors = 0UL;
56 0 : features->update_hashes_per_tick5 = 0UL;
57 0 : features->full_inflation_vote = 0UL;
58 0 : features->skip_rent_rewrites = 0UL;
59 0 : features->switch_to_new_elf_parser = 0UL;
60 0 : features->require_rent_exempt_split_destination = 0UL;
61 0 : features->enable_turbine_fanout_experiments = 0UL;
62 0 : features->devnet_and_testnet = 0UL;
63 0 : features->enable_big_mod_exp_syscall = 0UL;
64 0 : features->enable_alt_bn128_compression_syscall = 0UL;
65 0 : features->update_hashes_per_tick2 = 0UL;
66 0 : features->bpf_account_data_direct_mapping = 0UL;
67 0 : features->relax_authority_signer_check_for_lookup_table_creation = 0UL;
68 0 : features->update_hashes_per_tick6 = 0UL;
69 0 : features->enable_poseidon_syscall = 0UL;
70 0 : features->better_error_codes_for_tx_lamport_check = 0UL;
71 0 : features->stake_minimum_delegation_for_rewards = 0UL;
72 0 : features->loosen_cpi_size_restriction = 0UL;
73 0 : features->drop_legacy_shreds = 0UL;
74 0 : features->deprecate_rewards_sysvar = 0UL;
75 0 : features->warp_timestamp_again = 0UL;
76 0 : features->reduce_stake_warmup_cooldown = 0UL;
77 0 : features->disable_turbine_fanout_experiments = 0UL;
78 0 : features->blake3_syscall_enabled = 0UL;
79 0 : features->last_restart_slot_sysvar = 0UL;
80 0 : features->disable_fees_sysvar = 0UL;
81 0 : }
82 :
83 : /* estimate_hashes_per_tick approximates the PoH hashrate of the current
84 : tile. Spins PoH hashing for estimate_dur_ns nanoseconds. Returns
85 : the hashes per tick achieved, where tick_mhz is the target tick rate
86 : in ticks per microsecond (MHz). Assumes that the estimate duration
87 : is larger than the tick duration. */
88 :
89 : static ulong
90 : estimate_hashes_per_tick( ulong tick_mhz,
91 0 : ulong estimate_dur_ns ) {
92 0 : ulong const batch = 1UL<<20;
93 0 : long const deadline = fd_log_wallclock() + (long)estimate_dur_ns;
94 :
95 0 : uchar poh_hash[ 32 ] = {0};
96 0 : ulong hash_cnt = 0UL;
97 0 : do {
98 0 : fd_poh_append( poh_hash, batch );
99 0 : hash_cnt += batch;
100 0 : } while( fd_log_wallclock() < deadline );
101 :
102 0 : double hash_cnt_dbl = (double)hash_cnt;
103 0 : double tick_cnt_dbl = (double)estimate_dur_ns / ( (double)tick_mhz * 1000.0 );
104 0 : if( tick_cnt_dbl < 1.0 ) return 0UL;
105 :
106 : /* Apply 50% factor to the maximum machine hash rate. */
107 0 : double hashes_per_tick = hash_cnt_dbl / tick_cnt_dbl / 2.0;
108 0 : return (ulong)hashes_per_tick;
109 0 : }
110 :
111 :
112 : /* Create a new genesis.bin file contents into the provided blob buffer
113 : and return the size of the buffer. Will abort on error if the
114 : provided buffer is not large enough. */
115 :
116 : static ulong
117 : create_genesis( config_t * const config,
118 : uchar * blob,
119 0 : ulong blob_sz ) {
120 :
121 0 : fd_genesis_options_t options[1];
122 :
123 : /* Read in keys */
124 :
125 0 : uchar const * identity_pubkey_ = fd_keyload_load( config->consensus.identity_path, 1 );
126 0 : if( FD_UNLIKELY( !identity_pubkey_ ) ) FD_LOG_ERR(( "Failed to load identity key" ));
127 0 : memcpy( options->identity_pubkey.key, identity_pubkey_, 32 );
128 :
129 0 : char file_path[ PATH_MAX ];
130 0 : FD_TEST( fd_cstr_printf_check( file_path, PATH_MAX, NULL, "%s/faucet.json", config->scratch_directory ) );
131 0 : uchar const * faucet_pubkey_ = fd_keyload_load( file_path, 1 );
132 0 : if( FD_UNLIKELY( !faucet_pubkey_ ) ) FD_LOG_ERR(( "Failed to load faucet key" ));
133 0 : memcpy( options->faucet_pubkey.key, faucet_pubkey_, 32 );
134 :
135 0 : FD_TEST( fd_cstr_printf_check( file_path, PATH_MAX, NULL, "%s/stake-account.json", config->scratch_directory ) );
136 0 : uchar const * stake_pubkey_ = fd_keyload_load( file_path, 1 );
137 0 : if( FD_UNLIKELY( !stake_pubkey_ ) ) FD_LOG_ERR(( "Failed to load stake account key" ));
138 0 : memcpy( options->stake_pubkey.key, stake_pubkey_, 32 );
139 :
140 0 : FD_TEST( fd_cstr_printf_check( file_path, PATH_MAX, NULL, "%s/vote-account.json", config->scratch_directory ) );
141 0 : uchar const * vote_pubkey_ = fd_keyload_load( file_path, 1 );
142 0 : if( FD_UNLIKELY( !vote_pubkey_ ) ) FD_LOG_ERR(( "Failed to load vote account key" ));
143 0 : memcpy( options->vote_pubkey.key, vote_pubkey_, 32 );
144 :
145 :
146 0 : options->creation_time = (ulong)fd_log_wallclock() / (ulong)1e9;
147 0 : options->faucet_balance = 500000000000000000UL;
148 0 : options->vote_account_stake = config->development.genesis.vote_account_stake_lamports;
149 :
150 : /* Set up PoH config */
151 :
152 0 : if( 0UL==config->development.genesis.hashes_per_tick ) {
153 :
154 : /* set hashes_per_tick to whatever machine is capable of */
155 0 : ulong hashes_per_tick =
156 0 : estimate_hashes_per_tick( config->development.genesis.target_tick_duration_micros,
157 0 : (ulong)3e9 /* 3 seconds */ );
158 :
159 0 : if( hashes_per_tick == 0UL ) {
160 0 : FD_LOG_WARNING(( "PoH rate estimation failed. Defaulting to %lu",
161 0 : FD_SYSVAR_CLOCK_DEFAULT_HASHES_PER_TICK ));
162 0 : hashes_per_tick = FD_SYSVAR_CLOCK_DEFAULT_HASHES_PER_TICK;
163 0 : }
164 :
165 0 : options->hashes_per_tick = hashes_per_tick;
166 :
167 0 : } else if( 1UL==config->development.genesis.hashes_per_tick ) {
168 :
169 : /* set hashes_per_tick field to 0, which means sleep mode */
170 0 : options->hashes_per_tick = 0UL;
171 :
172 0 : } else {
173 :
174 : /* set hashes_per_tick to the specified value */
175 0 : options->hashes_per_tick = config->development.genesis.hashes_per_tick;
176 :
177 0 : }
178 :
179 0 : options->ticks_per_slot = config->development.genesis.ticks_per_slot;
180 0 : options->target_tick_duration_micros = config->development.genesis.target_tick_duration_micros;
181 :
182 0 : options->fund_initial_accounts = config->development.genesis.fund_initial_accounts;
183 0 : options->fund_initial_amount_lamports = config->development.genesis.fund_initial_amount_lamports;
184 :
185 0 : options->warmup_epochs = config->development.genesis.warmup_epochs;
186 :
187 0 : fd_features_t features[1];
188 0 : fd_features_disable_all( features );
189 0 : uint version[] = {FD_DEFAULT_AGAVE_CLUSTER_VERSION_MAJOR, FD_DEFAULT_AGAVE_CLUSTER_VERSION_MINOR, FD_DEFAULT_AGAVE_CLUSTER_VERSION_PATCH};
190 0 : fd_features_enable_cleaned_up(features, version);
191 0 : default_enable_features( features );
192 :
193 0 : options->features = features;
194 :
195 : /* Serialize blob */
196 :
197 0 : static uchar scratch_smem[ 16<<20UL ]; /* fits at least 32k accounts */
198 0 : ulong scratch_fmem[ 4 ];
199 0 : fd_scratch_attach( scratch_smem, scratch_fmem,
200 0 : sizeof(scratch_smem), sizeof(scratch_fmem)/sizeof(ulong) );
201 :
202 0 : ulong blob_len = fd_genesis_create( blob, blob_sz, options );
203 0 : if( FD_UNLIKELY( !blob_sz ) ) FD_LOG_ERR(( "Failed to create genesis blob" ));
204 :
205 0 : fd_scratch_detach( NULL );
206 :
207 0 : fd_keyload_unload( identity_pubkey_, 1 );
208 0 : fd_keyload_unload( faucet_pubkey_, 1 );
209 0 : fd_keyload_unload( stake_pubkey_, 1 );
210 0 : fd_keyload_unload( vote_pubkey_, 1 );
211 :
212 0 : return blob_len;
213 0 : }
214 :
215 : static void
216 0 : init( config_t * const config ) {
217 0 : mkdir_all( config->ledger.path, config->uid, config->gid );
218 :
219 0 : static uchar blob[ 16<<20UL ];
220 0 : ulong blob_sz = create_genesis( config, blob, sizeof(blob) );
221 :
222 : /* Switch to target user in the configuration when creating the
223 : genesis.bin file so it is permissioned correctly. */
224 :
225 0 : gid_t gid = getgid();
226 0 : uid_t uid = getuid();
227 0 : if( FD_LIKELY( gid == 0 && setegid( config->gid ) ) )
228 0 : FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
229 0 : if( FD_LIKELY( uid == 0 && seteuid( config->uid ) ) )
230 0 : FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
231 :
232 0 : mode_t previous = umask( S_IRWXO | S_IRWXG );
233 :
234 0 : char genesis_path[ PATH_MAX ];
235 0 : FD_TEST( fd_cstr_printf_check( genesis_path, PATH_MAX, NULL, "%s/genesis.bin", config->ledger.path ) );
236 0 : do {
237 0 : FILE * genesis_file = fopen( genesis_path, "w" );
238 0 : FD_TEST( genesis_file );
239 0 : FD_TEST( 1L == fwrite( blob, blob_sz, 1L, genesis_file ) );
240 0 : FD_TEST( !fclose( genesis_file ) );
241 0 : } while(0);
242 :
243 0 : umask( previous );
244 :
245 0 : if( FD_UNLIKELY( seteuid( uid ) ) ) FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
246 0 : if( FD_UNLIKELY( setegid( gid ) ) ) FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
247 :
248 0 : uchar genesis_hash[ 32 ];
249 0 : char genesis_hash_cstr[ FD_BASE58_ENCODED_32_SZ ];
250 0 : ushort shred_version = compute_shred_version( genesis_path, genesis_hash );
251 :
252 0 : FD_LOG_INFO(( "Created %s: genesis_hash=%s sz=%lu",
253 0 : genesis_path,
254 0 : fd_base58_encode_32( genesis_hash, NULL, genesis_hash_cstr ),
255 0 : blob_sz ));
256 0 : FD_LOG_INFO(( "Shred version: %hu", shred_version ));
257 0 : }
258 :
259 : static void
260 : fini( config_t * const config,
261 0 : int pre_init ) {
262 0 : (void)pre_init;
263 :
264 0 : char genesis_path[ PATH_MAX ];
265 0 : FD_TEST( fd_cstr_printf_check( genesis_path, PATH_MAX, NULL, "%s/genesis.bin", config->ledger.path ) );
266 0 : if( FD_UNLIKELY( unlink( genesis_path ) && errno!=ENOENT ) )
267 0 : FD_LOG_ERR(( "could not remove genesis.bin file `%s` (%i-%s)", genesis_path, errno, fd_io_strerror( errno ) ));
268 0 : }
269 :
270 : static configure_result_t
271 0 : check( config_t * const config ) {
272 0 : char genesis_path[ PATH_MAX ];
273 0 : fd_cstr_printf_check( genesis_path, PATH_MAX, NULL, "%s/genesis.bin", config->ledger.path );
274 :
275 0 : struct stat st;
276 0 : if( FD_UNLIKELY( stat( genesis_path, &st ) && errno==ENOENT ) )
277 0 : NOT_CONFIGURED( "`%s` does not exist", genesis_path );
278 :
279 0 : CHECK( check_dir( config->ledger.path, config->uid, config->gid, S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR ) );
280 0 : CHECK( check_file( genesis_path, config->uid, config->gid, S_IFREG | S_IRUSR | S_IWUSR ) );
281 :
282 0 : PARTIALLY_CONFIGURED( "`%s` already exists", genesis_path );
283 0 : }
284 :
285 : configure_stage_t genesis = {
286 : .name = NAME,
287 : .init = init,
288 : .fini = fini,
289 : .check = check,
290 : /* It might be nice to not regenerate the genesis.bin if the
291 : parameters didn't change here, but it has a timestamp in it and
292 : also a variable number of hashes per tick in some configurations,
293 : which we would need to pull out and skip in the comparison, so we
294 : just always recreate it for now. */
295 : .always_recreate = 1,
296 : };
297 :
298 : #undef NAME
|