diff --git a/src/app/fdctl/config.c b/src/app/fdctl/config.c index 104bd95eb5..bc3df46636 100644 --- a/src/app/fdctl/config.c +++ b/src/app/fdctl/config.c @@ -86,7 +86,7 @@ static int parse_key_value( config_t * config, } \ *dst = '\0'; \ char * endptr; \ - unsigned long int result = strtoul( value, &endptr, 10 ); \ + ulong result = strtoul( value, &endptr, 10 ); \ if( FD_UNLIKELY( *endptr != '\0' || result > UINT_MAX ) ) { \ FD_LOG_ERR(( "invalid value for %s.%s: `%s`", section, key, value )); \ return 1; \ @@ -96,6 +96,30 @@ static int parse_key_value( config_t * config, } \ } while( 0 ) +#define ENTRY_ULONG(edot, esection, ekey) do { \ + if( FD_UNLIKELY( !strcmp( section, #esection ) && !strcmp( key, #ekey ) ) ) { \ + if( FD_UNLIKELY( strlen( value ) < 1 ) ) { \ + FD_LOG_ERR(( "invalid value for %s.%s: `%s`", section, key, value )); \ + return 1; \ + } \ + char * src = value; \ + char * dst = value; \ + while( *src ) { \ + if( *src != '_' ) *dst++ = *src; \ + src++; \ + } \ + *dst = '\0'; \ + char * endptr; \ + ulong result = strtoul( value, &endptr, 10 ); \ + if( FD_UNLIKELY( *endptr!='\0' ) ) { \ + FD_LOG_ERR(( "invalid value for %s.%s: `%s`", section, key, value )); \ + return 1; \ + } \ + config->esection edot ekey = result; \ + return 1; \ + } \ + } while( 0 ) + #define ENTRY_VUINT(edot, esection, ekey) do { \ if( FD_UNLIKELY( !strcmp( section, #esection ) && !strcmp( key, #ekey ) ) ) { \ if( FD_UNLIKELY( strlen( value ) < 1 ) ) { \ @@ -254,7 +278,13 @@ static int parse_key_value( config_t * config, ENTRY_STR ( ., development.netns, interface1_mac ); ENTRY_STR ( ., development.netns, interface1_addr ); - ENTRY_BOOL ( ., development.gossip, allow_private_address ); + ENTRY_BOOL ( ., development.gossip, allow_private_address ); + + ENTRY_UINT ( ., development.genesis, hashes_per_tick ); + ENTRY_UINT ( ., development.genesis, target_tick_duration_micros ); + ENTRY_UINT ( ., development.genesis, ticks_per_slot ); + ENTRY_UINT ( ., development.genesis, fund_initial_accounts ); + ENTRY_ULONG ( ., development.genesis, fund_initial_amount_lamports ); /* We have encountered a token that is not recognized, return 0 to indicate failure. */ return 0; diff --git a/src/app/fdctl/config.h b/src/app/fdctl/config.h index ce2b8b96e9..0a61f35991 100644 --- a/src/app/fdctl/config.h +++ b/src/app/fdctl/config.h @@ -133,6 +133,7 @@ typedef struct { int no_solana_labs; int bootstrap; uint debug_tile; + struct { int enabled; char interface0 [ 256 ]; @@ -142,9 +143,18 @@ typedef struct { char interface1_mac [ 32 ]; char interface1_addr[ 32 ]; } netns; + struct { int allow_private_address; } gossip; + + struct { + ulong hashes_per_tick; + ulong target_tick_duration_micros; + ulong ticks_per_slot; + ulong fund_initial_accounts; + ulong fund_initial_amount_lamports; + } genesis; } development; struct { diff --git a/src/app/fdctl/config/default.toml b/src/app/fdctl/config/default.toml index cca1366ae5..6f8e8031a5 100644 --- a/src/app/fdctl/config/default.toml +++ b/src/app/fdctl/config/default.toml @@ -721,15 +721,13 @@ dynamic_port_range = "8900-9000" # a period of time. Once this timeout is reached the connection # will be terminated. # - # An idle connection will be terminated if it remains idle longer than - # some threshold. "idle_timeout_millis" represents this threshold in - # milliseconds + # An idle connection will be terminated if it remains idle + # longer than this threshold. idle_timeout_millis = 10000 - # Whether QUIC RETRY is enabled - # QUIC RETRY is a feature to combat New Connection Request spamming - # - # This is a boolean + # QUIC retry is a feature to combat new connection request + # spamming. See rfc9000 8.1.2 for more details. This flag + # determines whether the feature is enabled in the validator. retry = true # Verify tiles perform signature verification of incoming @@ -820,8 +818,7 @@ dynamic_port_range = "8900-9000" # endpoint. [tiles.metric] # The port to listen on for HTTP request for Prometheus metrics. - # Firedancer serves metrics at a URI like - # 127.0.0.1:7999/metrics + # Firedancer serves metrics at a URI like 127.0.0.1:7999/metrics prometheus_listen_port = 7999 # These options can be useful for development, but should not be used @@ -852,28 +849,29 @@ dynamic_port_range = "8900-9000" # `--no-clone` argument to `fddev`. no_clone = false - # Firedancer currently hosts a Solana Labs client as a child process when - # it starts up, to provide functionality that has not yet been implemented. - # For development sometimes it is desirable to not launch this subprocess, - # although it will prevent the validator from operating correctly. + # Firedancer currently hosts a Solana Labs client as a child process + # when it starts up, to provide functionality that has not yet been + # implemented. For development sometimes it is desirable to not + # launch this subprocess, although it will prevent the validator + # from operating correctly. # - # In development, you can disable solana for testing and debugging with - # the `--no-solana-labs` argument to `fddev`. + # In development, you can disable solana for testing and debugging + # with the `--no-solana-labs` argument to `fddev`. no_solana_labs = false - # Sometimes, it may be useful to run a bootstrap firedancer validator, - # either for development or for testing purposes. The `fddev` tool is - # provided for this purpose, which creates the bootstrap keys and does - # the cluster genesis using some parameters that are typically useful - # for development. + # Sometimes, it may be useful to run a bootstrap firedancer + # validator, either for development or for testing purposes. The + # `fddev` tool is provided for this purpose, which creates the + # bootstrap keys and does the cluster genesis using some parameters + # that are typically useful for development. # # Enabling this allows de-coupling the genesis and key creation from - # the validator startup. The bootstrap validator can then be started - # up with `fdctl`. It will expect the genesis to already exist at - # [ledger.path]. The keys used during genesis should be the same as - # the ones supplied in the [consensus.identity_path] and - # [consensus.vote_account_path]. This option will not be effective if - # [gossip.entrypoints] is non-empty. + # the validator startup. The bootstrap validator can then be + # started up with `fdctl`. It will expect the genesis to already + # exist at [ledger.path]. The keys used during genesis should be + # the same as the ones supplied in the [consensus.identity_path] and + # [consensus.vote_account_path]. This option will not be effective + # if [gossip.entrypoints] is non-empty. bootstrap = false # It can be convenient during development to use a network namespace @@ -918,8 +916,59 @@ dynamic_port_range = "8900-9000" interface1_mac = "52:F1:7E:DA:2C:E1" # IP address of the second network namespace. interface1_addr = "198.18.0.2" + [development.gossip] - # Under normal operating conditions, a validator should always reach out to a host located on the public internet. - # This configuration item allows Firedancer to gossip with nodes located on a private internet (rfc1918). - # This option is passed to the Solana Labs client with the `--allow-private-addr` flag. + # Under normal operating conditions, a validator should always + # reach out to a host located on the public internet. If this + # value is true, it allows the validator to gossip with nodes + # configuration item allows Firedancer to gossip with nodes + # located on a private internet (rfc1918). + # + # This option is passed to the Solana Labs client with the + # `--allow-private-addr` flag. allow_private_address = false + + [development.genesis] + # When creating a new chain from genesis during development, + # this option can be used to specify the number of hashes in + # each tick of the proof history component. + # + # A value of one is the default, and will be used to mean that + # the proof of history component will run in low power mode, + # and use one hash per tick. This is equivalent to a value of + # "sleep" when providing hashes-per-tick to the Solana Labs + # genesis. + # + # A value of zero means the genesis will automatically determine + # the number of hashes in each tick based on how many hashes + # the generating computer can do in the target tick duration + # specified below. + # + # A value of 12500 is the same as mainnet, devnet, and testnet. + # + # This value specifies the initial value for chain in the + # genesis, but it might be immediately overriden at runtime if + # the related features which increase this value are enabled. + # The features are like update_hashes_per_tick2. + hashes_per_tick = 1 + + # How long each tick of the proof of history component should + # take, in microseconds. This value specifies the initial value + # of the chain and it will not change at runtime. The default + # value used here is the same as mainnet, devnet, and testnet. + target_tick_duration_micros = 6250 + + # The number of ticks in each slot. This value specifies the + # initial value of the chain and it will not change at runtime. + # The default value used here is the same as mainnet, devnet, + # and testnet. + ticks_per_slot = 64 + + # The count of accounts to pre-fund in the genesis block with + # SOL. Useful for benchmarking and development. The specific + # accounts that will be funded are those with private-keys of + # 0, 1, 2, .... N. + fund_initial_accounts = 1024 + + # The amount of SOL to pre-fund each account with. + fund_initial_amount_lamports = 50000000000000 diff --git a/src/app/fddev/bench.c b/src/app/fddev/bench.c index e6626a4817..1e4cb61f1e 100644 --- a/src/app/fddev/bench.c +++ b/src/app/fddev/bench.c @@ -60,6 +60,7 @@ solana_labs_thread_main( void * _args ) { static void add_bench_topo( fd_topo_t * topo, + ulong accounts_cnt, ushort send_to_port, uint send_to_ip_addr, ushort rpc_port, @@ -75,7 +76,10 @@ add_bench_topo( fd_topo_t * topo, fd_topo_tile_t *bencho = fd_topob_tile( topo, "bencho", "bench", "bench", "bench", USHORT_MAX, 0, "bencho_out", 0 ); bencho->bencho.rpc_port = rpc_port; bencho->bencho.rpc_ip_addr = rpc_ip_addr; - for( ulong i=0UL; ibenchg.accounts_cnt = accounts_cnt; + } fd_topo_tile_t * benchs = fd_topob_tile( topo, "benchs", "bench", "bench", "bench", USHORT_MAX, 0, NULL, 0 ); benchs->benchs.send_to_ip_addr = send_to_ip_addr; benchs->benchs.send_to_port = send_to_port; @@ -93,7 +97,8 @@ bench_cmd_fn( args_t * args, config_t * const config ) { (void)args; - add_bench_topo( &config->topo, config->tiles.quic.regular_transaction_listen_port, config->tiles.net.ip_addr, + add_bench_topo( &config->topo, config->development.genesis.fund_initial_accounts, + config->tiles.quic.regular_transaction_listen_port, config->tiles.net.ip_addr, config->rpc.port, config->tiles.net.ip_addr ); if( FD_LIKELY( !args->dev.no_configure ) ) { diff --git a/src/app/fddev/configure/genesis.c b/src/app/fddev/configure/genesis.c index 400ff9e9b5..74d552f7ee 100644 --- a/src/app/fddev/configure/genesis.c +++ b/src/app/fddev/configure/genesis.c @@ -42,40 +42,44 @@ init( config_t * const config ) { char initial_accounts[ PATH_MAX ]; FD_TEST( fd_cstr_printf_check( initial_accounts, PATH_MAX, NULL, "%s/initial-accounts.yaml", config->scratch_directory ) ); - FILE * f = fopen( initial_accounts, "w" ); - FD_TEST( f ); - for( ulong i=0UL; i<128UL; i++ ) { - /* Fund the first 128 accounts with some lamports for benchmarking... temporary hack */ - uchar privkey[ 64 ] = {0}; - privkey[ 0 ] = (uchar)i; - uchar pubkey[ 64 ]; - fd_sha512_t sha[1]; - fd_sha512_join( fd_sha512_new( sha ) ); - fd_ed25519_public_from_private( pubkey, privkey, sha ); - - char pubkey_encoded[ FD_BASE58_ENCODED_32_SZ ]; - fd_base58_encode_32( pubkey, NULL, pubkey_encoded ); - FD_TEST( fprintf( f, "%s:\n", pubkey_encoded ) >= 0 ); - FD_TEST( fprintf( f, " balance: 500000000000000\n" ) >= 0 ); - FD_TEST( fprintf( f, " owner: 11111111111111111111111111111111\n" ) >= 0 ); - FD_TEST( fprintf( f, " data: ~\n" ) >= 0 ); - FD_TEST( fprintf( f, " executable: false\n" ) >= 0 ); - } - FD_TEST( !fclose( f ) ); - ADD1( "fddev" ); ADD( "--faucet-pubkey", faucet ); - ADD( "--hashes-per-tick", "sleep" ); - // MAINNET VALUES - // ADD( "--hashes-per-tick", "12500" ); - // ADD( "--target-tick-duration", "6" ); - // ADD( "--ticks-per-slot", "64" ); ADDU( "--faucet-lamports", 500000000000000000UL ); + ADD( "--bootstrap-validator", config->consensus.identity_path ); ADD1( vote ); ADD1( stake ); ADD( "--ledger", config->ledger.path ); ADD( "--cluster-type", "development" ); - ADD( "--primordial-accounts-file", initial_accounts ); + + if( 0UL==config->development.genesis.hashes_per_tick ) ADD( "--hashes-per-tick", "auto" ); + else if( 1UL==config->development.genesis.hashes_per_tick ) ADD( "--hashes-per-tick", "sleep" ); + else ADDU( "--hashes-per-tick", config->development.genesis.hashes_per_tick ); + + ADDU( "--target-tick-duration", config->development.genesis.target_tick_duration_micros ); + ADDU( "--ticks-per-slot", config->development.genesis.ticks_per_slot ); + + if( FD_LIKELY( config->development.genesis.fund_initial_accounts ) ) { + FILE * f = fopen( initial_accounts, "w" ); + FD_TEST( f ); + for( ulong i=0UL; idevelopment.genesis.fund_initial_accounts; i++ ) { + uchar privkey[ 32 ] = {0}; + FD_STORE( ulong, privkey, i ); + uchar pubkey[ 32 ]; + fd_sha512_t sha[1]; + fd_sha512_join( fd_sha512_new( sha ) ); + fd_ed25519_public_from_private( pubkey, privkey, sha ); + + char pubkey_encoded[ FD_BASE58_ENCODED_32_SZ ]; + fd_base58_encode_32( pubkey, NULL, pubkey_encoded ); + FD_TEST( fprintf( f, "%s:\n", pubkey_encoded ) >= 0 ); + FD_TEST( fprintf( f, " balance: %lu\n", config->development.genesis.fund_initial_amount_lamports ) >= 0 ); + FD_TEST( fprintf( f, " owner: 11111111111111111111111111111111\n" ) >= 0 ); + FD_TEST( fprintf( f, " data: ~\n" ) >= 0 ); + FD_TEST( fprintf( f, " executable: false\n" ) >= 0 ); + } + FD_TEST( !fclose( f ) ); + ADD( "--primordial-accounts-file", initial_accounts ); + } /* these are copied out of the output of `solana/fetch-spl.sh` ... need to figure out what to do here long term. */ diff --git a/src/app/fddev/tiles/fd_benchg.c b/src/app/fddev/tiles/fd_benchg.c index 2a7474921a..4be1c1d1d0 100644 --- a/src/app/fddev/tiles/fd_benchg.c +++ b/src/app/fddev/tiles/fd_benchg.c @@ -1,5 +1,7 @@ #include "../../fdctl/run/tiles/tiles.h" +#include "../../../flamenco/types/fd_types_custom.h" + #include typedef struct { @@ -13,9 +15,9 @@ typedef struct { int has_recent_blockhash; uchar recent_blockhash[ 32 ]; - ulong sender_cnt; - uchar sender_public_key[ 128UL ][ 32UL ]; - uchar sender_private_key[ 128UL ][ 32UL ]; + ulong acct_cnt; + fd_pubkey_t * acct_public_keys; + fd_pubkey_t * acct_private_keys; fd_wksp_t * mem; ulong out_chunk0; @@ -33,6 +35,8 @@ scratch_footprint( fd_topo_tile_t const * tile ) { (void)tile; ulong l = FD_LAYOUT_INIT; l = FD_LAYOUT_APPEND( l, alignof( fd_benchg_ctx_t ), sizeof( fd_benchg_ctx_t ) ); + l = FD_LAYOUT_APPEND( l, alignof( fd_pubkey_t ), sizeof( fd_pubkey_t ) * tile->benchg.accounts_cnt ); + l = FD_LAYOUT_APPEND( l, alignof( fd_pubkey_t ), sizeof( fd_pubkey_t ) * tile->benchg.accounts_cnt ); return FD_LAYOUT_FINI( l, scratch_align() ); } @@ -89,24 +93,24 @@ after_credit( void * _ctx, .lamports = ctx->lamport_idx, /* Unique per transaction so they aren't duplicates */ }; - ulong receiver_idx = fd_rng_ulong_roll( ctx->rng, ctx->sender_cnt-1UL ); + ulong receiver_idx = fd_rng_ulong_roll( ctx->rng, ctx->acct_cnt-1UL ); receiver_idx = fd_ulong_if( receiver_idx>=ctx->sender_idx, receiver_idx+1UL, receiver_idx ); - fd_memcpy( transfer->fee_payer, ctx->sender_public_key[ ctx->sender_idx ], 32UL ); - fd_memcpy( transfer->dest_acct, ctx->sender_public_key[ receiver_idx ], 32UL ); + fd_memcpy( transfer->fee_payer, ctx->acct_public_keys[ ctx->sender_idx ].uc, 32UL ); + fd_memcpy( transfer->dest_acct, ctx->acct_public_keys[ receiver_idx ].uc, 32UL ); fd_memcpy( transfer->recent_blockhash, ctx->recent_blockhash, 32UL ); fd_ed25519_sign( transfer->signature, &(transfer->_sig_cnt), sizeof(*transfer)-65UL, - ctx->sender_public_key[ ctx->sender_idx ], - ctx->sender_private_key[ ctx->sender_idx ], + ctx->acct_public_keys[ ctx->sender_idx ].uc, + ctx->acct_private_keys[ ctx->sender_idx ].uc, ctx->sha ); fd_mux_publish( mux, 0UL, ctx->out_chunk, sizeof(*transfer), 0UL, 0UL, 0UL ); ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, sizeof(*transfer), ctx->out_chunk0, ctx->out_wmark ); - ctx->sender_idx = (ctx->sender_idx + 1UL) % ctx->sender_cnt; + ctx->sender_idx = (ctx->sender_idx + 1UL) % ctx->acct_cnt; if( FD_UNLIKELY( !ctx->sender_idx ) ) { if( FD_UNLIKELY( ctx->changed_blockhash ) ) ctx->lamport_idx = 1UL; else ctx->lamport_idx++; @@ -143,15 +147,17 @@ unprivileged_init( fd_topo_t * topo, void * scratch ) { FD_SCRATCH_ALLOC_INIT( l, scratch ); fd_benchg_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_benchg_ctx_t ), sizeof( fd_benchg_ctx_t ) ); + ctx->acct_public_keys = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_pubkey_t ), sizeof( fd_pubkey_t ) * tile->benchg.accounts_cnt ); + ctx->acct_private_keys = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_pubkey_t ), sizeof( fd_pubkey_t ) * tile->benchg.accounts_cnt ); FD_TEST( fd_rng_join( fd_rng_new( ctx->rng, 0UL, 0UL ) ) ); FD_TEST( fd_sha512_join( fd_sha512_new( ctx->sha ) ) ); - ctx->sender_cnt = 128UL; - for( ulong i=0UL; isender_cnt; i++ ) { - fd_memset( ctx->sender_private_key[ i ], 0, 32UL ); - ctx->sender_private_key[ i ][ 0 ] = (uchar)i; - fd_ed25519_public_from_private( ctx->sender_public_key[ i ], ctx->sender_private_key[ i ] , ctx->sha ); + ctx->acct_cnt = tile->benchg.accounts_cnt; + for( ulong i=0UL; iacct_cnt; i++ ) { + fd_memset( ctx->acct_private_keys[ i ].uc, 0, 32UL ); + FD_STORE( ulong, ctx->acct_private_keys[ i ].uc, i ); + fd_ed25519_public_from_private( ctx->acct_public_keys[ i ].uc, ctx->acct_private_keys[ i ].uc , ctx->sha ); } ctx->has_recent_blockhash = 0; diff --git a/src/disco/topo/fd_topo.h b/src/disco/topo/fd_topo.h index 1460dedff1..3fbf487f45 100644 --- a/src/disco/topo/fd_topo.h +++ b/src/disco/topo/fd_topo.h @@ -200,6 +200,10 @@ typedef struct { ushort rpc_port; uint rpc_ip_addr; } bencho; + + struct { + ulong accounts_cnt; + } benchg; }; } fd_topo_tile_t;