diff --git a/src/discof/gossip/fd_gossip_tile.c b/src/discof/gossip/fd_gossip_tile.c index 9b2cec9c1b..313b09a750 100644 --- a/src/discof/gossip/fd_gossip_tile.c +++ b/src/discof/gossip/fd_gossip_tile.c @@ -46,6 +46,7 @@ struct fd_gossip_tile_ctx { fd_gossip_out_ctx_t net_out[ 1 ]; fd_gossip_out_ctx_t gossip_out[ 1 ]; + fd_gossip_out_ctx_t sign_out[ 1 ]; fd_keyguard_client_t keyguard_client[ 1 ]; fd_keyswitch_t * keyswitch; @@ -157,7 +158,8 @@ privileged_init( fd_topo_t * topo, static inline fd_gossip_out_ctx_t out1( fd_topo_t const * topo, fd_topo_tile_t const * tile, - char const * name ) { + char const * name, + ulong * opt_tile_out_idx ) { ulong idx = ULONG_MAX; for( ulong i=0UL; iout_cnt; i++ ) { @@ -169,6 +171,7 @@ out1( fd_topo_t const * topo, } if( FD_UNLIKELY( idx==ULONG_MAX ) ) FD_LOG_ERR(( "tile %s:%lu had no output link named %s", tile->name, tile->kind_id, name )); + if( opt_tile_out_idx ) *opt_tile_out_idx = idx; void * mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ idx ] ].dcache_obj_id ].wksp_id ].wksp; ulong chunk0 = fd_dcache_compact_chunk0( mem, topo->links[ tile->out_link_id[ idx ] ].dcache ); @@ -210,7 +213,8 @@ unprivileged_init( fd_topo_t * topo, ctx->ticks_per_ns = fd_tempo_tick_per_ns( NULL ); ctx->last_wallclock = fd_log_wallclock(); ctx->last_tickcount = fd_tickcount(); - + + ulong sign_in_tile_idx = ULONG_MAX; for( ulong i=0UL; iin_cnt; i++ ) { fd_topo_link_t * link = &topo->links[ tile->in_link_id[ i ] ]; fd_topo_wksp_t * link_wksp = &topo->workspaces[ topo->objs[ link->dcache_obj_id ].wksp_id ]; @@ -223,13 +227,29 @@ unprivileged_init( fd_topo_t * topo, ctx->in_kind[ i ] = IN_KIND_SHRED_VERSION; } else if( FD_UNLIKELY( !strcmp( link->name, "net_gossip" ) ) ) { ctx->in_kind[ i ] = IN_KIND_NET; + } else if( FD_UNLIKELY( !strcmp( link->name, "sign_gossip" ) ) ) { + ctx->in_kind[ i ] = IN_KIND_SIGN; + sign_in_tile_idx = i; } else { FD_LOG_ERR(( "unexpected input link name %s", link->name )); } } - *ctx->net_out = out1( topo, tile, "gossip_net" ); - *ctx->gossip_out = out1( topo, tile, "gossip_out" ); + *ctx->net_out = out1( topo, tile, "gossip_net", NULL ); + *ctx->gossip_out = out1( topo, tile, "gossip_out", NULL ); + + ulong sign_out_tile_idx = ULONG_MAX; + *ctx->sign_out = out1( topo, tile, "gossip_sign", &sign_out_tile_idx ); + + fd_topo_link_t * sign_in = &topo->links[ tile->in_link_id [ sign_in_tile_idx ] ]; + fd_topo_link_t * sign_out = &topo->links[ tile->out_link_id[ sign_out_tile_idx ] ]; + if( fd_keyguard_client_join( fd_keyguard_client_new( ctx->keyguard_client, + sign_out->mcache, + sign_out->dcache, + sign_in->mcache, + sign_in->dcache ) ) ){ + FD_LOG_ERR(( "failed to join keyguard client" )); +} ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, 1UL ); if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) ) diff --git a/src/flamenco/gossip/fd_crds.h b/src/flamenco/gossip/fd_crds.h index 232d170df8..fbaa229155 100644 --- a/src/flamenco/gossip/fd_crds.h +++ b/src/flamenco/gossip/fd_crds.h @@ -2,6 +2,7 @@ #define HEADER_fd_src_flamenco_gossip_fd_crds_h #include "../../util/fd_util.h" +#include "../../util/net/fd_net_headers.h" struct fd_crds_value_private; typedef struct fd_crds_value_private fd_crds_value_t; @@ -101,7 +102,7 @@ fd_crds_release( fd_crds_t * crds, /* fd_crds_upserts checks if inserting the value into the CRDS would succeed. An insert will fail if the value is already present in the CRDS with a newer timestamp, or if the value is not present. */ - +int fd_crds_upserts( fd_crds_t * crds, fd_crds_value_t * value ); diff --git a/src/flamenco/gossip/fd_crds_value.h b/src/flamenco/gossip/fd_crds_value.h index a8925aa2a2..68f2e1d526 100644 --- a/src/flamenco/gossip/fd_crds_value.h +++ b/src/flamenco/gossip/fd_crds_value.h @@ -2,5 +2,65 @@ #define HEADER_fd_src_flamenco_gossip_fd_crds_value_h #include "../../util/fd_util.h" +#include "../../util/net/fd_net_headers.h" + +#define FD_GOSSIP_CONTACT_INFO_SOCKET_GOSSIP ( 0) +#define FD_GOSSIP_CONTACT_INFO_SOCKET_SERVE_REPAIR_QUIC ( 1) +#define FD_GOSSIP_CONTACT_INFO_SOCKET_RPC ( 2) +#define FD_GOSSIP_CONTACT_INFO_SOCKET_RPC_PUBSUB ( 3) +#define FD_GOSSIP_CONTACT_INFO_SOCKET_SERVE_REPAIR ( 4) +#define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU ( 5) +#define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_FORWARDS ( 6) +#define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_FORWARDS_QUIC ( 7) +#define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_QUIC ( 8) +#define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_VOTE ( 9) +#define FD_GOSSIP_CONTACT_INFO_SOCKET_TVU (10) +#define FD_GOSSIP_CONTACT_INFO_SOCKET_TVU_QUIC (11) +#define FD_GOSSIP_CONTACT_INFO_SOCKET_TPU_VOTE_QUIC (12) + +#define FD_GOSSIP_CLIENT_SOLANA (0) +#define FD_GOSSIP_CLIENT_JITO (1) +#define FD_GOSSIP_CLIENT_FD (2) +#define FD_GOSSIP_CLIENT_AGAVE (3) + +struct fd_gossip_contact_info { + long instance_creation_wallclock_nanos; + ushort shred_version; + + struct { + uchar client; + + ushort major; + ushort minor; + ushort patch; + + int has_commit; + uint commit; + uint feature_set; + } version; + + struct { + /* WARNING: in gossip contact info message, + ports are encoded in host form. The parser will + perform the conversion */ + fd_ip4_port_t addr; + } sockets[ 13UL ]; +}; + +typedef struct fd_gossip_contact_info fd_gossip_contact_info_t; + +struct fd_gossip_vote { + ulong slot; + uchar vote_tower_index; + uchar txn[ 1232UL ]; +}; + +typedef struct fd_gossip_vote fd_gossip_vote_t; + +struct fd_gossip_crds_value { + uchar signature[ 64UL ]; + // fd_gossip_crds_data_t data[ ]; +}; +typedef struct fd_gossip_crds_value fd_gossip_crds_value_t; #endif /* HEADER_fd_src_flamenco_gossip_fd_crds_value_h */ diff --git a/src/flamenco/gossip/fd_gossip.c b/src/flamenco/gossip/fd_gossip.c index c71c2d06e2..5c6f1c7929 100644 --- a/src/flamenco/gossip/fd_gossip.c +++ b/src/flamenco/gossip/fd_gossip.c @@ -1,171 +1,256 @@ #include "fd_gossip.h" #include "fd_gossip_types.h" +#include "fd_gossip_msg.h" +#include "fd_gossip_private.h" #include "fd_crds.h" #include "fd_active_set.h" #include "fd_prune_finder.h" #include "fd_ping_tracker.h" +#include "../../ballet/ed25519/fd_ed25519.h" +#include "../../ballet/sha256/fd_sha256.h" static int parse_message( uchar const * data, ulong data_sz, fd_gossip_message_t * message ) { - - return 0; + ulong decoded_sz = fd_gossip_msg_parse( message, data, data_sz ); + if( FD_UNLIKELY( !decoded_sz ) ) return FD_GOSSIP_RX_PARSE_ERR; + return FD_GOSSIP_RX_OK; } static int -rx_pull_request( fd_gossip_t * gossip, - fd_gossip_pull_request_t const * pull_request, - long now ) { - /* TODO: Implement data budget? */ - - fd_gossip_crds_data_t const * data = pull_request->value->data; - if( FD_UNLIKELY( data->tag!=FD_GOSSIP_VALUE_CONTACT_INFO ) ) return FD_GOSSIP_RX_ERR_PULL_REQUEST_NOT_CONTACT_INFO; - - fd_gossip_contact_info_t const * contact_info = data->contact_info; - if( FD_UNLIKELY( !memcmp( data->contact_info->pubkey, gossip->identity_pubkey, 32UL ) ) ) return FD_GOSSIP_RX_ERR_PULL_REQUEST_LOOPBACK; - - if( FD_UNLIKELY( !is_valid_address( node ) ) ) return FD_GOSSIP_RX_ERR_PULL_REQUEST_INVALID_ADDRESS; - - fd_gossip_crds_filter_t const * filter = pull_request->filter; - - /* TODO: Jitter? */ - long clamp_wallclock_lower_nanos = now - 15L*1000L*1000L*1000L; - long clamp_wallclock_upper_nanos = now + 15L*1000L*1000L*1000L; - if( FD_UNLIKELY( contact_info->wallclockwallclock>clamp_wallclock_upper_nanos ) ) return FD_GOSSIP_RX_ERR_PULL_REQUEST_WALLCLOCK; - - ulong packet_sz = 0UL; - uchar packet[ 1232UL; ]; - - for( fd_crds_iter_t it=fd_crds_mask_iter_init( gossip->crds, mask, mask_bits ); !fd_crds_mask_iter_done( it ); it=fd_crds_mask_iter_next(it) ) { - fd_crds_value_t * candidate = fd_crds_mask_iter_value( it ); - - /* TODO: Add jitter here? */ - if( FD_UNLIKELY( fd_crds_value_wallclock( candidate )>contact_info->wallclock ) ) continue; - - ulong serialized_sz; - error = serialize_crds_value_into_packet( candidate, packet, 1232UL-packet_sz, &serialized_sz ); - if( FD_LIKELY( !error ) ) { - packet_sz += serialized_sz; - } else { - /* CRDS value can't fit into the packet anymore, just ship what - we have now and start a new one. */ - gossip->tx_fn( gossip->tx_ctx, packet, packet_sz ); - packet_sz = 0UL; - } +verify_signatures( fd_gossip_message_t const * message, + uchar const * payload ) { + if( FD_UNLIKELY( !( !!message->has_signable_data || !!message->crds_cnt ) ) ) + return FD_GOSSIP_RX_VERIFY_NO_SIGNABLE_DATA; + + fd_sha512_t sha[1]; + + int err = 0; + /* Optimize for CRDS composites (push/pull) that don't have an outer signable + data */ + if( FD_UNLIKELY( message->has_signable_data ) ) { + /* TODO: Special case for prune */ + err = fd_ed25519_verify( payload + message->signable_data_offset, + message->signable_sz, + message->signature, + message->pubkey, + sha ); } - - /* TODO: Send packet if there's anything leftover */ - - return 0; -} - -static int -rx_pull_response( fd_gossip_t * gossip, - fd_gossip_pull_response_t const * pull_response, - long now ) { - /* TODO: use epoch_duration and make timeouts ... ? */ - - for( ulong i=0UL; ivalues_len; i++ ) { - int upserts = fd_crds_upserts( gossip->crds, pull_response->values[ i ] ); - - if( FD_UNLIKELY( !upserts ) ) { - failed_inserts_append( gossip, pull_response->values[ i ] ); - continue; - } - - /* TODO: Is this jittered in Agave? */ - long accept_after_nanos; - if( FD_UNLIKELY( !memcmp( pull_response->sender_pubkey, gossip->identity_pubkey, 32UL ) ) ) { - accept_after_nanos = 0L; - } else if( stake( pull_response->sender_pubkey ) ) { - accept_after_nanos = now-15L*1000L*1000L*1000L; - } else { - accept_after_nanos = now-432000L*1000L*1000L*1000L; - } - - if( FD_LIKELY( accept_after_nanos<=fd_crds_value_wallclock( pull_response->values[ i ] ) ) ) { - fd_crds_insert( gossip->crds, pull_response->values[ i ], now ); - fd_crds_update_record_timestamp( pull_response->sender_pubkey, now ); - } else if( fd_crds_has_contact_info( pull_response->sender_pubkey ) ) { - fd_crds_insert( gossip->crds, pull_response->values[ i ], now ); - } else { - failed_inserts_append( gossip, pull_response->values[ i ] ); - } + if( FD_UNLIKELY( err != FD_ED25519_SUCCESS ) ) return err; + + /* Verify CRDS entries */ + for( ulong i=0UL; icrds_cnt; i++ ) { + err = fd_ed25519_verify( payload + message->crds[i].offset + 64UL, + message->crds[i].sz - 64UL, + message->crds[i].signature, + message->pubkey, + sha ); + + /* Full message must be dropped if any one value fails verify */ + if( FD_UNLIKELY( err != FD_ED25519_SUCCESS ) ) return err; } - return 0; + return FD_GOSSIP_RX_OK; } -static int -rx_push( fd_gossip_t * gossip, - fd_gossip_push_t const * push, - long now ) { - uchar const * relayer_pubkey = push->sender_pubkey; - - for( ulong i=0UL; ivalues_len; i++ ) { - fd_gossip_crds_value_t * value = push->values[ i ]; - if( FD_UNLIKELY( value->timestamptimestamp>now+30L*1000L*1000L*1000L ) ) continue; - - uchar const * origin_pubkey = fd_crds_value_pubkey( value ); - - int error = fd_crds_insert( gossip->crds, value, now ); - ulong num_duplicates = 0UL; - if( FD_UNLIKELY( error>0 ) ) num_duplicates = (ulong)error; - else if( FD_UNLIKELY( error<0 ) ) num_duplicates = ULONG_MAX; +static fd_gossip_message_t * +new_outgoing( fd_gossip_t * gossip ) { + fd_gossip_msg_init( gossip->outgoing ); + return gossip->outgoing; +} - fd_prune_finder_record( gossip->prune_finder, origin_pubkey, relayer_pubkey, num_duplicates ); - } - return 0; -} +// static int +// rx_pull_request( fd_gossip_t * gossip, +// fd_gossip_pull_request_t const * pull_request, +// long now ) { +// /* TODO: Implement data budget? */ + +// fd_gossip_crds_data_t const * data = pull_request->value->data; +// if( FD_UNLIKELY( data->tag!=FD_GOSSIP_VALUE_CONTACT_INFO ) ) return FD_GOSSIP_RX_ERR_PULL_REQUEST_NOT_CONTACT_INFO; + +// fd_gossip_contact_info_t const * contact_info = data->contact_info; +// if( FD_UNLIKELY( !memcmp( data->contact_info->pubkey, gossip->identity_pubkey, 32UL ) ) ) return FD_GOSSIP_RX_ERR_PULL_REQUEST_LOOPBACK; + +// if( FD_UNLIKELY( !is_valid_address( node ) ) ) return FD_GOSSIP_RX_ERR_PULL_REQUEST_INVALID_ADDRESS; + +// fd_gossip_crds_filter_t const * filter = pull_request->filter; + +// /* TODO: Jitter? */ +// long clamp_wallclock_lower_nanos = now - 15L*1000L*1000L*1000L; +// long clamp_wallclock_upper_nanos = now + 15L*1000L*1000L*1000L; +// if( FD_UNLIKELY( contact_info->wallclockwallclock>clamp_wallclock_upper_nanos ) ) return FD_GOSSIP_RX_ERR_PULL_REQUEST_WALLCLOCK; + +// ulong packet_sz = 0UL; +// uchar packet[ 1232UL ]; + +// for( fd_crds_iter_t it=fd_crds_mask_iter_init( gossip->crds, mask, mask_bits ); !fd_crds_mask_iter_done( it ); it=fd_crds_mask_iter_next(it) ) { +// fd_crds_value_t * candidate = fd_crds_mask_iter_value( it ); + +// /* TODO: Add jitter here? */ +// if( FD_UNLIKELY( fd_crds_value_wallclock( candidate )>contact_info->wallclock ) ) continue; + +// ulong serialized_sz; +// error = serialize_crds_value_into_packet( candidate, packet, 1232UL-packet_sz, &serialized_sz ); +// if( FD_LIKELY( !error ) ) { +// packet_sz += serialized_sz; +// } else { +// /* CRDS value can't fit into the packet anymore, just ship what +// we have now and start a new one. */ +// gossip->tx_fn( gossip->tx_ctx, packet, packet_sz ); +// packet_sz = 0UL; +// } +// } + +// /* TODO: Send packet if there's anything leftover */ + +// return 0; +// } + +// static int +// rx_pull_response( fd_gossip_t * gossip, +// fd_gossip_pull_response_t const * pull_response, +// long now ) { +// /* TODO: use epoch_duration and make timeouts ... ? */ + +// for( ulong i=0UL; ivalues_len; i++ ) { +// int upserts = fd_crds_upserts( gossip->crds, pull_response->values[ i ] ); + +// if( FD_UNLIKELY( !upserts ) ) { +// failed_inserts_append( gossip, pull_response->values[ i ] ); +// continue; +// } + +// /* TODO: Is this jittered in Agave? */ +// long accept_after_nanos; +// if( FD_UNLIKELY( !memcmp( pull_response->sender_pubkey, gossip->identity_pubkey, 32UL ) ) ) { +// accept_after_nanos = 0L; +// } else if( stake( pull_response->sender_pubkey ) ) { +// accept_after_nanos = now-15L*1000L*1000L*1000L; +// } else { +// accept_after_nanos = now-432000L*1000L*1000L*1000L; +// } + +// if( FD_LIKELY( accept_after_nanos<=fd_crds_value_wallclock( pull_response->values[ i ] ) ) ) { +// fd_crds_insert( gossip->crds, pull_response->values[ i ], now ); +// fd_crds_update_record_timestamp( pull_response->sender_pubkey, now ); +// } else if( fd_crds_has_contact_info( pull_response->sender_pubkey ) ) { +// fd_crds_insert( gossip->crds, pull_response->values[ i ], now ); +// } else { +// failed_inserts_append( gossip, pull_response->values[ i ] ); +// } +// } + +// return 0; +// } + +// static int +// rx_push( fd_gossip_t * gossip, +// fd_gossip_push_t const * push, +// long now ) { +// uchar const * relayer_pubkey = push->sender_pubkey; + +// for( ulong i=0UL; ivalues_len; i++ ) { +// fd_gossip_crds_value_t * value = push->values[ i ]; +// if( FD_UNLIKELY( value->timestamptimestamp>now+30L*1000L*1000L*1000L ) ) continue; + +// uchar const * origin_pubkey = fd_crds_value_pubkey( value ); + +// int error = fd_crds_insert( gossip->crds, value, now ); +// ulong num_duplicates = 0UL; +// if( FD_UNLIKELY( error>0 ) ) num_duplicates = (ulong)error; +// else if( FD_UNLIKELY( error<0 ) ) num_duplicates = ULONG_MAX; + +// fd_prune_finder_record( gossip->prune_finder, origin_pubkey, relayer_pubkey, num_duplicates ); +// } + +// return 0; +// } static int rx_prune( fd_gossip_t * gossip, fd_gossip_prune_t const * prune, long now ) { - if( FD_UNLIKELY( now-500L*1000L*1000L>prune->data->wallclock ) ) return FD_GOSSIP_RX_ERR_PRUNE_TIMEOUT; - else if( FD_UNLIKELY( !memcmp( gossip->identity_pubkey, prune->data->destination, 32UL ) ) ) return FD_GOSSIP_RX_ERR_PRUNE_DESTINATION; + if( FD_UNLIKELY( now-500L*1000L*1000L>(long)prune->wallclock ) ) return FD_GOSSIP_RX_PRUNE_ERR_TIMEOUT; + else if( FD_UNLIKELY( !!memcmp( gossip->identity_pubkey, prune->destination, 32UL ) ) ) return FD_GOSSIP_RX_PRUNE_ERR_DESTINATION; - ulong identity_stake = ??; - for( ulong i=0UL; idata->prunes_len; i++ ) { - ulong origin_stake = ??; + ulong identity_stake = 0UL; /* FIXME */ + for( ulong i=0UL; iprunes_len; i++ ) { + ulong origin_stake = 0UL; /* FIXME */ fd_active_set_prune( gossip->active_set, gossip->identity_pubkey, - gossip->identity_stake, - prune->data->pubkey, - prune->data->destination, - prune->data->prunes[ i ], + identity_stake, + prune->from, + prune->destination, + prune->prunes[ i ], origin_stake ); } + return FD_GOSSIP_RX_OK; } static int -rx_ping( fd_gossip_t * gossip, - fd_gossip_ping_t * ping ) { +rx_ping( fd_gossip_t * gossip, + fd_gossip_ping_pong_t * ping, + fd_ip4_port_t * peer_address, + long now ) { fd_gossip_message_t * message = new_outgoing( gossip ); message->tag = FD_GOSSIP_MESSAGE_PONG; - fd_memcpy( message->pong->from, gossip->identity_pubkey, 32UL ); - message->pong->hash = hash_ping_token( ping->token ); - gossip->sign_fn( gossip->sign_ctx, message->pong->hash, 32UL, message->pong->signature ); - - /* TODO: Send it */ + fd_memcpy( message->piong->from, gossip->identity_pubkey, 32UL ); + fd_ping_tracker_hash_ping_token( ping->token, message->piong->hash ); + gossip->sign_fn( gossip->sign_ctx, message->piong->hash, 32UL, message->piong->signature ); + + fd_ping_tracker_track( gossip->ping_tracker, + ping->from, + 0UL, /* FIXME: Get stake */ + peer_address, + now ); + uchar payload[ 1232UL ]; + ulong payload_sz = fd_gossip_msg_serialize( message, payload, 1232UL ); + gossip->send_fn( gossip->send_ctx, payload, payload_sz, peer_address ); + return FD_GOSSIP_RX_OK; } static int -rx_pong( fd_gossip_t * gossip, - fd_gossip_pong_t * pong ) { - for( ulong i=0UL; i<2UL; i++ ) { - - if( FD_LIKELY( hash_ping_token( ) ) ) { - return FD_GOSSIP_RX_SUCCESS; - } - } +rx_pong( fd_gossip_t * gossip, + fd_gossip_ping_pong_t * pong, + fd_ip4_port_t * peer_address, + long now ) { + fd_ping_tracker_register( gossip->ping_tracker, + pong->from, + 0UL, /* FIXME: Get stake */ + peer_address, + pong->hash, + now ); + return 0; +} - return FD_GOSSIP_RX_ERR_PONG_UNMATCHED; +/* FIXME: This feels like it should be higher up the rx processing stack */ +static int +strip_network_hdrs( uchar const * data, + ulong data_sz, + uchar ** const payload, + ulong * payload_sz, + fd_ip4_port_t * peer_address ) { + fd_eth_hdr_t const * eth = (fd_eth_hdr_t const *)data; + fd_ip4_hdr_t const * ip4 = (fd_ip4_hdr_t const *)( (ulong)eth + sizeof(fd_eth_hdr_t) ); + fd_udp_hdr_t const * udp = (fd_udp_hdr_t const *)( (ulong)ip4 + FD_IP4_GET_LEN( *ip4 ) ); + + if( FD_UNLIKELY( (ulong)udp+sizeof(fd_udp_hdr_t) > (ulong)eth+data_sz ) ) return FD_GOSSIP_RX_ERR_NETWORK_HDRS; + ulong udp_sz = fd_ushort_bswap( udp->net_len ); + if( FD_UNLIKELY( udp_sz (ulong)eth+data_sz ) ) return FD_GOSSIP_RX_ERR_NETWORK_HDRS; + + *payload = (uchar *)( (ulong)udp + sizeof(fd_udp_hdr_t) ); + *payload_sz = payload_sz_; + + peer_address->addr = ip4->saddr; + peer_address->port = udp->net_sport; + return FD_GOSSIP_RX_OK; } int @@ -173,43 +258,55 @@ fd_gossip_rx( fd_gossip_t * gossip, uchar const * data, ulong data_sz, long now ) { - fd_gossip_message_t message[ 1 ]; - int error = parse_message( data, data_sz, message ); - if( FD_UNLIKELY( error ) ) return error; - error = verify_signatures( gossip, message ); - if( FD_UNLIKELY( error ) ) return error; + uchar * gossip_payload; + ulong gossip_payload_sz; + fd_ip4_port_t peer_address; - error = filter_shred_version( gossip, message ); + int error = strip_network_hdrs( data, + data_sz, + &gossip_payload, + &gossip_payload_sz, + &peer_address ); if( FD_UNLIKELY( error ) ) return error; - error = check_duplicate_instance( gossip, message ); + fd_gossip_message_t message[ 1 ]; + ulong decode_sz = fd_gossip_msg_parse( message, gossip_payload, gossip_payload_sz ); + if( FD_UNLIKELY( !!decode_sz ) ) return FD_GOSSIP_RX_PARSE_ERR; + + error = verify_signatures( message, data ); if( FD_UNLIKELY( error ) ) return error; + // error = filter_shred_version( gossip, message ); + // if( FD_UNLIKELY( error ) ) return error; + + // error = check_duplicate_instance( gossip, message ); + // if( FD_UNLIKELY( error ) ) return error; + /* TODO: This should verify ping tracker active for pull request */ - error = verify_gossip_address( gossip, message ); + // error = verify_gossip_address( gossip, message ); if( FD_UNLIKELY( error ) ) return error; /* TODO: Implement traffic shaper / bandwidth limiter */ switch( message->tag ) { case FD_GOSSIP_MESSAGE_PULL_REQUEST: - error = rx_pull_request( gossip, message->pull_request ); + // error = rx_pull_request( gossip, message->pull_request ); break; case FD_GOSSIP_MESSAGE_PULL_RESPONSE: - error = rx_pull_response( gossip, message->pull_response ); + // error = rx_pull_response( gossip, message->pull_response ); break; case FD_GOSSIP_MESSAGE_PUSH: - error = rx_push( gossip, message->push ); + // error = rx_push( gossip, message->push ); break; case FD_GOSSIP_MESSAGE_PRUNE: - error = rx_prune( gossip, message->prune ); + error = rx_prune( gossip, message->prune, now ); break; case FD_GOSSIP_MESSAGE_PING: - error = rx_ping( gossip, message->ping ); + error = rx_ping( gossip, message->piong, &peer_address, now ); break; case FD_GOSSIP_MESSAGE_PONG: - error = rx_pong( gossip, message->pong ); + error = rx_pong( gossip, message->piong, &peer_address, now ); break; default: FD_LOG_CRIT(( "Unknown gossip message type %d", message->tag )); @@ -236,8 +333,22 @@ static void tx_ping( fd_gossip_t * gossip, long now ) { uchar const * peer_pubkey; - while( fd_ping_tracker_pop_request( gossip->ping_tracker, now, &peer_pubkey ) ) { - /* TODO: Generate and send a ping message ... */ + uchar const * ping_token; + fd_ip4_port_t const * peer_address; + while( fd_ping_tracker_pop_request( gossip->ping_tracker, + now, + &peer_pubkey, + &peer_address, + &ping_token ) ) { + fd_gossip_message_t * message = new_outgoing( gossip ); + message->tag = FD_GOSSIP_MESSAGE_PING; + fd_memcpy( message->piong->from, gossip->identity_pubkey, 32UL ); + fd_memcpy( message->piong->token, ping_token, 32UL ); + gossip->sign_fn( gossip->sign_ctx, message->piong->token, 32UL, message->pong->signature ); + + uchar payload[ 1232UL ]; + ulong payload_sz = fd_gossip_msg_serialize( message, payload, 1232UL ); + gossip->send_fn( gossip->send_ctx, payload, payload_sz, peer_address ); } } diff --git a/src/flamenco/gossip/fd_gossip.h b/src/flamenco/gossip/fd_gossip.h index 1d847478d5..d6a34f1868 100644 --- a/src/flamenco/gossip/fd_gossip.h +++ b/src/flamenco/gossip/fd_gossip.h @@ -4,6 +4,16 @@ #include "../../util/rng/fd_rng.h" #include "../../util/net/fd_net_headers.h" +#define FD_GOSSIP_RX_OK (0) + +#define FD_GOSSIP_RX_PARSE_ERR (1) +#define FD_GOSSIP_RX_ERR_NETWORK_HDRS (2) + +#define FD_GOSSIP_RX_VERIFY_NO_SIGNABLE_DATA (1) + +#define FD_GOSSIP_RX_PRUNE_ERR_TIMEOUT (1) +#define FD_GOSSIP_RX_PRUNE_ERR_DESTINATION (2) + /* TODO: When we get a pull request, respond with ContactInfos first if we have any available that are responsive. */ diff --git a/src/flamenco/gossip/fd_gossip_msg.c b/src/flamenco/gossip/fd_gossip_msg.c new file mode 100644 index 0000000000..d11950e249 --- /dev/null +++ b/src/flamenco/gossip/fd_gossip_msg.c @@ -0,0 +1,6 @@ +#include "fd_gossip_msg.h" +void +fd_gossip_msg_init( fd_gossip_message_t * msg ) { + fd_memset( msg, 0, sizeof(fd_gossip_message_t) ); + msg->tag = FD_GOSSIP_MESSAGE_END; /* default to invalid message */ +} diff --git a/src/flamenco/gossip/fd_gossip_msg.h b/src/flamenco/gossip/fd_gossip_msg.h new file mode 100644 index 0000000000..808e8491d8 --- /dev/null +++ b/src/flamenco/gossip/fd_gossip_msg.h @@ -0,0 +1,107 @@ +#ifndef HEADER_fd_src_flamenco_gossip_fd_gossip_msg_h +#define HEADER_fd_src_flamenco_gossip_fd_gossip_msg_h + +#include "fd_gossip_types.h" +#include "fd_crds_value.h" + + +#define FD_GOSSIP_MSG_MTU (1232UL) /* Maximum size of a gossip message */ +/* Deriving maximum number of CRDS values a message can hold: + - Maximum bytes the CRDS array can hold is + 1232(MTU)-4(msg disc)-32(pubkey)-8(crds len)=1188b + - Smallest CRDS value is 64+4+48=116b + (64b signature + 4b discriminant + 48b slot hashes) + - So, maximum number of CRDS values is 1188/(64+4+48) ~= 10 + - TODO: We might want to use a more conservative estimate that only includes + the size of the signature and discriminant. */ +#define FD_GOSSIP_MSG_MAX_CRDS (10UL) + +/* FIXME: This should be imported from fd_crds instead. */ +struct fd_crds_key { + uchar tag; + uchar pubkey[ 32UL ]; + union { + uchar vote_index; + uchar epoch_slots_index; + ushort duplicate_shred_index; + }; +}; +typedef struct fd_crds_key fd_crds_key_t; + +struct fd_gossip_message { + uchar tag; // uint in rust bincode + union { + fd_gossip_pull_request_t pull_request[ 1 ]; + fd_gossip_pull_response_t pull_response[ 1 ]; /* CRDS Composite Type */ + fd_gossip_push_t push[ 1 ]; /* CRDS Composite Type */ + fd_gossip_prune_t prune[ 1 ]; + fd_gossip_ping_pong_t piong[ 1 ]; + }; + + /* Begin parsed gossip message metadata + + FIXME: These are strictly to operate on a parsed + gossip message that is received in encoded form. The structure + is a little awkward, especially if using this same struct to encode + a message. Crux of the problem is half the fd_gossip_message fields function as metadata + for the encoded message/payload, and the other half owns the data it parses + (namely the inner message types defined above) via memcpys. We can: + - Split this into two structs, one for metadata and one for the + inner message types. This would be a little cleaner, but also a little + more work to maintain. + - Leave it as is, and just document the structure. This isn't as clean + but is less work to maintain. */ + + /* Signature related metadata, analagous to Agave's Signable trait (at least on the rx side) + FIXME: Prune does not define signable data as a contiguous region, which is really annoying */ + struct{ + uchar has_signable_data; /* 0 for CRDS composite type */ + + /* Should these be offsets in payload instead? */ + uchar pubkey[32UL]; + uchar signature[64UL]; + + ulong signable_data_offset; /* offset to start of signable region in payload */ + ulong signable_sz; + }; + + uchar has_shred_version; + ushort shred_version; + + /* For CRDS composites, this holds information about the CRDS values necessary + to perform an insertion into the CRDS and signature verification */ + ulong crds_cnt; /* number of CRDS values in the message, if any */ + struct { + ulong offset; /* offset to start of CRDS value in payload */ + ulong sz; /* size of CRDS value in payload */ + + fd_crds_key_t key; + + + uchar signature[64UL]; // signable data is always offset + sizeof(signature); signable_sz = sz - sizeof(signature) + long wallclock_nanos; + + union { + fd_gossip_contact_info_t contact_info; + fd_gossip_vote_t vote; + }; + } crds[ FD_GOSSIP_MSG_MAX_CRDS ]; + +}; + +typedef struct fd_gossip_message fd_gossip_message_t; + +void +fd_gossip_msg_init( fd_gossip_message_t * msg ); + +ulong +fd_gossip_msg_parse( fd_gossip_message_t * msg, + uchar const * payload, + ulong payload_sz ); + +ulong +fd_gossip_msg_serialize( fd_gossip_message_t const * msg, + uchar * payload, + ulong payload_sz ); + +#endif /* HEADER_fd_src_flamenco_gossip_fd_gossip_msg_h */ diff --git a/src/flamenco/gossip/fd_gossip_msg_parse.c b/src/flamenco/gossip/fd_gossip_msg_parse.c new file mode 100644 index 0000000000..409a0207fc --- /dev/null +++ b/src/flamenco/gossip/fd_gossip_msg_parse.c @@ -0,0 +1,90 @@ +#include "fd_gossip_msg.h" +// #include "../../ballet/txn/fd_compact_u16.h" + + +/* Adapted from fd_txn_parse.c */ +#define CHECK_INIT( payload, payload_sz ) \ + uchar const * _payload = (payload); \ + ulong _payload_sz = (payload_sz); \ + ulong _bytes_consumed = 0; \ + ulong i = 0; \ + (void) _payload; \ + (void) _bytes_consumed; \ + +#define CHECK( cond ) do { \ + if( FD_UNLIKELY( !(cond) ) ) { \ + return 0; \ + } \ +} while( 0 ) + +#define CHECK_LEFT( n ) CHECK( (n)<=(_payload_sz-i) ) + +// #define READ_CHECKED_COMPACT_U16( out_sz, var_name, where ) \ +// do { \ +// ulong _where = (where); \ +// ulong _out_sz = fd_cu16_dec_sz( _payload+_where, _payload_sz-_where ); \ +// CHECK( _out_sz ); \ +// (var_name) = fd_cu16_dec_fixed( _payload+_where, _out_sz ); \ +// (out_sz) = _out_sz; \ +// } while( 0 ) + +static ulong +fd_gossip_msg_ping_pong_parse( fd_gossip_message_t * msg, + uchar const * payload, + ulong payload_sz ) { + CHECK_INIT( payload, payload_sz ); + fd_gossip_ping_pong_t * piong = msg->piong; + CHECK_LEFT( 32UL ); memcpy( piong->from, payload+i, 32UL ); i+=32UL; /* Pubkey */ + CHECK_LEFT( 32UL ); memcpy( piong->hash, payload+i, 32UL ); i+=32UL; /* Token/Hash */ + CHECK_LEFT( 64UL ); memcpy( piong->signature, payload+i, 64UL ); i+=64UL; /* Signature */ + + /* metadata */ + fd_memcpy( msg->pubkey, piong->from, 32UL ); + fd_memcpy( msg->signature, piong->signature, 64UL ); + + msg->has_signable_data = 1; + msg->signable_data_offset = 32UL; + msg->signable_sz = 32UL; + + return i; +} + +ulong +fd_gossip_msg_parse( fd_gossip_message_t * msg, + uchar const * payload, + ulong payload_sz ) { + CHECK_INIT( payload, payload_sz ); + CHECK( payload_sz<=FD_GOSSIP_MSG_MTU ); + + /* Extract enum discriminant/tag (4b encoded) */ + uint tag = 0; + CHECK_LEFT( 4UL ); tag = payload[ i ]; i+=4; + CHECK( tagtag = (uchar)tag; + + ulong inner_decoded_sz = 0UL; + switch( msg->tag ){ + case FD_GOSSIP_MESSAGE_PULL_REQUEST: + case FD_GOSSIP_MESSAGE_PULL_RESPONSE: + case FD_GOSSIP_MESSAGE_PUSH: + case FD_GOSSIP_MESSAGE_PRUNE: + FD_LOG_ERR(( "Gossip message type %d parser not implemented", msg->tag )); + break; + case FD_GOSSIP_MESSAGE_PING: + inner_decoded_sz = fd_gossip_msg_ping_pong_parse( msg, payload+i, payload_sz-i ); + CHECK( inner_decoded_sz==payload_sz-i ); + break; + default: + return 0; + } + i += inner_decoded_sz; + CHECK( i<=payload_sz ); + + /* Need to increment inner offsets by 4b to account for tag + TODO: make this less error prone (at this point message is technically validated) */ + msg->signable_data_offset += 4UL; + for( ulong j=0; jcrds_cnt; j++ ) { + msg->crds[j].offset += 4UL; + } + return i; +} diff --git a/src/flamenco/gossip/fd_gossip_msg_ser.c b/src/flamenco/gossip/fd_gossip_msg_ser.c new file mode 100644 index 0000000000..88ccf4363e --- /dev/null +++ b/src/flamenco/gossip/fd_gossip_msg_ser.c @@ -0,0 +1,71 @@ +#include "fd_gossip_msg.h" + +#define CHECK_INIT( payload, payload_sz ) \ + uchar const * _payload = (payload); \ + ulong _payload_sz = (payload_sz); \ + ulong _bytes_consumed = 0; \ + ulong i = 0; \ + (void) _payload; \ + (void) _bytes_consumed; \ + +#define CHECK( cond ) do { \ + if( FD_UNLIKELY( !(cond) ) ) { \ + return 0; \ + } \ +} while( 0 ) + +#define CHECK_LEFT( n ) CHECK( (n)<=(_payload_sz-i) ) + + +static ulong +fd_gossip_msg_ping_pong_serialize( fd_gossip_message_t const * msg, + uchar * payload, + ulong payload_sz ) { + CHECK_INIT( payload, payload_sz ); + fd_gossip_ping_pong_t const * piong = msg->piong; + CHECK_LEFT( 32 + 32 + 64 ); /* Pubkey + Hash/Token + Signature */ + fd_memcpy( payload+i, piong->from, 32UL ); i+=32UL; /* Pubkey */ + fd_memcpy( payload+i, piong->hash, 32UL ); i+=32UL; /* Hash */ + fd_memcpy( payload+i, piong->signature, 64UL ); i+=64UL; /* Signature */ + + return i; +} + +ulong +fd_gossip_msg_serialize( fd_gossip_message_t const * msg, + uchar * payload, + ulong payload_sz ) { + /* Serialize tag */ + if( FD_UNLIKELY( msg->tag>=FD_GOSSIP_MESSAGE_END ) ) { + FD_LOG_ERR(( "Invalid message tag %d", msg->tag )); + return 0; + } + + CHECK_INIT( payload, payload_sz ); + CHECK_LEFT( 4UL ); /* Tag/Discriminant is actually 4b */ + payload[i] = msg->tag; i+=4UL; + + ulong inner_payload_sz = 0UL; + switch( msg->tag ) { + case FD_GOSSIP_MESSAGE_PULL_REQUEST: + FD_LOG_ERR(( "Gossip message type %d serializer not implemented", msg->tag )); + break; + case FD_GOSSIP_MESSAGE_PULL_RESPONSE: + FD_LOG_ERR(( "Gossip message type %d serializer not implemented", msg->tag )); + break; + case FD_GOSSIP_MESSAGE_PUSH: + FD_LOG_ERR(( "Gossip message type %d serializer not implemented", msg->tag )); + break; + case FD_GOSSIP_MESSAGE_PRUNE: + FD_LOG_ERR(( "Gossip message type %d serializer not implemented", msg->tag )); + break; + case FD_GOSSIP_MESSAGE_PING: + case FD_GOSSIP_MESSAGE_PONG: + inner_payload_sz = fd_gossip_msg_ping_pong_serialize( msg, payload+i, payload_sz-i ); + break; + default: + FD_LOG_ERR(( "Unknown message tag %d", msg->tag )); + } + CHECK( inner_payload_sz!=0UL ); + return payload_sz + i; +} diff --git a/src/flamenco/gossip/fd_gossip_private.h b/src/flamenco/gossip/fd_gossip_private.h new file mode 100644 index 0000000000..f5acc8af56 --- /dev/null +++ b/src/flamenco/gossip/fd_gossip_private.h @@ -0,0 +1,37 @@ +#ifndef HEADER_fd_src_flamenco_gossip_fd_gossip_private_h +#define HEADER_fd_src_flamenco_gossip_fd_gossip_private_h + +#include "fd_active_set.h" +#include "fd_gossip.h" +#include "fd_crds.h" +#include "fd_gossip_msg.h" +#include "fd_ping_tracker.h" + +typedef void (*fd_gossip_send_fn)( void * ctx, + uchar const * data, + ulong sz, + fd_ip4_port_t const * peer_address ); +typedef void (*fd_gossip_sign_fn)( void * ctx, + uchar const * data, + ulong sz, + uchar * signature ); +struct fd_gossip_private { + uchar identity_pubkey[ 32UL ]; + + fd_gossip_metrics_t metrics[1]; + + fd_crds_t * crds; + fd_active_set_t * active_set; + fd_ping_tracker_t * ping_tracker; + + fd_gossip_message_t outgoing[ 1 ]; /* Not sure how to use this exactly */ + + /* Callbacks */ + fd_gossip_sign_fn sign_fn; + void * sign_ctx; + + fd_gossip_send_fn send_fn; + void * send_ctx; +}; + +#endif diff --git a/src/flamenco/gossip/fd_gossip_types.h b/src/flamenco/gossip/fd_gossip_types.h index d55c0d5f94..46bf8a74d6 100644 --- a/src/flamenco/gossip/fd_gossip_types.h +++ b/src/flamenco/gossip/fd_gossip_types.h @@ -1,4 +1,8 @@ +#ifndef HEADER_fd_src_flamenco_gossip_fd_gossip_types_h +#define HEADER_fd_src_flamenco_gossip_fd_gossip_types_h + #include "fd_gossip.h" +#include "fd_crds_value.h" #define FD_GOSSIP_MESSAGE_PULL_REQUEST (0) #define FD_GOSSIP_MESSAGE_PULL_RESPONSE (1) @@ -6,6 +10,7 @@ #define FD_GOSSIP_MESSAGE_PRUNE (3) #define FD_GOSSIP_MESSAGE_PING (4) #define FD_GOSSIP_MESSAGE_PONG (5) +#define FD_GOSSIP_MESSAGE_END (6) #define FD_GOSSIP_VALUE_VOTE ( 1) #define FD_GOSSIP_VALUE_LOWEST_SLOT ( 2) @@ -17,10 +22,10 @@ #define FD_GOSSIP_VALUE_RESTART_HEAVIEST_FORK (13) struct fd_gossip_bloom { - ulong keys_len; - ulong * keys; - ulong bits_len; - ulong * bits; + ulong keys_len; + ulong keys[ 150UL ]; /* max num keys if len(bits) == 1 */ + ulong bits_len; + ulong bits[ 150UL ]; /* max num bits if len(keys) == 1 */ ulong num_bits_set; }; @@ -34,28 +39,10 @@ struct fd_gossip_crds_filter { typedef struct fd_gossip_crds_filter fd_gossip_crds_filter_t; -struct fd_gossip_crds_data { - uchar tag; - union { - fd_gossip_vote_t vote[ 1 ]; - fd_gossip_lowest_slot_t lowest_slot[ 1 ]; - fd_gossip_epoch_slots_t epoch_slots[ 1 ]; - fd_gossip_duplicate_shred_t duplicate_shred[ 1 ]; - fd_gossip_snapshot_hashes_t snapshot_hashes[ 1 ]; - fd_gossip_contact_info_t contact_info[ 1 ]; - fd_gossip_restart_last_voted_fork_slots_t restart_last_voted_fork_slots[ 1 ]; - fd_gossip_restart_heaviest_fork_t restart_heaviest_fork[ 1 ]; - }; -}; typedef struct fd_gossip_crds_data fd_gossip_crds_data_t; -struct fd_gossip_crds_value { - uchar signature[ 64UL ]; - fd_gossip_crds_data_t data[ 1 ]; -}; -typedef struct fd_gossip_crds_value fd_gossip_crds_value_t; struct fd_gossip_pull_request { fd_gossip_crds_filter_t filter[ 1 ]; @@ -65,47 +52,41 @@ struct fd_gossip_pull_request { typedef struct fd_gossip_pull_request fd_gossip_pull_request_t; struct fd_gossip_pull_response { - uchar sender_pubkey[ 32UL ]; - ulong values_len; - fd_gossip_crds_value_t * values; + uchar sender_pubkey[ 32UL ]; + ulong values_len; + // fd_gossip_crds_value_t values[ ]; }; typedef struct fd_gossip_pull_response fd_gossip_pull_response_t; struct fd_gossip_push { - uchar sender_pubkey[ 32UL ]; - ulong values_len; - fd_gossip_crds_value_t * values; + uchar sender_pubkey[ 32UL ]; + ulong values_len; + // fd_gossip_crds_value_t values[ ]; }; typedef struct fd_gossip_push fd_gossip_push_t; -struct fd_gossip_message_ping { +struct fd_gossip_message_ping_pong { uchar from[ 32UL ]; - uchar token[ 32UL ]; + union{ + uchar hash[ 32UL ]; /* Hash of the last ping */ + uchar token[ 32UL ]; /* Token to be used in the pong */ + }; uchar signature[ 64UL ]; }; -typedef struct fd_gossip_message_ping fd_gossip_ping_t; +typedef struct fd_gossip_message_ping_pong fd_gossip_ping_pong_t; -struct fd_gossip_message_pong { +struct fd_gossip_message_prune { uchar from[ 32UL ]; - uchar hash[ 32UL ]; + ulong prunes_len; + uchar prunes[ 33UL ][ 32UL ]; /* 33 pubkeys fit in MTU (rounded down) */ uchar signature[ 64UL ]; -}; + uchar destination[ 32UL ]; + ulong wallclock; -typedef struct fd_gossip_message_pong fd_gossip_pong_t; - -struct fd_gossip_message { - uchar tag; - union { - fd_gossip_pull_request_t pull_request[ 1 ]; - fd_gossip_pull_response_t pull_response[ 1 ]; - fd_gossip_push_t push[ 1 ]; - fd_gossip_prune_t prune[ 1 ]; - fd_gossip_ping_t ping[ 1 ]; - fd_gossip_pong_t pong[ 1 ]; - }; }; +typedef struct fd_gossip_message_prune fd_gossip_prune_t; -typedef struct fd_gossip_message fd_gossip_message_t; +#endif /* HEADER_fd_src_flamenco_gossip_fd_gossip_types_h */ diff --git a/src/flamenco/gossip/fd_ping_tracker.c b/src/flamenco/gossip/fd_ping_tracker.c index 483965d72a..9e0ea6e0da 100644 --- a/src/flamenco/gossip/fd_ping_tracker.c +++ b/src/flamenco/gossip/fd_ping_tracker.c @@ -201,6 +201,16 @@ fd_ping_tracker_join( void * shpt ) { return ping_tracker; } +static void +hash_ping_token( uchar const * ping_token, + uchar * expected_pong_token, + fd_sha256_t * sha ) { + fd_sha256_init( sha ); + fd_sha256_append( sha, "SOLANA_PING_PONG", 16UL ); + fd_sha256_append( sha, ping_token, 32UL ); + fd_sha256_fini( sha, expected_pong_token ); +} + static void remove_tracking( fd_ping_tracker_t * ping_tracker, fd_ping_peer_t * peer ) { @@ -235,10 +245,7 @@ fd_ping_tracker_track( fd_ping_tracker_t * ping_tracker, peer->state = FD_PING_TRACKER_STATE_INVALID; for( ulong i=0UL; i<32UL; i++ ) peer->ping_token[ i ] = fd_rng_uchar( ping_tracker->rng ); - fd_sha256_init( ping_tracker->sha ); - fd_sha256_append( ping_tracker->sha, "SOLANA_PING_PONG", 16UL ); - fd_sha256_append( ping_tracker->sha, peer->ping_token, 32UL ); - fd_sha256_fini( ping_tracker->sha, peer->expected_pong_token ); + hash_ping_token( peer->ping_token, peer->expected_pong_token, ping_tracker->sha ); invalid_list_ele_push_head( ping_tracker->invalid, peer, ping_tracker->pool ); peer_map_ele_insert( ping_tracker->peers, peer, ping_tracker->pool ); @@ -263,10 +270,7 @@ fd_ping_tracker_track( fd_ping_tracker_t * ping_tracker, peer->next_ping_nanos = now; peer->state = FD_PING_TRACKER_STATE_INVALID; for( ulong i=0UL; i<32UL; i++ ) peer->ping_token[ i ] = fd_rng_uchar( ping_tracker->rng ); - fd_sha256_init( ping_tracker->sha ); - fd_sha256_append( ping_tracker->sha, "SOLANA_PING_PONG", 16UL ); - fd_sha256_append( ping_tracker->sha, peer->ping_token, 32UL ); - fd_sha256_fini( ping_tracker->sha, peer->expected_pong_token ); + hash_ping_token( peer->ping_token, peer->expected_pong_token, ping_tracker->sha ); invalid_list_ele_push_head( ping_tracker->invalid, peer, ping_tracker->pool ); } } @@ -297,10 +301,6 @@ fd_ping_tracker_register( fd_ping_tracker_t * ping_tracker, remove_tracking( ping_tracker, peer ); peer->state = FD_PING_TRACKER_STATE_VALID; for( ulong i=0UL; i<32UL; i++ ) peer->ping_token[ i ] = fd_rng_uchar( ping_tracker->rng ); - fd_sha256_init( ping_tracker->sha ); - fd_sha256_append( ping_tracker->sha, "SOLANA_PING_PONG", 16UL ); - fd_sha256_append( ping_tracker->sha, peer->ping_token, 32UL ); - fd_sha256_fini( ping_tracker->sha, peer->expected_pong_token ); waiting_list_ele_push_tail( ping_tracker->waiting, peer, ping_tracker->pool ); } @@ -375,3 +375,10 @@ fd_ping_tracker_pop_request( fd_ping_tracker_t * ping_tracker, return 1; } } + +void +fd_ping_tracker_hash_ping_token( uchar const * token, + uchar * hash ) { + fd_sha256_t sha[1]; + hash_ping_token( token, hash, sha ); +} diff --git a/src/flamenco/gossip/fd_ping_tracker.h b/src/flamenco/gossip/fd_ping_tracker.h index cdd83ec204..46a2a6167a 100644 --- a/src/flamenco/gossip/fd_ping_tracker.h +++ b/src/flamenco/gossip/fd_ping_tracker.h @@ -20,7 +20,7 @@ Once a peer has been pinged, we wait up to twenty seconds for a response before trying again. We repeatedly retry pinging the peer - until the peer responds, or their most recent mesage becomes older + until the peer responds, or their most recent message becomes older than two minutes. Once a peer is validated by responding to a ping with a valid pong, @@ -120,4 +120,14 @@ fd_ping_tracker_pop_request( fd_ping_tracker_t * ping_tracker, fd_ip4_port_t const ** out_peer_address, uchar const ** out_token ); +/* fd_ping_tracker_hash_ping_token generates a hash of a ping token, to be + embedded in a corresponding pong message that is then verified by the ping + sender. + + Assumes both token and hash are the starting address of a 32byte region of + memory */ +void +fd_ping_tracker_hash_ping_token( uchar const * token, + uchar * hash ); + #endif /* HEADER_fd_src_flamenco_gossip_fd_ping_tracker_h */