Line data Source code
1 : #define _GNU_SOURCE
2 : #include "../../../shared/commands/configure/configure.h"
3 :
4 : #include "../../../platform/fd_sys_util.h"
5 : #include "../../../platform/fd_file_util.h"
6 :
7 : #include "../../../../ballet/shred/fd_shred.h"
8 : #include "../../../../ballet/poh/fd_poh.h"
9 : #include "../../../../disco/shred/fd_shredder.h"
10 : #include "../../../../disco/tiles.h"
11 : #include "../../../../discof/genesis/genesis_hash.h"
12 :
13 : #include <unistd.h>
14 : #include <dirent.h>
15 : #include <sys/stat.h>
16 : #include <sys/wait.h>
17 :
18 : #define NAME "blockstore"
19 :
20 : extern void
21 : fd_ext_blockstore_create_block0( char const * ledger_path,
22 : ulong shred_cnt,
23 : uchar const * shred_bytes,
24 : ulong shred_sz,
25 : ulong stride );
26 :
27 0 : static inline void zero_signer( void * _1, uchar * sig, uchar const * _2 ) { (void)_1; (void)_2; memset( sig, '\0', 64UL ); }
28 :
29 : static void
30 0 : init( config_t const * config ) {
31 : /* The Agave validator cannot boot without a block 0 existing in the
32 : blockstore in the ledger directory, so we have to create one. This
33 : creates a directory "rocksdb" under which the blockstore with
34 : block0 is created. The entire directory should be included in the
35 : genesis archive "genesis.tar.bz2". */
36 :
37 : /* TODO: Parse genesis.bin and use those values instead. */
38 0 : ulong ticks_per_slot = config->development.genesis.ticks_per_slot;
39 0 : ulong hashes_per_tick = config->development.genesis.hashes_per_tick;
40 :
41 0 : char genesis_path[ PATH_MAX ];
42 0 : FD_TEST( fd_cstr_printf_check( genesis_path, PATH_MAX, NULL, "%s/genesis.bin", config->frankendancer.paths.ledger ) );
43 0 : uchar genesis_hash[ 32 ] = { 0 };
44 0 : ushort shred_version = 0;
45 0 : int result = compute_shred_version( genesis_path, &shred_version, genesis_hash );
46 0 : if( FD_UNLIKELY( -1==result && errno!=ENOENT ) ) FD_LOG_ERR(( "could not compute shred version from genesis file `%s` (%i-%s)", genesis_path, errno, fd_io_strerror( errno ) ));
47 :
48 :
49 : /* This is not a fundamental limit. It could be set as high as 663
50 : with no modifications to the rest of the code. It's set to 128
51 : because that seems more than enough and we don't need to consume
52 : that much stack space. It could be set even higher if you add
53 : multiple FEC sets. */
54 0 : #define GENESIS_MAX_TICKS_PER_SLOT 128UL
55 0 : FD_TEST( ticks_per_slot<GENESIS_MAX_TICKS_PER_SLOT );
56 0 : struct {
57 0 : ulong ticks_in_batch;
58 0 : fd_entry_batch_header_t ticks[ GENESIS_MAX_TICKS_PER_SLOT ];
59 0 : } batch;
60 :
61 0 : batch.ticks_in_batch = ticks_per_slot;
62 0 : uchar poh_hash[ 32 ] = {0};
63 0 : memcpy( poh_hash, genesis_hash, 32UL );
64 :
65 0 : for( ulong i=0UL; i<ticks_per_slot; i++ ) {
66 0 : fd_poh_append( poh_hash, hashes_per_tick );
67 :
68 0 : batch.ticks[ i ].hashcnt_delta = hashes_per_tick;
69 0 : batch.ticks[ i ].txn_cnt = 0UL;
70 0 : memcpy( batch.ticks[ i ].hash, poh_hash, 32UL );
71 0 : }
72 :
73 0 : ulong batch_sz = sizeof(ulong)+ticks_per_slot*sizeof(fd_entry_batch_header_t);
74 :
75 0 : FD_TEST( fd_shredder_count_data_shreds ( batch_sz, FD_SHRED_TYPE_MERKLE_DATA )<=34UL );
76 0 : FD_TEST( fd_shredder_count_parity_shreds( batch_sz, FD_SHRED_TYPE_MERKLE_CODE )<=34UL );
77 :
78 0 : fd_shred34_t data, parity;
79 0 : fd_fec_set_t fec;
80 0 : for( ulong i=0UL; i<34UL; i++ ) {
81 0 : fec.data_shreds [ i ] = data.pkts [ i ].buffer;
82 0 : fec.parity_shreds[ i ] = parity.pkts[ i ].buffer;
83 0 : }
84 0 : for( ulong i=34UL; i<FD_REEDSOL_DATA_SHREDS_MAX; i++ ) fec.data_shreds [ i ] = NULL;
85 0 : for( ulong i=34UL; i<FD_REEDSOL_PARITY_SHREDS_MAX; i++ ) fec.parity_shreds[ i ] = NULL;
86 :
87 0 : fd_entry_batch_meta_t meta[ 1 ] = {{
88 0 : .parent_offset = 0UL,
89 0 : .reference_tick = ticks_per_slot,
90 0 : .block_complete = 1
91 0 : }};
92 :
93 0 : fd_shredder_t _shredder[ 1 ];
94 0 : fd_shredder_t * shredder = fd_shredder_join( fd_shredder_new( _shredder, zero_signer, NULL ) );
95 0 : fd_shredder_set_shred_version( shredder, shred_version );
96 :
97 0 : uchar chained_merkle_root[ FD_SHRED_MERKLE_ROOT_SZ ] = { 0 };
98 0 : fd_shredder_init_batch( shredder, &batch, batch_sz, 0UL, meta );
99 0 : fd_shredder_next_fec_set( shredder, &fec, /* chained */ chained_merkle_root, NULL );
100 :
101 : /* Fork off a new process for inserting the shreds to the blockstore.
102 : RocksDB creates a dozen background workers, and doesn't close them
103 : by default. This would cause problems for sandboxing because you
104 : can't unshare a user namespace once multi-threaded, so just do it
105 : in another process that we can then terminate. */
106 :
107 0 : pid_t pid = fork();
108 0 : if( FD_UNLIKELY( pid == -1 ) ) FD_LOG_ERR(( "fork() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
109 :
110 0 : if( FD_LIKELY( !pid ) ) {
111 : /* Switch to target user in the configuration when creating the
112 : genesis.bin file so it is permissioned correctly. */
113 0 : gid_t gid = getgid();
114 0 : uid_t uid = getuid();
115 0 : if( FD_LIKELY( gid == 0 && setegid( config->gid ) ) )
116 0 : FD_LOG_ERR(( "setegid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
117 0 : if( FD_LIKELY( uid == 0 && seteuid( config->uid ) ) )
118 0 : FD_LOG_ERR(( "seteuid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
119 :
120 0 : umask( S_IRWXO | S_IRWXG );
121 :
122 0 : fd_ext_blockstore_create_block0( config->frankendancer.paths.ledger, fec.data_shred_cnt, (uchar const *)data.pkts, FD_SHRED_MIN_SZ, FD_SHRED_MAX_SZ );
123 :
124 0 : fd_sys_util_exit_group( 0 );
125 0 : } else {
126 0 : int wstatus;
127 0 : if( FD_UNLIKELY( waitpid( pid, &wstatus, 0 )==-1 ) ) FD_LOG_ERR(( "waitpid() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
128 0 : if( FD_UNLIKELY( WIFSIGNALED( wstatus ) ) )
129 0 : FD_LOG_ERR(( "genesis blockstore creation process terminated by signal %i-%s", WTERMSIG( wstatus ), fd_io_strsignal( WTERMSIG( wstatus ) ) ));
130 0 : if( FD_UNLIKELY( WEXITSTATUS( wstatus ) ) )
131 0 : FD_LOG_ERR(( "genesis blockstore creation process exited with status %i", WEXITSTATUS( wstatus ) ));
132 0 : }
133 :
134 0 : }
135 :
136 : static int
137 : fini( config_t const * config,
138 0 : int pre_init FD_PARAM_UNUSED ) {
139 0 : DIR * dir = opendir( config->frankendancer.paths.ledger );
140 0 : if( FD_UNLIKELY( !dir ) ) {
141 0 : if( errno == ENOENT ) return 0;
142 0 : FD_LOG_ERR(( "opendir `%s` failed (%i-%s)", config->frankendancer.paths.ledger, errno, fd_io_strerror( errno ) ));
143 0 : }
144 :
145 0 : struct dirent * entry;
146 0 : errno = 0;
147 0 : while(( entry = readdir( dir ) )) {
148 0 : if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
149 :
150 : /* genesis.bin managed by genesis stage*/
151 0 : if( FD_LIKELY( !strcmp( entry->d_name, "genesis.bin" ) ) ) continue;
152 :
153 0 : char path1[ PATH_MAX ];
154 0 : FD_TEST( fd_cstr_printf_check( path1, PATH_MAX, NULL, "%s/%s", config->frankendancer.paths.ledger, entry->d_name ) );
155 :
156 0 : struct stat st;
157 0 : if( FD_UNLIKELY( lstat( path1, &st ) ) ) {
158 0 : if( FD_LIKELY( errno == ENOENT ) ) continue;
159 0 : FD_LOG_ERR(( "stat `%s` failed (%i-%s)", path1, errno, fd_io_strerror( errno ) ));
160 0 : }
161 :
162 0 : if( FD_UNLIKELY( S_ISDIR( st.st_mode ) ) ) {
163 0 : if( FD_UNLIKELY( -1==fd_file_util_rmtree( path1, 1 ) ) ) FD_LOG_ERR(( "rmtree `%s` failed (%i-%s)", path1, errno, fd_io_strerror( errno ) ));
164 0 : } else {
165 0 : if( FD_UNLIKELY( unlink( path1 ) && errno != ENOENT ) )
166 0 : FD_LOG_ERR(( "unlink `%s` failed (%i-%s)", path1, errno, fd_io_strerror( errno ) ));
167 0 : }
168 0 : }
169 :
170 0 : if( FD_UNLIKELY( errno && errno!=ENOENT ) ) FD_LOG_ERR(( "readdir `%s` failed (%i-%s)", config->frankendancer.paths.ledger, errno, fd_io_strerror( errno ) ));
171 0 : if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir `%s` failed (%i-%s)", config->frankendancer.paths.ledger, errno, fd_io_strerror( errno ) ));
172 :
173 0 : return 1;
174 0 : }
175 :
176 : static configure_result_t
177 : check( config_t const * config,
178 0 : int check_type FD_PARAM_UNUSED ) {
179 0 : int has_non_genesis = 0;
180 :
181 0 : DIR * dir = opendir( config->frankendancer.paths.ledger );
182 0 : if( FD_UNLIKELY( !dir ) ) {
183 0 : if( FD_UNLIKELY( errno==ENOENT ) ) NOT_CONFIGURED( "ledger directory does not exist at `%s`", config->frankendancer.paths.ledger );
184 0 : FD_LOG_ERR(( "opendir `%s` failed (%i-%s)", config->frankendancer.paths.ledger, errno, fd_io_strerror( errno ) ));
185 0 : }
186 :
187 0 : struct dirent * entry;
188 0 : errno = 0;
189 0 : while(( entry = readdir( dir ) )) {
190 0 : if( FD_LIKELY( !strcmp( entry->d_name, "." ) || !strcmp( entry->d_name, ".." ) ) ) continue;
191 :
192 : /* genesis.bin managed by genesis stage*/
193 0 : if( FD_LIKELY( !strcmp( entry->d_name, "genesis.bin" ) ) ) continue;
194 0 : if( FD_LIKELY( !strcmp( entry->d_name, "rocksdb" ) ) ) {
195 0 : char rocksdb_path[ PATH_MAX ];
196 0 : fd_cstr_printf_check( rocksdb_path, PATH_MAX, NULL, "%s/rocksdb", config->frankendancer.paths.ledger );
197 :
198 0 : configure_result_t result = check_dir( rocksdb_path, config->uid, config->gid, S_IFDIR | S_IRUSR | S_IWUSR | S_IXUSR );
199 0 : if( FD_UNLIKELY( result.result != CONFIGURE_OK ) ) {
200 0 : if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir `%s` failed (%i-%s)", config->frankendancer.paths.ledger, errno, fd_io_strerror( errno ) ));
201 0 : return result;
202 0 : }
203 0 : }
204 :
205 0 : has_non_genesis = 1;
206 0 : break;
207 0 : }
208 :
209 0 : if( FD_UNLIKELY( errno && errno!=ENOENT ) ) FD_LOG_ERR(( "readdir `%s` failed (%i-%s)", config->frankendancer.paths.ledger, errno, fd_io_strerror( errno ) ));
210 0 : if( FD_UNLIKELY( closedir( dir ) ) ) FD_LOG_ERR(( "closedir `%s` failed (%i-%s)", config->frankendancer.paths.ledger, errno, fd_io_strerror( errno ) ));
211 :
212 0 : if( FD_LIKELY( has_non_genesis ) ) {
213 0 : PARTIALLY_CONFIGURED( "rocksdb directory exists at `%s`", config->frankendancer.paths.ledger );
214 0 : } else {
215 0 : NOT_CONFIGURED( "rocksdb directory does not exist at `%s`", config->frankendancer.paths.ledger );
216 0 : }
217 0 : }
218 :
219 : configure_stage_t fd_cfg_stage_blockstore = {
220 : .name = NAME,
221 : .always_recreate = 1,
222 : .init = init,
223 : .fini = fini,
224 : .check = check,
225 : };
226 :
227 : #undef NAME
|