Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 23 additions & 53 deletions src/discof/genesis/fd_genesi_tile.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
#include "fd_genesis_client.h"
#include "../../disco/topo/fd_topo.h"
#include "../../ballet/sha256/fd_sha256.h"
#include "../../flamenco/runtime/fd_txn_account.h"
#include "../../flamenco/runtime/fd_genesis_parse.h"
#include "../../flamenco/accdb/fd_accdb_admin.h"
#include "../../flamenco/accdb/fd_accdb_impl_v1.h"
#include "../../flamenco/runtime/fd_hashes.h"
Expand Down Expand Up @@ -68,7 +68,7 @@ struct fd_genesi_tile {
ushort expected_shred_version;

ulong genesis_sz;
uchar genesis[ GENESIS_MAX_SZ ] __attribute__((aligned(alignof(fd_genesis_solana_global_t)))); /* 10 MiB buffer for decoded genesis */
uchar genesis[ GENESIS_MAX_SZ ] __attribute__((aligned(alignof(fd_genesis_t)))); /* 10 MiB buffer for decoded genesis */
uchar buffer[ GENESIS_MAX_SZ ]; /* 10 MiB buffer for reading genesis file */

char genesis_path[ PATH_MAX ];
Expand Down Expand Up @@ -120,44 +120,38 @@ initialize_accdb( fd_genesi_tile_t * ctx ) {
/* Insert accounts at root */
fd_funk_txn_xid_t root_xid; fd_funk_txn_xid_set_root( &root_xid );

fd_genesis_solana_global_t * genesis = fd_type_pun( ctx->genesis );

fd_pubkey_account_pair_global_t const * accounts = fd_genesis_solana_accounts_join( genesis );
fd_genesis_t * genesis = fd_type_pun( ctx->genesis );

fd_funk_t * funk = fd_accdb_user_v1_funk( ctx->accdb );
for( ulong i=0UL; i<genesis->accounts_len; i++ ) {
fd_pubkey_account_pair_global_t const * account = &accounts[ i ];
fd_genesis_account_t * account = fd_type_pun( (uchar *)genesis + genesis->accounts_off[ i ] );

/* FIXME use accdb API */
/* FIXME: use accdb API */
fd_funk_rec_prepare_t prepare[1];
fd_funk_rec_key_t key[1]; memcpy( key->uc, account->key.uc, sizeof(fd_pubkey_t) );
fd_funk_rec_key_t key[1]; memcpy( key->uc, account->pubkey, sizeof(fd_pubkey_t) );
fd_funk_rec_t * rec = fd_funk_rec_prepare( funk, &root_xid, key, prepare, NULL );
FD_TEST( rec );
fd_account_meta_t * meta = fd_funk_val_truncate( rec, funk->alloc, funk->wksp, 16UL, sizeof(fd_account_meta_t)+account->account.data_len, NULL );
fd_account_meta_t * meta = fd_funk_val_truncate( rec, funk->alloc, funk->wksp, 16UL, sizeof(fd_account_meta_t)+account->meta.dlen, NULL );
FD_TEST( meta );
void * data = (void *)( meta+1 );
fd_memcpy( meta->owner, account->account.owner.uc, sizeof(fd_pubkey_t) );
meta->lamports = account->account.lamports;
fd_memcpy( meta->owner, account->meta.owner, sizeof(fd_pubkey_t) );
meta->lamports = account->meta.lamports;
meta->slot = 0UL;
meta->executable = !!account->account.executable;
meta->dlen = (uint)account->account.data_len;
fd_memcpy( data, fd_solana_account_data_join( &account->account ), account->account.data_len );
meta->executable = !!account->meta.executable;
meta->dlen = (uint)account->meta.dlen;
fd_memcpy( data, account->data, account->meta.dlen );
fd_funk_rec_publish( funk, prepare );

fd_lthash_value_t new_hash[1];
fd_hashes_account_lthash( &account->key, meta, data, new_hash );
fd_hashes_account_lthash( fd_type_pun( account->pubkey ), meta, data, new_hash );
fd_lthash_add( ctx->lthash, new_hash );
}
}

static inline void
verify_cluster_type( fd_genesis_solana_global_t const * genesis,
uchar const * genesis_hash,
char const * genesis_path ) {
#define TESTNET (0)
#define MAINNET (1)
#define DEVNET (2)
#define DEVELOPMENT (3)
verify_cluster_type( fd_genesis_t const * genesis,
uchar const * genesis_hash,
char const * genesis_path ) {

uchar mainnet_hash[ 32 ];
FD_TEST( fd_base58_decode_32( "5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpKuc147dw2N9d", mainnet_hash ) );
Expand All @@ -169,21 +163,21 @@ verify_cluster_type( fd_genesis_solana_global_t const * genesis,
FD_TEST( fd_base58_decode_32( "EtWTRABZaYq6iMfeYKouRu166VU2xqa1wcaWoxPkrZBG", devnet_hash ) );

switch( genesis->cluster_type ) {
case MAINNET: {
case FD_GENESIS_TYPE_MAINNET: {
if( FD_UNLIKELY( memcmp( genesis_hash, mainnet_hash, 32UL ) ) ) {
FD_LOG_ERR(( "genesis file `%s` has cluster type MAINNET but unexpected genesis hash `%s`",
genesis_path, FD_BASE58_ENC_32_ALLOCA( genesis_hash ) ));
}
break;
}
case TESTNET: {
case FD_GENESIS_TYPE_TESTNET: {
if( FD_UNLIKELY( memcmp( genesis_hash, testnet_hash, 32UL ) ) ) {
FD_LOG_ERR(( "genesis file `%s` has cluster type TESTNET but unexpected genesis hash `%s`",
genesis_path, FD_BASE58_ENC_32_ALLOCA( genesis_hash ) ));
}
break;
}
case DEVNET: {
case FD_GENESIS_TYPE_DEVNET: {
if( FD_UNLIKELY( memcmp( genesis_hash, devnet_hash, 32UL ) ) ) {
FD_LOG_ERR(( "genesis file `%s` has cluster type DEVNET but unexpected genesis hash `%s`",
genesis_path, FD_BASE58_ENC_32_ALLOCA( genesis_hash ) ));
Expand All @@ -193,11 +187,6 @@ verify_cluster_type( fd_genesis_solana_global_t const * genesis,
default:
break;
}

#undef TESTNET
#undef MAINNET
#undef DEVNET
#undef DEVELOPMENT
}

static void
Expand Down Expand Up @@ -283,19 +272,10 @@ after_credit( fd_genesi_tile_t * ctx,
}

FD_TEST( !ctx->bootstrap );

fd_bincode_decode_ctx_t decode_ctx = {
.data = decompressed+512UL,
.dataend = decompressed+512UL+fd_tar_meta_get_size( meta ),
};
ulong size = 512UL+fd_tar_meta_get_size( meta );

ctx->genesis_sz = 0UL;
int err = fd_genesis_solana_decode_footprint( &decode_ctx, &ctx->genesis_sz );
if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) FD_LOG_ERR(( "malformed genesis file from peer at http://" FD_IP4_ADDR_FMT ":%hu", FD_IP4_ADDR_FMT_ARGS( peer.addr ), peer.port ));
if( FD_UNLIKELY( ctx->genesis_sz>sizeof(ctx->genesis) ) ) FD_LOG_ERR(( "genesis file at `%s` decode footprint too large (%lu bytes, max %lu)", ctx->genesis_path, ctx->genesis_sz, sizeof(ctx->genesis) ));

fd_genesis_solana_global_t * genesis = fd_genesis_solana_decode_global( ctx->genesis, &decode_ctx );
FD_TEST( genesis );
fd_genesis_t * genesis = fd_genesis_parse( ctx->buffer, size, &ctx->genesis_sz, ctx->genesis );

verify_cluster_type( genesis, hash, ctx->genesis_path );

Expand All @@ -320,7 +300,7 @@ after_credit( fd_genesi_tile_t * ctx,
char basename_partial[ PATH_MAX ];
FD_TEST( fd_cstr_printf_check( basename_partial, PATH_MAX, NULL, "%s.partial", basename ) );

err = renameat2( ctx->out_dir_fd, basename_partial, ctx->out_dir_fd, basename, RENAME_NOREPLACE );
int err = renameat2( ctx->out_dir_fd, basename_partial, ctx->out_dir_fd, basename, RENAME_NOREPLACE );
if( FD_UNLIKELY( -1==err ) ) FD_LOG_ERR(( "renameat2() failed (%i-%s)", errno, fd_io_strerror( errno ) ));

FD_LOG_NOTICE(( "retrieved genesis `%s` from peer at http://" FD_IP4_ADDR_FMT ":%hu/genesis.tar.bz2",
Expand Down Expand Up @@ -353,18 +333,8 @@ process_local_genesis( fd_genesi_tile_t * ctx,

if( FD_UNLIKELY( -1==close( ctx->in_fd ) ) ) FD_LOG_ERR(( "close() failed (%i-%s)", errno, fd_io_strerror( errno ) ));

fd_bincode_decode_ctx_t decode_ctx = {
.data = ctx->buffer,
.dataend = ctx->buffer+size,
};

ctx->genesis_sz = 0UL;
err = fd_genesis_solana_decode_footprint( &decode_ctx, &ctx->genesis_sz );
if( FD_UNLIKELY( err!=FD_BINCODE_SUCCESS ) ) FD_LOG_ERR(( "malformed genesis file at `%s`", genesis_path ));
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) ));

fd_genesis_solana_global_t * genesis = fd_genesis_solana_decode_global( ctx->genesis, &decode_ctx );
FD_TEST( genesis );
fd_genesis_t * genesis = fd_genesis_parse( ctx->buffer, size, &ctx->genesis_sz, ctx->genesis );

union {
uchar c[ 32 ];
Expand Down
3 changes: 2 additions & 1 deletion src/discof/replay/fd_replay_tile.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

#include "../../flamenco/runtime/fd_runtime.h"
#include "../../flamenco/runtime/fd_runtime_stack.h"
#include "../../flamenco/runtime/fd_genesis_parse.h"
#include "../../flamenco/fd_flamenco_base.h"
#include "../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h"

Expand Down Expand Up @@ -1267,7 +1268,7 @@ boot_genesis( fd_replay_tile_t * ctx,
// TODO: Do not pass the fd_types type between tiles, it have offsets
// that are unsafe and can't be validated as being in-bounds. Need to
// pass an actual owned genesis type.
fd_genesis_solana_global_t const * genesis = fd_type_pun( (uchar*)fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk )+sizeof(fd_hash_t)+sizeof(fd_lthash_value_t) );
fd_genesis_t const * genesis = fd_type_pun( (uchar*)fd_chunk_to_laddr( ctx->in[ in_idx ].mem, chunk )+sizeof(fd_hash_t)+sizeof(fd_lthash_value_t) );

fd_bank_t * bank = fd_banks_bank_query( ctx->banks, FD_REPLAY_BOOT_BANK_IDX );
FD_TEST( bank );
Expand Down
3 changes: 3 additions & 0 deletions src/flamenco/fd_flamenco_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,9 @@ typedef struct fd_txn_out fd_txn_out_t;
struct fd_log_collector;
typedef struct fd_log_collector fd_log_collector_t;

struct fd_genesis;
typedef struct fd_genesis fd_genesis_t;

struct fd_account_meta {
uchar owner[32];
ulong lamports;
Expand Down
1 change: 0 additions & 1 deletion src/flamenco/genesis/fd_genesis_create.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include "../runtime/fd_system_ids.h"
#include "../runtime/program/fd_stake_program.h"
#include "../runtime/program/fd_vote_program.h"
#include "../runtime/sysvar/fd_sysvar_clock.h"
#include "../runtime/sysvar/fd_sysvar_rent.h"
#include "../types/fd_types.h"

Expand Down
3 changes: 3 additions & 0 deletions src/flamenco/runtime/Local.mk
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ $(call add-objs,fd_compute_budget_details,fd_flamenco)
$(call add-hdrs,fd_borrowed_account.h)
$(call add-objs,fd_borrowed_account,fd_flamenco)

$(call add-hdrs,fd_genesis_parse.h)
$(call add-objs,fd_genesis_parse,fd_flamenco)

$(call add-hdrs,fd_txn_account.h)
$(call add-objs,fd_txn_account,fd_flamenco)
ifdef FD_HAS_INT128
Expand Down
139 changes: 139 additions & 0 deletions src/flamenco/runtime/fd_genesis_parse.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#include "fd_genesis_parse.h"
#include "../../util/bits/fd_bits.h"

/* Adapted from fd_txn_parse.c */
#define CHECK_INIT( payload, payload_sz, offset ) \
uchar const * _payload = (payload); \
ulong const _payload_sz = (payload_sz); \
ulong const _offset = (offset); \
ulong _i = (offset); \
(void) _payload; \
(void) _offset; \

#define CHECK( cond ) do { \
if( FD_UNLIKELY( !(cond) ) ) { \
return 0; \
} \
} while( 0 )

#define CHECK_LEFT( n ) CHECK( (n)<=(_payload_sz-_i) )

#define INC( n ) (_i += (ulong)(n))
#define CUR_OFFSET ((ushort)_i)
#define CURSOR (_payload+_i)


fd_genesis_t *
fd_genesis_parse( uchar * payload,
ulong payload_sz,
ulong * sz_out,
uchar * genesis_out ) {
FD_SCRATCH_ALLOC_INIT( l, genesis_out );
fd_genesis_t * genesis = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_genesis_t), sizeof(fd_genesis_t) );

CHECK_INIT( payload, payload_sz, 0U );

CHECK_LEFT( 8U ); genesis->creation_time = FD_LOAD( ulong, CURSOR ); INC( 8U );

CHECK_LEFT( 8U ); genesis->accounts_len = FD_LOAD( ulong, CURSOR ); INC( 8U );
if( FD_UNLIKELY( genesis->accounts_len>FD_GENESIS_ACCOUNT_MAX_COUNT ) ) {
FD_LOG_ERR(( "genesis accounts length %lu exceeds supported max count %lu", genesis->accounts_len, FD_GENESIS_ACCOUNT_MAX_COUNT ));
}
for( ulong i=0UL; i<genesis->accounts_len; i++ ) {
fd_genesis_account_t * account = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_genesis_account_t), sizeof(fd_genesis_account_t) );
genesis->accounts_off[ i ] = (uint)( (ulong)account - (ulong)genesis );
if( FD_UNLIKELY( genesis->accounts_off[ i ] + sizeof(fd_genesis_account_t) > FD_GENESIS_MAX_MESSAGE_SIZE ) ) {
FD_LOG_ERR(( "genesis accounts offset %u exceeds supported max size %lu", genesis->accounts_off[ i ], FD_GENESIS_MAX_MESSAGE_SIZE ));
}
CHECK_LEFT( 32U ); fd_memcpy( account->pubkey, CURSOR, 32U ); INC( 32U );
CHECK_LEFT( 8U ); account->meta.lamports = FD_LOAD( ulong, CURSOR ); INC( 8U );
CHECK_LEFT( 8U ); account->meta.dlen = (uint)FD_LOAD( ulong, CURSOR ); INC( 8U );
if( FD_UNLIKELY( genesis->accounts_off[ i ] + sizeof(fd_genesis_account_t) + account->meta.dlen > FD_GENESIS_MAX_MESSAGE_SIZE ) ) {
FD_LOG_ERR(( "genesis builtin account data length %lu exceeds supported max size %lu", genesis->accounts_off[ i ] + sizeof(fd_genesis_account_t) + account->meta.dlen, FD_GENESIS_MAX_MESSAGE_SIZE ));
}
uchar * data = FD_SCRATCH_ALLOC_APPEND( l, alignof(uchar), account->meta.dlen );
CHECK_LEFT( account->meta.dlen ); fd_memcpy( data, CURSOR, account->meta.dlen ); INC( account->meta.dlen );
CHECK_LEFT( 32U ); fd_memcpy( account->meta.owner, CURSOR, 32U ); INC( 32U );
CHECK_LEFT( 1U ); account->meta.executable = FD_LOAD( uchar, CURSOR ); INC( 1U );
CHECK_LEFT( 8U ); INC( 8U ); /* don't care about rent epoch */
}

CHECK_LEFT( 8U ); genesis->builtin_len = FD_LOAD( ulong, CURSOR ); INC( 8U );
if( FD_UNLIKELY( genesis->builtin_len>FD_GENESIS_BUILTIN_MAX_COUNT ) ) {
FD_LOG_ERR(( "genesis builtin length %lu exceeds supported max count %lu", genesis->builtin_len, FD_GENESIS_BUILTIN_MAX_COUNT ));
}
for( ulong i=0UL; i<genesis->builtin_len; i++ ) {
fd_genesis_account_t * account = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_genesis_account_t), sizeof(fd_genesis_account_t) );
genesis->builtin_off[ i ] = (uint)( (ulong)account - (ulong)genesis );
if( FD_UNLIKELY( genesis->builtin_off[ i ] + sizeof(fd_genesis_account_t) > FD_GENESIS_MAX_MESSAGE_SIZE ) ) {
FD_LOG_ERR(( "genesis builtin offset %lu exceeds supported max size %lu", genesis->builtin_off[ i ] + sizeof(fd_genesis_account_t), FD_GENESIS_MAX_MESSAGE_SIZE ));
}
CHECK_LEFT( 32U ); fd_memcpy( account->pubkey, CURSOR, 32U ); INC( 32U );
CHECK_LEFT( 8U ); account->meta.lamports = FD_LOAD( ulong, CURSOR ); INC( 8U );
CHECK_LEFT( 8U ); account->meta.dlen = (uint)FD_LOAD( ulong, CURSOR ); INC( 8U );
if( FD_UNLIKELY( genesis->builtin_off[ i ] + sizeof(fd_genesis_account_t) + account->meta.dlen > FD_GENESIS_MAX_MESSAGE_SIZE ) ) {
FD_LOG_ERR(( "genesis builtin account data length %lu exceeds supported max size %lu", genesis->builtin_off[ i ] + sizeof(fd_genesis_account_t) + account->meta.dlen, FD_GENESIS_MAX_MESSAGE_SIZE ));
}
uchar * data = FD_SCRATCH_ALLOC_APPEND( l, alignof(uchar), account->meta.dlen );
CHECK_LEFT( account->meta.dlen ); fd_memcpy( data, CURSOR, account->meta.dlen ); INC( account->meta.dlen );
CHECK_LEFT( 32U ); fd_memcpy( account->meta.owner, CURSOR, 32U ); INC( 32U );
CHECK_LEFT( 1U ); account->meta.executable = FD_LOAD( uchar, CURSOR ); INC( 1U );
CHECK_LEFT( 8U ); INC( 8U ); /* don't care about rent epoch */
}

*sz_out = (ulong)(FD_SCRATCH_ALLOC_FINI( l, alignof(fd_genesis_t) )) - (ulong)genesis_out;

CHECK_LEFT( 8U ); ulong rewards_len = FD_LOAD( ulong, CURSOR ); INC( 8U );
for( ulong i=0UL; i<rewards_len; i++ ) {
fd_genesis_account_t * account = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_genesis_account_t), sizeof(fd_genesis_account_t) );
CHECK_LEFT( 32U ); INC( 32U ); /* pubkey */
CHECK_LEFT( 8U ); INC( 8U ); /* lamports */
CHECK_LEFT( 8U ); INC( 8U ); /* dlen */
CHECK_LEFT( account->meta.dlen ); INC( account->meta.dlen ); /* data */
CHECK_LEFT( 32U ); INC( 32U ); /* owner */
CHECK_LEFT( 1U ); INC( 1U ); /* executable */
CHECK_LEFT( 8U ); INC( 8U ); /* rent epoch */
}

CHECK_LEFT( 8U ); genesis->poh.ticks_per_slot = FD_LOAD( ulong, CURSOR ); INC( 8U );

CHECK_LEFT( sizeof(ulong) ); INC( sizeof(ulong) ); /* unused */

CHECK_LEFT( 8U ); genesis->poh.tick_duration_secs = FD_LOAD( ulong, CURSOR ); INC( 8U );
CHECK_LEFT( 4U ); genesis->poh.tick_duration_ns = FD_LOAD( uint, CURSOR ); INC( 4U );
CHECK_LEFT( 1U ); int has_target_tick_count = FD_LOAD( uchar, CURSOR ); INC( 1U );
if( has_target_tick_count ) { CHECK_LEFT( 8U ); genesis->poh.target_tick_count = FD_LOAD( ulong, CURSOR ); INC( 8U ); }
else genesis->poh.target_tick_count = 0UL;
CHECK_LEFT( 1U ); int has_hashes_per_tick = FD_LOAD( uchar, CURSOR ); INC( 1U );
if( has_hashes_per_tick ) { CHECK_LEFT( 8U ); genesis->poh.hashes_per_tick = FD_LOAD( ulong, CURSOR ); INC( 8U ); }
else genesis->poh.hashes_per_tick = 0UL;

CHECK_LEFT( sizeof(ulong) ); INC( sizeof(ulong) ); /* bakcward compat v23 */

CHECK_LEFT( 8U ); genesis->fee_rate_governor.target_lamports_per_signature = FD_LOAD( ulong, CURSOR ); INC( 8U );
CHECK_LEFT( 8U ); genesis->fee_rate_governor.target_signatures_per_slot = FD_LOAD( ulong, CURSOR ); INC( 8U );
CHECK_LEFT( 8U ); genesis->fee_rate_governor.min_lamports_per_signature = FD_LOAD( ulong, CURSOR ); INC( 8U );
CHECK_LEFT( 8U ); genesis->fee_rate_governor.max_lamports_per_signature = FD_LOAD( ulong, CURSOR ); INC( 8U );
CHECK_LEFT( 1U ); genesis->fee_rate_governor.burn_percent = FD_LOAD( uchar, CURSOR ); INC( 1U );

CHECK_LEFT( 8U ); genesis->rent.lamports_per_uint8_year = FD_LOAD( ulong, CURSOR ); INC( 8U );
CHECK_LEFT( 8U ); genesis->rent.exemption_threshold = FD_LOAD( double, CURSOR ); INC( 8U );
CHECK_LEFT( 1U ); genesis->rent.burn_percent = FD_LOAD( uchar, CURSOR ); INC( 1U );

CHECK_LEFT( 8U ); genesis->inflation.initial = FD_LOAD( double, CURSOR ); INC( 8U );
CHECK_LEFT( 8U ); genesis->inflation.terminal = FD_LOAD( double, CURSOR ); INC( 8U );
CHECK_LEFT( 8U ); genesis->inflation.taper = FD_LOAD( double, CURSOR ); INC( 8U );
CHECK_LEFT( 8U ); genesis->inflation.foundation = FD_LOAD( double, CURSOR ); INC( 8U );
CHECK_LEFT( 8U ); genesis->inflation.foundation_term = FD_LOAD( double, CURSOR ); INC( 8U );
CHECK_LEFT( 8U ); INC( 8U ); /* unused */

CHECK_LEFT( 8U ); genesis->epoch_schedule.slots_per_epoch = FD_LOAD( ulong, CURSOR ); INC( 8U );
CHECK_LEFT( 8U ); genesis->epoch_schedule.leader_schedule_slot_offset = FD_LOAD( ulong, CURSOR ); INC( 8U );
CHECK_LEFT( 1U ); genesis->epoch_schedule.warmup = FD_LOAD( uchar, CURSOR ); INC( 1U );
CHECK_LEFT( 8U ); genesis->epoch_schedule.first_normal_epoch = FD_LOAD( ulong, CURSOR ); INC( 8U );
CHECK_LEFT( 8U ); genesis->epoch_schedule.first_normal_slot = FD_LOAD( ulong, CURSOR ); INC( 8U );

CHECK_LEFT( 4U ); genesis->cluster_type = FD_LOAD( uint, CURSOR ); INC( 4U );

return genesis;
}
Loading
Loading