diff --git a/src/choreo/fd_choreo.h b/src/choreo/fd_choreo.h index a930dca8c4..bfeb51cf0d 100644 --- a/src/choreo/fd_choreo.h +++ b/src/choreo/fd_choreo.h @@ -6,5 +6,6 @@ #include "commitment/fd_commitment.h" /* Includes fd_choreo_base.h */ #include "forks/fd_forks.h" /* Includes fd_choreo_base.h */ #include "ghost/fd_ghost.h" /* Includes fd_choreo_base.h */ +#include "vote/fd_vote.h" /* Includes fd_choreo_base.h */ #endif /* HEADER_fd_src_choreo_fd_choreo_h */ diff --git a/src/choreo/vote/Local.mk b/src/choreo/vote/Local.mk new file mode 100644 index 0000000000..7d1d0f35e5 --- /dev/null +++ b/src/choreo/vote/Local.mk @@ -0,0 +1,5 @@ +ifdef FD_HAS_INT128 +$(call add-hdrs,fd_vote.h) +$(call add-objs,fd_vote,fd_choreo) +$(call make-unit-test,test_choreo_vote,test_choreo_vote,fd_disco fd_choreo fd_flamenco fd_funk fd_tango fd_util fd_ballet fd_reedsol fd_waltz,$(SECP256K1_LIBS)) +endif diff --git a/src/choreo/vote/fd_vote.c b/src/choreo/vote/fd_vote.c new file mode 100644 index 0000000000..84a2c5936f --- /dev/null +++ b/src/choreo/vote/fd_vote.c @@ -0,0 +1,117 @@ +#include "fd_vote.h" + +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + +ulong +fd_vote_txn_generate(fd_compact_vote_state_update_t* compact_vote_update, + fd_pubkey_t* validator_pubkey, + fd_pubkey_t* vote_acct_pubkey, + uchar* validator_privkey, + uchar* vote_acct_privkey, + uchar* recent_blockhash, + uchar out_txn_meta_buf [static FD_TXN_MAX_SZ], + uchar out_txn_buf [static FD_TXN_MTU] + ) { + /* Create the transaction base */ + fd_pubkey_t pubkeys[2], vote_program_pubkey; + memcpy( pubkeys, validator_pubkey, sizeof(fd_pubkey_t) ); + memcpy( pubkeys + 1, vote_acct_pubkey, sizeof(fd_pubkey_t) ); + memcpy( &vote_program_pubkey, &fd_solana_vote_program_id, sizeof(fd_pubkey_t) ); + + fd_txn_accounts_t vote_txn_accounts; + vote_txn_accounts.signature_cnt = 2; + vote_txn_accounts.readonly_signed_cnt = 0; + vote_txn_accounts.readonly_unsigned_cnt = 1; + vote_txn_accounts.acct_cnt = 3; /* 3 accounts: validator, vote account, vote program */ + vote_txn_accounts.signers_w = pubkeys; /* 2 signers: validator, vote account */ + vote_txn_accounts.signers_r = NULL; + vote_txn_accounts.non_signers_w = NULL; + vote_txn_accounts.non_signers_r = &vote_program_pubkey; /* 1 non-signer: vote program */ + FD_TEST( fd_txn_base_generate( out_txn_meta_buf, out_txn_buf, 2, &vote_txn_accounts, recent_blockhash ) ); + + /* Add the vote instruction to the transaction */ + fd_vote_instruction_t vote_instr; + uchar vote_instr_buf[FD_TXN_MTU]; + vote_instr.discriminant = fd_vote_instruction_enum_compact_update_vote_state; + vote_instr.inner.compact_update_vote_state = *compact_vote_update; + fd_bincode_encode_ctx_t encode = { .data = vote_instr_buf, .dataend = (vote_instr_buf + FD_TXN_MTU) }; + fd_vote_instruction_encode ( &vote_instr, &encode ); + ushort vote_instr_size = (ushort)fd_vote_instruction_size( &vote_instr ); + + uchar instr_accounts[2]; + instr_accounts[0] = 1; /* vote account */ + instr_accounts[1] = 1; /* vote authority */ + uchar program_id = 2; /* vote program */ + ulong txn_size = fd_txn_add_instr(out_txn_meta_buf, + out_txn_buf, + program_id, + instr_accounts, + 2, /* 2 accounts in instr_accounts */ + vote_instr_buf, + vote_instr_size); + + /* Add the signatures of validator and vote account */ + fd_sha512_t sha; + fd_txn_t * txn_meta = (fd_txn_t *) fd_type_pun( out_txn_meta_buf ); + fd_ed25519_sign( /* sig */ out_txn_buf + txn_meta->signature_off, + /* msg */ out_txn_buf + txn_meta->message_off, + /* sz */ txn_size - txn_meta->message_off, + /* public_key */ validator_pubkey->key, + /* private_key */ validator_privkey, + &sha); + fd_ed25519_sign( /* sig */ out_txn_buf + txn_meta->signature_off + FD_TXN_SIGNATURE_SZ, + /* msg */ out_txn_buf + txn_meta->message_off, + /* sz */ txn_size - txn_meta->message_off, + /* public_key */ vote_acct_pubkey->key, + /* private_key */ vote_acct_privkey, + &sha); + + return txn_size; +} + +int +fd_vote_txn_parse(uchar txn_buf [static FD_TXN_MTU], + ulong txn_size, + fd_valloc_t valloc, + fd_compact_vote_state_update_t *out_compact_vote_update){ + uchar out_buf[ FD_TXN_MAX_SZ ]; + fd_txn_t * parsed_txn = (fd_txn_t *)fd_type_pun( out_buf ); + ulong out_sz = fd_txn_parse( txn_buf, txn_size, out_buf, NULL ); + FD_TEST( out_sz ); + FD_TEST( parsed_txn ); + FD_TEST( parsed_txn->instr_cnt == 1); + + uchar program_id = parsed_txn->instr[0].program_id; + uchar* program_account_addr = (txn_buf + parsed_txn->acct_addr_off + + FD_TXN_ACCT_ADDR_SZ * program_id ); + + if ( memcmp( program_account_addr, fd_solana_vote_program_id.key, sizeof( fd_pubkey_t ) ) ) { + FD_LOG_WARNING( ("fd_vote_txn_parse: txn targets program %32J instead of %32J", + program_account_addr, + fd_solana_vote_program_id.key) ); + return FD_VOTE_TXN_PARSE_ERR_WRONG_PROG; + } else { + fd_vote_instruction_t vote_instr = { 0 }; + ushort instr_data_sz = parsed_txn->instr[0].data_sz; + uchar* instr_data = txn_buf + parsed_txn->instr[0].data_off; + fd_bincode_decode_ctx_t decode = { + .data = instr_data, + .dataend = instr_data + instr_data_sz, + .valloc = valloc + }; + int decode_result = fd_vote_instruction_decode( &vote_instr, &decode ); + if( decode_result != FD_BINCODE_SUCCESS) { + FD_LOG_WARNING(("fd_vote_txn_parse: fail at decoding vote instruction")); + return FD_VOTE_TXN_PARSE_ERR_BAD_INST; + } else { + if (vote_instr.discriminant != fd_vote_instruction_enum_compact_update_vote_state) { + FD_LOG_WARNING(("fd_vote_txn_parse: not compact_update_vote_state instruction")); + return FD_VOTE_TXN_PARSE_ERR_BAD_INST; + } else { + *out_compact_vote_update = vote_instr.inner.compact_update_vote_state; + } + } + } + return FD_VOTE_TXN_PARSE_OK; +} diff --git a/src/choreo/vote/fd_vote.h b/src/choreo/vote/fd_vote.h new file mode 100644 index 0000000000..3596385e55 --- /dev/null +++ b/src/choreo/vote/fd_vote.h @@ -0,0 +1,28 @@ +#ifndef HEADER_fd_src_choreo_vote_fd_vote_h +#define HEADER_fd_src_choreo_vote_fd_vote_h + +#include "../fd_choreo_base.h" +#include "../../flamenco/txn/fd_txn_generate.h" +#include "../../flamenco/runtime/fd_system_ids.h" + +#define FD_VOTE_TXN_PARSE_OK 0 +#define FD_VOTE_TXN_PARSE_ERR_BAD_INST -1 +#define FD_VOTE_TXN_PARSE_ERR_WRONG_PROG -2 + +ulong +fd_vote_txn_generate(fd_compact_vote_state_update_t *vote_update, + fd_pubkey_t *valicator_pubkey, + fd_pubkey_t *vote_acct_pubkey, + uchar* valicator_privkey, + uchar* vote_acct_privkey, + uchar* recent_blockhash, + uchar out_txn_meta_buf [static FD_TXN_MAX_SZ], + uchar out_txn_buf [static FD_TXN_MTU]); + +int +fd_vote_txn_parse(uchar txn_buf [static FD_TXN_MTU], + ulong txn_size, + fd_valloc_t valloc, + fd_compact_vote_state_update_t *out_vote_update); + +#endif diff --git a/src/choreo/vote/test_choreo_vote.c b/src/choreo/vote/test_choreo_vote.c new file mode 100644 index 0000000000..d234609a55 --- /dev/null +++ b/src/choreo/vote/test_choreo_vote.c @@ -0,0 +1,66 @@ +#include +#include "../../util/fd_util.h" +#include "../../choreo/fd_choreo.h" +#include "../../ballet/ed25519/fd_ed25519.h" +#include "../../flamenco/fd_flamenco.h" +#include "../../flamenco/txn/fd_txn_generate.h" +#include "../../flamenco/runtime/fd_system_ids.h" + +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + +#define TEST_VOTE_TXN_MAGIC (0x7e58UL) + +int +main( int argc, char ** argv ) { + fd_boot( &argc, &argv ); + fd_flamenco_boot( &argc, &argv ); + + /* keys */ + fd_sha512_t sha[2]; + fd_pubkey_t vote_acct_pubkey, validator_pubkey; + uchar vote_acct_privkey[32], validator_privkey[32]; + + FD_TEST( 32UL == getrandom( vote_acct_privkey, 32UL, 0 ) ); + FD_TEST( 32UL == getrandom( validator_privkey, 32UL, 0 ) ); + FD_TEST( fd_ed25519_public_from_private( vote_acct_pubkey.key, vote_acct_privkey, &sha[0] ) ); + FD_TEST( fd_ed25519_public_from_private( validator_pubkey.key, validator_privkey, &sha[1] ) ); + + /* workspace */ + ulong page_cnt = 1; + char * _page_sz = "gigantic"; + ulong numa_idx = fd_shmem_numa_idx( 0 ); + fd_wksp_t * wksp = fd_wksp_new_anonymous( + fd_cstr_to_shmem_page_sz( _page_sz ), page_cnt, fd_shmem_cpu_idx( numa_idx ), "wksp", 0UL ); + FD_TEST( wksp ); + + /* alloc */ + void * alloc_shmem = + fd_wksp_alloc_laddr( wksp, fd_alloc_align(), fd_alloc_footprint(), TEST_VOTE_TXN_MAGIC ); + void * alloc_shalloc = fd_alloc_new( alloc_shmem, TEST_VOTE_TXN_MAGIC ); + fd_alloc_t * alloc = fd_alloc_join( alloc_shalloc, 0UL ); + fd_valloc_t valloc = fd_alloc_virtual( alloc ); + + /* create compact_vote_state_update with dummy values */ + fd_compact_vote_state_update_t compact_vote_update; + memset( &compact_vote_update, 0, sizeof(fd_compact_vote_state_update_t) ); + compact_vote_update.root = 100; + compact_vote_update.lockouts_len = 0; + static ulong now = 1715701506716580798UL; + compact_vote_update.timestamp = &now; + FD_TEST( 32UL == getrandom( compact_vote_update.hash.key, 32UL, 0 ) ); + + /* create the vote transaction */ + uchar txn_meta_buf[ FD_TXN_MAX_SZ ]; + uchar txn_buf [ FD_TXN_MTU ]; + uchar *recent_blockhash = NULL; + ulong txn_size = fd_vote_txn_generate( &compact_vote_update, &validator_pubkey, &vote_acct_pubkey, validator_privkey, vote_acct_privkey, recent_blockhash, txn_meta_buf, txn_buf); + FD_LOG_NOTICE(("fd_vote_txn_generate: vote txn has %lu bytes", txn_size)); + + /* parse the transaction back */ + fd_compact_vote_state_update_t parsed_vote_update; + FD_TEST( FD_VOTE_TXN_PARSE_OK == fd_vote_txn_parse(txn_buf, txn_size, valloc, &parsed_vote_update) ); + FD_LOG_NOTICE((".root: %ld == %ld", compact_vote_update.root, parsed_vote_update.root)); + FD_LOG_NOTICE((".timestamp: %lu == %lu", *compact_vote_update.timestamp, *parsed_vote_update.timestamp)); + FD_LOG_NOTICE((".hash: %32J == %32J", compact_vote_update.hash.key, parsed_vote_update.hash.key)); +} diff --git a/src/disco/consensus/Local.mk b/src/disco/consensus/Local.mk index 94c6f587de..f9aa02641a 100644 --- a/src/disco/consensus/Local.mk +++ b/src/disco/consensus/Local.mk @@ -1,3 +1,4 @@ ifdef FD_HAS_INT128 $(call make-unit-test,test_consensus,test_consensus,fd_disco fd_choreo fd_flamenco fd_funk fd_tango fd_util fd_ballet fd_reedsol fd_waltz,$(SECP256K1_LIBS)) +$(call make-unit-test,test_gossip_echo_vote,test_gossip_echo_vote,fd_disco fd_choreo fd_flamenco fd_funk fd_tango fd_util fd_ballet fd_reedsol fd_waltz,$(SECP256K1_LIBS)) endif diff --git a/src/disco/consensus/test_gossip_echo_vote.c b/src/disco/consensus/test_gossip_echo_vote.c new file mode 100644 index 0000000000..f618dfbeb5 --- /dev/null +++ b/src/disco/consensus/test_gossip_echo_vote.c @@ -0,0 +1,467 @@ +#define _GNU_SOURCE /* See feature_test_macros(7) */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../keyguard/fd_keyload.h" +#include "../keyguard/fd_keyguard_client.h" +#include "../metrics/fd_metrics.h" +#include "../shred/fd_shred_cap.h" +#include "../tvu/fd_replay.h" +#include "../tvu/fd_store.h" + +#include "../../choreo/fd_choreo.h" +#include "../../flamenco/fd_flamenco.h" +#include "../../flamenco/repair/fd_repair.h" +#include "../../flamenco/runtime/fd_hashes.h" +#include "../../flamenco/runtime/fd_system_ids.h" +#include "../../flamenco/runtime/fd_snapshot_loader.h" +#include "../../flamenco/runtime/program/fd_bpf_program_util.h" +#include "../../flamenco/runtime/program/fd_vote_program.h" +#include "../../flamenco/runtime/sysvar/fd_sysvar_epoch_schedule.h" +#include "../../flamenco/types/fd_types.h" +#include "../../util/fd_util.h" +#include "../../util/net/fd_eth.h" + +#pragma GCC diagnostic ignored "-Wformat" +#pragma GCC diagnostic ignored "-Wformat-extra-args" + +#define MAX_ADDR_STRLEN 128 +#define TEST_GOSSIP_VOTE_MAGIC ( 0x7e57UL ) /* test */ + +uchar metrics_scratch[ FD_METRICS_FOOTPRINT( 0, 0 ) ] __attribute__((aligned(FD_METRICS_ALIGN))); + +static void +sign_fun( void * arg, uchar * sig, uchar const * buffer, ulong len ) { + fd_gossip_config_t * config = (fd_gossip_config_t *)arg; + fd_sha512_t sha[1]; + if (len == 48UL && (memcmp( buffer, "SOLANA_PING_PONG", 16UL ) == 0)) { + /* Gossip currently requires special handling of signing the ping/pong messages */ + uchar hash[32]; + fd_sha256_hash( buffer, len, hash ); + fd_ed25519_sign( /* sig */ sig, + /* msg */ hash, + /* sz */ 32, + /* public_key */ config->public_key->uc, + /* private_key */ config->private_key, + sha ); + } else { + fd_ed25519_sign( /* sig */ sig, + /* msg */ buffer, + /* sz */ len, + /* public_key */ config->public_key->uc, + /* private_key */ config->private_key, + sha ); + } +} + +static int +to_sockaddr( uchar * dst, fd_gossip_peer_addr_t const * src ) { + fd_memset( dst, 0, sizeof( struct sockaddr_in ) ); + struct sockaddr_in * t = (struct sockaddr_in *)dst; + t->sin_family = AF_INET; + t->sin_addr.s_addr = src->addr; + t->sin_port = src->port; + return sizeof( struct sockaddr_in ); +} + +/* Convert my style of address from UNIX style */ +static int +from_sockaddr( fd_gossip_peer_addr_t * dst, uchar const * src ) { + FD_STATIC_ASSERT( sizeof( fd_gossip_peer_addr_t ) == sizeof( ulong ), "messed up size" ); + dst->l = 0; + const struct sockaddr_in * sa = (const struct sockaddr_in *)src; + dst->addr = sa->sin_addr.s_addr; + dst->port = sa->sin_port; + return 0; +} + +static int +create_socket( fd_gossip_peer_addr_t * addr ) { + int fd; + if( ( fd = socket( AF_INET, SOCK_DGRAM, 0 ) ) < 0 ) { + FD_LOG_ERR( ( "socket failed: %s", strerror( errno ) ) ); + return -1; + } + int optval = 1 << 20; + if( setsockopt( fd, SOL_SOCKET, SO_RCVBUF, (char *)&optval, sizeof( int ) ) < 0 ) { + FD_LOG_ERR( ( "setsocketopt failed: %s", strerror( errno ) ) ); + return -1; + } + + if( setsockopt( fd, SOL_SOCKET, SO_SNDBUF, (char *)&optval, sizeof( int ) ) < 0 ) { + FD_LOG_ERR( ( "setsocketopt failed: %s", strerror( errno ) ) ); + return -1; + } + + uchar saddr[sizeof( struct sockaddr_in6 )]; + int addrlen = to_sockaddr( saddr, addr ); + if( addrlen < 0 || bind( fd, (struct sockaddr *)saddr, (uint)addrlen ) < 0 ) { + char tmp[MAX_ADDR_STRLEN]; + FD_LOG_ERR( ( "bind failed: %s for %s", + strerror( errno ), + fd_gossip_addr_str( tmp, sizeof( tmp ), addr ) ) ); + return -1; + } + if( getsockname( fd, (struct sockaddr *)saddr, (uint *)&addrlen ) < 0 ) { + FD_LOG_ERR( ( "getsockname failed: %s", strerror( errno ) ) ); + return -1; + } + from_sockaddr( addr, saddr ); + + return fd; +} + +static void +gossip_send_fun( uchar const * data, + size_t sz, + fd_gossip_peer_addr_t const * gossip_peer_addr, + void * arg ) { + uchar saddr[sizeof( struct sockaddr_in )]; + int saddrlen = to_sockaddr( saddr, gossip_peer_addr ); + char s[MAX_ADDR_STRLEN] = { 0 }; + fd_gossip_addr_str( s, sizeof( s ), gossip_peer_addr ); + if( sendto( *(int *)arg, + data, + sz, + MSG_DONTWAIT, + (const struct sockaddr *)saddr, + (socklen_t)saddrlen ) < 0 ) { + FD_LOG_WARNING( ( "sendto failed: %s", strerror( errno ) ) ); + } +} + +struct gossip_deliver_arg { + fd_valloc_t valloc; + fd_gossip_t *gossip; + fd_gossip_config_t *gossip_config; + + const uchar* voter_keypair; + const uchar* validator_keypair; +}; +typedef struct gossip_deliver_arg gossip_deliver_arg_t; + +static void +gossip_deliver_fun( fd_crds_data_t * data, void * arg ) { + gossip_deliver_arg_t * arg_ = (gossip_deliver_arg_t *)arg; + if ( data->discriminant == fd_crds_data_enum_vote ) { + fd_gossip_vote_t *vote = &data->inner.vote; + fd_txn_t *parsed_txn = (fd_txn_t *)fd_type_pun( vote->txn.txn ); + + FD_TEST( parsed_txn ); + FD_TEST( parsed_txn->instr_cnt == 1); + + uchar program_id = parsed_txn->instr[0].program_id; + uchar* account_addr = (vote->txn.raw + parsed_txn->acct_addr_off + + FD_TXN_ACCT_ADDR_SZ * program_id ); + + if ( !memcmp( account_addr, fd_solana_vote_program_id.key, sizeof( fd_pubkey_t ) ) ) { + fd_vote_instruction_t vote_instr = { 0 }; + ushort instr_data_sz = parsed_txn->instr[0].data_sz; + uchar* instr_data = vote->txn.raw + parsed_txn->instr[0].data_off; + fd_bincode_decode_ctx_t decode = { + .data = instr_data, + .dataend = instr_data + instr_data_sz, + .valloc = arg_->valloc + }; + int decode_result = fd_vote_instruction_decode( &vote_instr, &decode ); + if( decode_result == FD_BINCODE_SUCCESS) { + if ( vote_instr.discriminant == fd_vote_instruction_enum_compact_update_vote_state ) { + /* Replace the timestamp in compact_update_vote_state */ + static ulong MAGIC_TIMESTAMP = TEST_GOSSIP_VOTE_MAGIC; + vote_instr.inner.compact_update_vote_state.timestamp = &MAGIC_TIMESTAMP; + + /* Prepare the keys for the vote transaction */ + fd_pubkey_t validator_pubkey, vote_acct_pubkey; + uchar validator_privkey[32], vote_acct_privkey[32]; + memcpy( validator_privkey, arg_->validator_keypair, 32 ); + memcpy( vote_acct_privkey, arg_->voter_keypair, 32 ); + memcpy( validator_pubkey.key, arg_->validator_keypair + 32UL, 32 ); + memcpy( vote_acct_pubkey.key, arg_->voter_keypair + 32UL, 32 ); + + /* Generate and send the vote transaction */ + fd_crds_data_t echo_data; + echo_data.discriminant = fd_crds_data_enum_vote; + echo_data.inner.vote.txn.raw_sz = fd_vote_txn_generate( &vote_instr.inner.compact_update_vote_state, + &validator_pubkey, + &vote_acct_pubkey, + validator_privkey, + vote_acct_privkey, + vote->txn.raw + parsed_txn->recent_blockhash_off, + echo_data.inner.vote.txn.txn_buf, + echo_data.inner.vote.txn.raw); + fd_gossip_push_value( arg_->gossip, &echo_data, NULL ); + + static ulong echo_cnt = 0; + FD_LOG_NOTICE( ("Echo gossip vote#%lu: root=%lu, from=%32J, gossip_pubkey=%32J, txn_acct_cnt=%u(readonly_s=%u, readonly_us=%u), sign_cnt=%u, sign_off=%u | instruction#0: program=%32J", + echo_cnt++, + vote_instr.inner.compact_update_vote_state.root, + &vote->from, + arg_->gossip_config->public_key, + parsed_txn->acct_addr_cnt, + parsed_txn->readonly_signed_cnt, + parsed_txn->readonly_unsigned_cnt, + parsed_txn->signature_cnt, + parsed_txn->signature_off, + account_addr) ); + + } else { + FD_LOG_WARNING( ("Gossip receives vote instruction with other discriminant") ); + } + } else { + FD_LOG_ERR( ("Unable to decode the vote instruction in gossip, error=%d", decode_result) ); + } + } else { + FD_LOG_ERR( ("Received gossip vote txn targets program %32J instead of %32J", + account_addr, + fd_solana_vote_program_id.key) ); + } + } +} + +/* Convert a host:port string to a repair network address. If host is + * missing, it assumes the local hostname. */ +static fd_repair_peer_addr_t * +resolve_hostport( const char * str /* host:port */, fd_repair_peer_addr_t * res ) { + fd_memset( res, 0, sizeof( fd_repair_peer_addr_t ) ); + + /* Find the : and copy out the host */ + char buf[MAX_ADDR_STRLEN]; + uint i; + for( i = 0;; ++i ) { + if( str[i] == '\0' || i > sizeof( buf ) - 1U ) { + FD_LOG_ERR( ( "missing colon" ) ); + return NULL; + } + if( str[i] == ':' ) { + buf[i] = '\0'; + break; + } + buf[i] = str[i]; + } + if( i == 0 || strcmp( buf, "localhost" ) == 0 || + strcmp( buf, "127.0.0.1" ) == 0 ) /* :port means $HOST:port */ + gethostname( buf, sizeof( buf ) ); + + struct hostent * host = gethostbyname( buf ); + if( host == NULL ) { + FD_LOG_WARNING( ( "unable to resolve host %s", buf ) ); + return NULL; + } + /* Convert result to repair address */ + res->l = 0; + res->addr = ( (struct in_addr *)host->h_addr_list[0] )->s_addr; + int port = atoi( str + i + 1 ); + if( ( port > 0 && port < 1024 ) || port > (int)USHORT_MAX ) { + FD_LOG_ERR( ( "invalid port number" ) ); + return NULL; + } + res->port = htons( (ushort)port ); + + return res; +} + +struct gossip_targ { + int gossip_fd; + fd_valloc_t valloc; + fd_gossip_t * gossip; +}; +typedef struct gossip_targ gossip_targ_t; + +#define VLEN 32U +#define CLEAR_MSGS \ + fd_memset( msgs, 0, sizeof( msgs ) ); \ + for( uint i = 0; i < VLEN; i++ ) { \ + iovecs[i].iov_base = bufs[i]; \ + iovecs[i].iov_len = FD_ETH_PAYLOAD_MAX; \ + msgs[i].msg_hdr.msg_iov = &iovecs[i]; \ + msgs[i].msg_hdr.msg_iovlen = 1; \ + msgs[i].msg_hdr.msg_name = sockaddrs[i]; \ + msgs[i].msg_hdr.msg_namelen = sizeof( struct sockaddr_in6 ); \ + } + +void* +gossip_thread( void* arg ) { + gossip_targ_t * _arg = (gossip_targ_t *)arg; + int gossip_fd = _arg->gossip_fd; + fd_gossip_t * gossip = _arg->gossip; + + ulong smax = 1UL << 21UL; + ulong sdepth = 1UL << 5UL; + void * smem = + fd_valloc_malloc( _arg->valloc, fd_scratch_smem_align(), fd_scratch_smem_footprint( smax ) ); + void * fmem = + fd_valloc_malloc( _arg->valloc, fd_scratch_fmem_align(), fd_scratch_fmem_footprint( sdepth ) ); + FD_TEST( ( !!smem ) & ( !!fmem ) ); + fd_scratch_attach( smem, fmem, smax, sdepth ); + + struct mmsghdr msgs[VLEN]; + struct iovec iovecs[VLEN]; + uchar bufs[VLEN][FD_ETH_PAYLOAD_MAX]; + uchar sockaddrs[VLEN][sizeof( struct sockaddr_in6 )]; /* sockaddr is smaller than sockaddr_in6 */ + while( FD_LIKELY( 1 /* !fd_tile_shutdown_flag */ ) ) { + long now = fd_log_wallclock(); + fd_gossip_settime( gossip, now ); + + /* Loop gossip */ + fd_gossip_continue( gossip ); + + /* Read more packets */ + CLEAR_MSGS; + int gossip_rc = recvmmsg( gossip_fd, msgs, VLEN, MSG_DONTWAIT, NULL ); + if( gossip_rc < 0 ) { + if( errno == EINTR || errno == EWOULDBLOCK ) continue; + break; + } + + for( uint i = 0; i < (uint)gossip_rc; ++i ) { + fd_gossip_peer_addr_t from; + from_sockaddr( &from, msgs[i].msg_hdr.msg_name ); + fd_gossip_recv_packet( gossip, bufs[i], msgs[i].msg_len, &from ); + } + } + return 0; +} + +int +main( int argc, char ** argv ) { + fd_boot( &argc, &argv ); + fd_flamenco_boot( &argc, &argv ); + fd_metrics_register( (ulong *)fd_metrics_new( metrics_scratch, 0UL, 0UL ) ); + + const char * gossip_peer_addr = + fd_env_strip_cmdline_cstr( &argc, &argv, "--gossip-peer-addr", NULL, NULL ); + ushort gossip_port = fd_env_strip_cmdline_ushort( &argc, &argv, "--gossip-port", NULL, 9001 ); + FD_TEST( gossip_peer_addr ); + const char * voter_keypair_file = + fd_env_strip_cmdline_cstr( &argc, &argv, "--voter-keypair-file", NULL, NULL ); + const char * validator_keypair_file = + fd_env_strip_cmdline_cstr( &argc, &argv, "--validator-keypair-file", NULL, NULL ); + ushort shred_version = fd_env_strip_cmdline_ushort( &argc, &argv, "--shred-version", NULL, 0 ); + FD_TEST( shred_version != 0 ); + FD_TEST( voter_keypair_file ); + FD_TEST( validator_keypair_file ); + + /**********************************************************************/ + /* wksp */ + /**********************************************************************/ + + char * _page_sz = "gigantic"; + ulong numa_idx = fd_shmem_numa_idx( 0 ); + FD_LOG_NOTICE( ( "Creating workspace (--page-cnt %lu, --page-sz %s, --numa-idx %lu)", + 1, + _page_sz, + numa_idx ) ); + fd_wksp_t * wksp = fd_wksp_new_anonymous( + fd_cstr_to_shmem_page_sz( _page_sz ), 1, fd_shmem_cpu_idx( numa_idx ), "wksp", 0UL ); + FD_TEST( wksp ); + FD_LOG_DEBUG( ( "Finish setup wksp" ) ); + + /**********************************************************************/ + /* alloc */ + /**********************************************************************/ + + void * alloc_shmem = + fd_wksp_alloc_laddr( wksp, fd_alloc_align(), fd_alloc_footprint(), TEST_GOSSIP_VOTE_MAGIC ); + void * alloc_shalloc = fd_alloc_new( alloc_shmem, TEST_GOSSIP_VOTE_MAGIC ); + fd_alloc_t * alloc = fd_alloc_join( alloc_shalloc, 0UL ); + fd_valloc_t valloc = fd_alloc_virtual( alloc ); + + /**********************************************************************/ + /* scratch */ + /**********************************************************************/ + + ulong smax = 1UL << 21UL; + ulong sdepth = 1UL << 5UL; + void * smem = + fd_valloc_malloc( valloc, fd_scratch_smem_align(), fd_scratch_smem_footprint( smax ) ); + void * fmem = + fd_valloc_malloc( valloc, fd_scratch_fmem_align(), fd_scratch_fmem_footprint( sdepth ) ); + FD_TEST( ( !!smem ) & ( !!fmem ) ); + fd_scratch_attach( smem, fmem, smax, sdepth ); + + /**********************************************************************/ + /* keys */ + /**********************************************************************/ + + uchar private_key[32]; + FD_TEST( 32UL == getrandom( private_key, 32UL, 0 ) ); + fd_sha512_t sha[1]; + fd_pubkey_t public_key; + FD_TEST( fd_ed25519_public_from_private( public_key.uc, private_key, sha ) ); + + /**********************************************************************/ + /* gossip */ + /**********************************************************************/ + + void * gossip_shmem = + fd_wksp_alloc_laddr( wksp, fd_gossip_align(), fd_gossip_footprint(), TEST_GOSSIP_VOTE_MAGIC ); + fd_gossip_t * gossip = + fd_gossip_join( fd_gossip_new( gossip_shmem, TEST_GOSSIP_VOTE_MAGIC ) ); + + fd_gossip_config_t gossip_config; + char gossip_addr[7] = { 0 }; + snprintf( gossip_addr, sizeof( gossip_addr ), ":%u", gossip_port ); + FD_TEST( resolve_hostport( gossip_addr, &gossip_config.my_addr ) ); + gossip_config.shred_version = shred_version; + gossip_deliver_arg_t gossip_deliver_arg = { .valloc = valloc, + .gossip_config = &gossip_config, + .gossip = gossip, + .voter_keypair = fd_keyload_load(voter_keypair_file, 0), + .validator_keypair = fd_keyload_load(validator_keypair_file, 0)}; + gossip_config.deliver_arg = &gossip_deliver_arg; + gossip_config.deliver_fun = gossip_deliver_fun; + gossip_config.send_fun = gossip_send_fun; + int gossip_sockfd = create_socket( &gossip_config.my_addr ); + gossip_config.send_arg = &gossip_sockfd; + gossip_config.public_key = &public_key; + gossip_config.private_key = private_key; + gossip_config.sign_fun = sign_fun; + gossip_config.sign_arg = &gossip_config; + gossip_config.my_version = (fd_gossip_version_v2_t){ + .from = public_key, + .major = 1337U, + .minor = 1337U, + .patch = 1337U, + .commit = 0U, + .has_commit = 0U, + .feature_set = 0U, + }; + FD_TEST( !fd_gossip_set_config( gossip, &gossip_config ) ); + + uint entrypoints[16]; + fd_gossip_peer_addr_t _gossip_peer_addr; + resolve_hostport( gossip_peer_addr, &_gossip_peer_addr ); + entrypoints[0] = _gossip_peer_addr.addr; + ushort port = fd_ushort_bswap(_gossip_peer_addr.port); + fd_gossip_set_entrypoints( gossip, entrypoints, 1, &port); + + fd_gossip_update_addr( gossip, &gossip_config.my_addr ); + fd_gossip_settime( gossip, fd_log_wallclock() ); + fd_gossip_start( gossip ); + + /**********************************************************************/ + /* gossip thread */ + /**********************************************************************/ + + gossip_targ_t gossip_targ = { .gossip_fd = gossip_sockfd, .valloc = valloc, .gossip = gossip }; + pthread_t t; + pthread_create(&t, NULL, gossip_thread, &gossip_targ); + while( FD_LIKELY( 1 /* !fd_tile_shutdown_flag */ ) ) { + /* Allow other threads to add pendings */ + struct timespec ts = { .tv_sec = 0, .tv_nsec = (long)1e6 }; + nanosleep( &ts, NULL ); + } + + fd_halt(); + return 0; +} diff --git a/src/flamenco/txn/fd_txn_generate.c b/src/flamenco/txn/fd_txn_generate.c index fc357f79b5..3f8ce8ac4d 100644 --- a/src/flamenco/txn/fd_txn_generate.c +++ b/src/flamenco/txn/fd_txn_generate.c @@ -142,7 +142,7 @@ fd_txn_add_instr( uchar * txn_meta_ptr, program_id, (ushort)accounts_sz, data_sz, acct_off, data_off ); - return (ulong)(instr_start - out_txn_payload); + return (ulong)(write_ptr - out_txn_payload); } void diff --git a/src/flamenco/txn/fd_txn_generate.h b/src/flamenco/txn/fd_txn_generate.h index fef679b4f2..b9790681a7 100644 --- a/src/flamenco/txn/fd_txn_generate.h +++ b/src/flamenco/txn/fd_txn_generate.h @@ -1,3 +1,6 @@ +#ifndef HEADER_fd_src_flamenco_txn_fd_txn_generate_h +#define HEADER_fd_src_flamenco_txn_fd_txn_generate_h + /* Provides utility methods to create txn templates for pre-staging, as well as a mechanism to build out an entire transaction with instructions. */ @@ -56,3 +59,5 @@ void fd_txn_reset_instrs( uchar * txn_meta_ptr, uchar out_txn_payload[ static FD_TXN_MTU ] ); FD_PROTOTYPES_END + +#endif