Line data Source code
1 : #include "../../disco/topo/fd_topo.h"
2 : #include "../../funk/fd_funk.h"
3 : #include "../../flamenco/types/fd_types.h"
4 : #include "../../ballet/lthash/fd_lthash.h"
5 : #include "../../ballet/sha256/fd_sha256.h"
6 : #include "../../flamenco/runtime/fd_acc_mgr.h"
7 : #include "../../flamenco/runtime/fd_hashes.h"
8 :
9 : #include <errno.h>
10 : #include <fcntl.h>
11 : #include <sys/stat.h>
12 : #include <unistd.h>
13 :
14 : #include "generated/fd_genesi_tile_seccomp.h"
15 : struct fd_genesi_tile {
16 : int fd;
17 :
18 : fd_funk_t funk[1];
19 :
20 : ushort shred_version;
21 : uchar genesis_hash[ 32UL ];
22 :
23 : fd_lthash_value_t lthash[1];
24 :
25 : int bootstrap;
26 : int shutdown;
27 :
28 : ulong genesis_sz;
29 : uchar genesis[ 10UL*1024UL*1024UL ] __attribute__((aligned(alignof(fd_genesis_solana_global_t)))); /* 10 MiB buffer for decoded genesis */
30 : uchar buffer[ 10UL*1024UL*1024UL ]; /* 10 MiB buffer for reading genesis file */
31 :
32 : fd_wksp_t * out_mem;
33 : ulong out_chunk0;
34 : ulong out_wmark;
35 : ulong out_chunk;
36 : };
37 :
38 : typedef struct fd_genesi_tile fd_genesi_tile_t;
39 :
40 : FD_FN_CONST static inline ulong
41 0 : scratch_align( void ) {
42 0 : return alignof( fd_genesi_tile_t );
43 0 : }
44 :
45 : FD_FN_PURE static inline ulong
46 0 : scratch_footprint( fd_topo_tile_t const * tile ) {
47 0 : (void)tile;
48 :
49 0 : ulong l = FD_LAYOUT_INIT;
50 0 : l = FD_LAYOUT_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t ) );
51 0 : return FD_LAYOUT_FINI( l, scratch_align() );
52 0 : }
53 :
54 : static inline int
55 0 : should_shutdown( fd_genesi_tile_t * ctx ) {
56 0 : return ctx->shutdown;
57 0 : }
58 :
59 : static void
60 0 : initialize_accdb( fd_genesi_tile_t * ctx ) {
61 : /* Change 'last published' XID to 0 */
62 0 : fd_funk_txn_xid_t root_xid; fd_funk_txn_xid_set_root( &root_xid );
63 0 : fd_funk_txn_xid_t target_xid = { .ul = { 0UL, 0UL } };
64 0 : fd_funk_txn_prepare( ctx->funk, &root_xid, &target_xid );
65 0 : fd_funk_txn_publish( ctx->funk, &target_xid );
66 :
67 0 : fd_genesis_solana_global_t * genesis = fd_type_pun( ctx->genesis );
68 :
69 0 : fd_pubkey_account_pair_global_t const * accounts = fd_genesis_solana_accounts_join( genesis );
70 :
71 0 : for( ulong i=0UL; i<genesis->accounts_len; i++ ) {
72 0 : fd_pubkey_account_pair_global_t const * account = &accounts[ i ];
73 :
74 0 : fd_funk_rec_prepare_t prepare;
75 :
76 0 : FD_TXN_ACCOUNT_DECL( rec );
77 0 : int err = fd_txn_account_init_from_funk_mutable( rec,
78 0 : &account->key,
79 0 : ctx->funk,
80 0 : &target_xid,
81 0 : 1, /* do_create */
82 0 : account->account.data_len,
83 0 : &prepare );
84 0 : FD_TEST( !err );
85 :
86 0 : fd_txn_account_set_data( rec, fd_solana_account_data_join( &account->account ), account->account.data_len );
87 0 : fd_txn_account_set_lamports( rec, account->account.lamports );
88 0 : fd_txn_account_set_executable( rec, account->account.executable );
89 0 : fd_txn_account_set_owner( rec, &account->account.owner );
90 0 : fd_txn_account_mutable_fini( rec, ctx->funk, &prepare );
91 :
92 0 : fd_lthash_value_t new_hash[1];
93 0 : fd_hashes_account_lthash( rec->pubkey, fd_txn_account_get_meta( rec ), fd_txn_account_get_data( rec ), new_hash );
94 0 : fd_lthash_add( ctx->lthash, new_hash );
95 0 : }
96 0 : }
97 :
98 : static void
99 : after_credit( fd_genesi_tile_t * ctx,
100 : fd_stem_context_t * stem,
101 : int * opt_poll_in,
102 0 : int * charge_busy ) {
103 0 : (void)opt_poll_in;
104 :
105 0 : if( FD_UNLIKELY( ctx->shutdown ) ) return;
106 :
107 0 : *charge_busy = 1;
108 :
109 0 : initialize_accdb( ctx );
110 :
111 0 : uchar * dst = fd_chunk_to_laddr( ctx->out_mem, ctx->out_chunk );
112 0 : fd_memcpy( dst, &ctx->lthash->bytes, sizeof(fd_lthash_value_t) );
113 0 : fd_memcpy( dst+sizeof(fd_lthash_value_t), &ctx->genesis_hash, sizeof(fd_hash_t) );
114 0 : fd_memcpy( dst+sizeof(fd_lthash_value_t)+sizeof(fd_hash_t), ctx->genesis, ctx->genesis_sz );
115 :
116 0 : fd_stem_publish( stem, 0UL, ctx->shred_version, ctx->out_chunk, 0UL, 0UL, 0UL, 0UL );
117 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, ctx->genesis_sz+sizeof(fd_hash_t)+sizeof(fd_lthash_value_t), ctx->out_chunk0, ctx->out_wmark );
118 :
119 0 : ctx->shutdown = 1;
120 0 : }
121 :
122 : static void
123 : process_local_genesis( fd_genesi_tile_t * ctx,
124 : char const * genesis_path,
125 0 : ushort expected_shred_version ) {
126 0 : struct stat st;
127 0 : int err = fstat( ctx->fd, &st );
128 0 : if( FD_UNLIKELY( -1==err ) ) FD_LOG_ERR(( "stat() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
129 :
130 0 : ulong size = (ulong)st.st_size;
131 :
132 0 : if( FD_UNLIKELY( size>sizeof(ctx->buffer) ) ) FD_LOG_ERR(( "genesis file `%s` too large (%lu bytes, max %lu)", genesis_path, size, (ulong)sizeof(ctx->buffer) ));
133 :
134 0 : ulong bytes_read = 0UL;
135 0 : while( bytes_read<size ) {
136 0 : long result = read( ctx->fd, ctx->buffer+bytes_read, size-bytes_read );
137 0 : if( FD_UNLIKELY( -1==result ) ) FD_LOG_ERR(( "read() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
138 0 : if( FD_UNLIKELY( !result ) ) FD_LOG_ERR(( "read() returned 0 before reading full file" ));
139 0 : bytes_read += (ulong)result;
140 0 : }
141 :
142 0 : FD_TEST( bytes_read==size );
143 :
144 0 : if( FD_UNLIKELY( -1==close( ctx->fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));
145 :
146 0 : fd_bincode_decode_ctx_t decode_ctx = {
147 0 : .data = ctx->buffer,
148 0 : .dataend = ctx->buffer+size,
149 0 : };
150 :
151 0 : ctx->genesis_sz = 0UL;
152 0 : err = fd_genesis_solana_decode_footprint( &decode_ctx, &ctx->genesis_sz );
153 0 : if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) FD_LOG_ERR(( "malformed genesis file at `%s`", genesis_path ));
154 0 : if( FD_UNLIKELY( ctx->genesis_sz>sizeof(ctx->genesis) ) ) FD_LOG_ERR(( "genesis file at `%s` decode footprint too large (%lu bytes, max %lu)", genesis_path, ctx->genesis_sz, sizeof(ctx->genesis) ));
155 :
156 0 : fd_genesis_solana_global_t * genesis = fd_genesis_solana_decode_global( ctx->genesis, &decode_ctx );
157 0 : FD_TEST( genesis );
158 :
159 0 : union {
160 0 : uchar c[ 32 ];
161 0 : ushort s[ 16 ];
162 0 : } hash;
163 0 : fd_sha256_hash( ctx->buffer, size, hash.c );
164 :
165 0 : fd_memcpy( ctx->genesis_hash, hash.c, 32UL );
166 :
167 0 : ushort xor = 0;
168 0 : for( ulong i=0UL; i<16UL; i++ ) xor ^= hash.s[ i ];
169 :
170 0 : xor = fd_ushort_bswap( xor );
171 0 : xor = fd_ushort_if( xor<USHORT_MAX, (ushort)(xor + 1), USHORT_MAX );
172 :
173 0 : ctx->shred_version = xor;
174 0 : FD_TEST( ctx->shred_version );
175 :
176 0 : if( FD_UNLIKELY( ctx->bootstrap && expected_shred_version && expected_shred_version!=ctx->shred_version ) ) {
177 0 : FD_LOG_ERR(( "This node is bootstrapping the cluster as it has no gossip entrypoints provided, but "
178 0 : "a [consensus.expected_shred_version] of %hu is provided which does not match the shred "
179 0 : "version of %hu computed from the genesis.bin file at `%s`",
180 0 : expected_shred_version, ctx->shred_version, genesis_path ));
181 0 : }
182 0 : }
183 :
184 : static void
185 : privileged_init( fd_topo_t * topo,
186 0 : fd_topo_tile_t * tile ) {
187 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
188 :
189 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
190 0 : fd_genesi_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t ) );
191 :
192 0 : ctx->fd = open( tile->genesi.genesis_path, O_RDONLY|O_CLOEXEC );
193 0 : if( FD_UNLIKELY( -1==ctx->fd ) ) {
194 0 : if( FD_LIKELY( errno==ENOENT ) ) {
195 0 : if( FD_UNLIKELY( !tile->genesi.entrypoints_cnt ) ) {
196 0 : FD_LOG_ERR(( "This node is bootstrapping the cluster as it has no gossip entrypoints provided, but "
197 0 : "the genesis.bin file at `%s` does not exist. Please provide a valid genesis.bin "
198 0 : "file by running genesis, or join an existing cluster.",
199 0 : tile->genesi.genesis_path ));
200 0 : } else {
201 0 : if( FD_UNLIKELY( !tile->genesi.allow_download ) ) {
202 0 : FD_LOG_ERR(( "There is no genesis.bin file at `%s` and automatic downloading is disabled as "
203 0 : "genesis_download is false in your configuration file. Please either provide a valid "
204 0 : "genesis.bin file locally, or allow donwloading from a gossip entrypoint.",
205 0 : tile->genesi.genesis_path ));
206 0 : } else {
207 0 : FD_LOG_WARNING(( "UNIMPLEMENTED: automatic downloading of genesis.bin from gossip entrypoints is not yet implemented. "
208 0 : "expected_genesis_hash and shred_version will not be verified." ));
209 0 : }
210 0 : }
211 0 : } else {
212 0 : FD_LOG_ERR(( "could not open genesis.bin file at `%s` (%i-%s)", tile->genesi.genesis_path, errno, fd_io_strerror( errno ) ));
213 0 : }
214 0 : }
215 0 : }
216 :
217 : static void
218 : unprivileged_init( fd_topo_t * topo,
219 0 : fd_topo_tile_t * tile ) {
220 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
221 :
222 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
223 0 : fd_genesi_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t ) );
224 :
225 0 : FD_TEST( fd_funk_join( ctx->funk, fd_topo_obj_laddr( topo, tile->genesi.funk_obj_id ) ) );
226 :
227 0 : fd_lthash_zero( ctx->lthash );
228 :
229 0 : ctx->shutdown = !!tile->genesi.entrypoints_cnt;
230 0 : ctx->bootstrap = !!tile->genesi.entrypoints_cnt;
231 0 : if( FD_LIKELY( -1!=ctx->fd ) ) process_local_genesis( ctx, tile->genesi.genesis_path, tile->genesi.expected_shred_version );
232 :
233 0 : ctx->out_mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
234 0 : ctx->out_chunk0 = fd_dcache_compact_chunk0( ctx->out_mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
235 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 );
236 0 : ctx->out_chunk = ctx->out_chunk0;
237 :
238 0 : ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, 1UL );
239 0 : if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
240 0 : FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
241 0 : }
242 :
243 : static ulong
244 : populate_allowed_seccomp( fd_topo_t const * topo,
245 : fd_topo_tile_t const * tile,
246 : ulong out_cnt,
247 0 : struct sock_filter * out ) {
248 :
249 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
250 :
251 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
252 0 : fd_genesi_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t ) );
253 :
254 0 : populate_sock_filter_policy_fd_genesi_tile( out_cnt, out, (uint)fd_log_private_logfile_fd(), (uint)ctx->fd );
255 0 : return sock_filter_policy_fd_genesi_tile_instr_cnt;
256 0 : }
257 :
258 : static ulong
259 : populate_allowed_fds( fd_topo_t const * topo,
260 : fd_topo_tile_t const * tile,
261 : ulong out_fds_cnt,
262 0 : int * out_fds ) {
263 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
264 :
265 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
266 0 : fd_genesi_tile_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_genesi_tile_t ), sizeof( fd_genesi_tile_t ) );
267 :
268 0 : if( FD_UNLIKELY( out_fds_cnt<3UL ) ) FD_LOG_ERR(( "out_fds_cnt %lu", out_fds_cnt ));
269 :
270 0 : ulong out_cnt = 0UL;
271 0 : out_fds[ out_cnt++ ] = 2; /* stderr */
272 0 : if( FD_LIKELY( -1!=fd_log_private_logfile_fd() ) )
273 0 : out_fds[ out_cnt++ ] = fd_log_private_logfile_fd(); /* logfile */
274 0 : if( FD_LIKELY( ctx->fd!=-1 ) ) out_fds[ out_cnt++ ] = ctx->fd;
275 0 : return out_cnt;
276 0 : }
277 :
278 0 : #define STEM_BURST (1UL)
279 :
280 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_genesi_tile_t
281 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_genesi_tile_t)
282 :
283 0 : #define STEM_CALLBACK_AFTER_CREDIT after_credit
284 : #define STEM_CALLBACK_SHOULD_SHUTDOWN should_shutdown
285 :
286 : #include "../../disco/stem/fd_stem.c"
287 :
288 : fd_topo_run_tile_t fd_tile_genesi = {
289 : .name = "genesi",
290 : .populate_allowed_seccomp = populate_allowed_seccomp,
291 : .populate_allowed_fds = populate_allowed_fds,
292 : .scratch_align = scratch_align,
293 : .scratch_footprint = scratch_footprint,
294 : .privileged_init = privileged_init,
295 : .unprivileged_init = unprivileged_init,
296 : .run = stem_run,
297 : };
|