Skip to content

Commit bcb0ba0

Browse files
committed
gui: fetch vote info from manifest
1 parent 43b71fe commit bcb0ba0

File tree

6 files changed

+125
-58
lines changed

6 files changed

+125
-58
lines changed

src/app/firedancer/topology.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -928,8 +928,9 @@ fd_topo_initialize( config_t * config ) {
928928
FOR(exec_tile_cnt) fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "exec_replay", i, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
929929

930930
if( FD_LIKELY( snapshots_enabled ) ) {
931-
/**/ fd_topob_tile_in ( topo, "gui", 0UL, "metric_in", "snapct_gui", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
932-
/**/ fd_topob_tile_in ( topo, "gui", 0UL, "metric_in", "snapin_gui", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
931+
/**/ fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "snapct_gui", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
932+
/**/ fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "snapin_gui", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
933+
fd_topob_tile_in( topo, "gui", 0UL, "metric_in", "snapin_manif", 0UL, FD_TOPOB_RELIABLE, FD_TOPOB_POLLED );
933934
}
934935
}
935936

src/disco/gui/fd_gui_config_parse.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ struct fd_gui_config_parse_info {
2626
char icon_uri[ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_ICON_URI_SZ + 1UL ];
2727
char keybase_username[ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_KEYBASE_USERNAME_SZ + 1UL ];
2828

29-
struct { ulong prev, next; } map;
29+
struct { ulong next; } map;
3030
struct { ulong next; } pool;
3131
};
3232

src/disco/gui/fd_gui_peers.c

Lines changed: 57 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ fd_gui_peers_align( void ) {
1818
a = fd_ulong_max( a, fd_gui_peers_live_table_align() );
1919
a = fd_ulong_max( a, fd_gui_peers_bandwidth_tracking_align() );
2020
a = fd_ulong_max( a, fd_gui_peers_node_info_pool_align() );
21+
a = fd_ulong_max( a, fd_gui_peers_node_vote_pool_align() );
2122
a = fd_ulong_max( a, fd_gui_peers_node_info_map_align() );
23+
a = fd_ulong_max( a, fd_gui_peers_node_vote_map_align() );
2224
a = fd_ulong_max( a, fd_gui_peers_node_pubkey_map_align() );
2325
a = fd_ulong_max( a, fd_gui_peers_node_sock_map_align() );
2426
a = fd_ulong_max( a, alignof(fd_gui_peers_ws_conn_t) );
@@ -30,6 +32,7 @@ fd_gui_peers_align( void ) {
3032
FD_FN_CONST ulong
3133
fd_gui_peers_footprint( ulong max_ws_conn_cnt ) {
3234
ulong info_chain_cnt = fd_gui_peers_node_info_map_chain_cnt_est ( FD_CONTACT_INFO_TABLE_SIZE );
35+
ulong vote_chain_cnt = fd_gui_peers_node_vote_map_chain_cnt_est ( FD_RUNTIME_MAX_VOTE_ACCOUNTS );
3336
ulong pubkey_chain_cnt = fd_gui_peers_node_pubkey_map_chain_cnt_est( FD_CONTACT_INFO_TABLE_SIZE );
3437
ulong sock_chain_cnt = fd_gui_peers_node_sock_map_chain_cnt_est ( FD_CONTACT_INFO_TABLE_SIZE );
3538

@@ -38,11 +41,13 @@ fd_gui_peers_footprint( ulong max_ws_conn_cnt ) {
3841
l = FD_LAYOUT_APPEND( l, fd_gui_peers_live_table_align(), fd_gui_peers_live_table_footprint ( FD_CONTACT_INFO_TABLE_SIZE ) );
3942
l = FD_LAYOUT_APPEND( l, fd_gui_peers_bandwidth_tracking_align(), fd_gui_peers_bandwidth_tracking_footprint( FD_CONTACT_INFO_TABLE_SIZE ) );
4043
l = FD_LAYOUT_APPEND( l, fd_gui_peers_node_info_pool_align(), fd_gui_peers_node_info_pool_footprint ( FD_CONTACT_INFO_TABLE_SIZE ) );
44+
l = FD_LAYOUT_APPEND( l, fd_gui_peers_node_vote_pool_align(), fd_gui_peers_node_vote_pool_footprint ( FD_RUNTIME_MAX_VOTE_ACCOUNTS ) );
4145
l = FD_LAYOUT_APPEND( l, fd_gui_peers_node_info_map_align(), fd_gui_peers_node_info_map_footprint ( info_chain_cnt ) );
46+
l = FD_LAYOUT_APPEND( l, fd_gui_peers_node_vote_map_align(), fd_gui_peers_node_vote_map_footprint ( vote_chain_cnt ) );
4247
l = FD_LAYOUT_APPEND( l, fd_gui_peers_node_pubkey_map_align(), fd_gui_peers_node_pubkey_map_footprint ( pubkey_chain_cnt ) );
4348
l = FD_LAYOUT_APPEND( l, fd_gui_peers_node_sock_map_align(), fd_gui_peers_node_sock_map_footprint ( sock_chain_cnt ) );
4449
l = FD_LAYOUT_APPEND( l, alignof(fd_gui_peers_ws_conn_t), max_ws_conn_cnt*sizeof(fd_gui_peers_ws_conn_t) );
45-
l = FD_LAYOUT_APPEND( l, alignof(fd_gui_ipinfo_node_t), sizeof(fd_gui_ipinfo_node_t)*IPINFO_MAX_NODES );
50+
l = FD_LAYOUT_APPEND( l, alignof(fd_gui_ipinfo_node_t), sizeof(fd_gui_ipinfo_node_t)*IPINFO_MAX_NODES );
4651

4752
return FD_LAYOUT_FINI( l, fd_gui_peers_align() );
4853
}
@@ -139,6 +144,7 @@ fd_gui_peers_new( void * shmem,
139144
}
140145

141146
ulong info_chain_cnt = fd_gui_peers_node_info_map_chain_cnt_est ( FD_CONTACT_INFO_TABLE_SIZE );
147+
ulong vote_chain_cnt = fd_gui_peers_node_vote_map_chain_cnt_est ( FD_RUNTIME_MAX_VOTE_ACCOUNTS );
142148
ulong pubkey_chain_cnt = fd_gui_peers_node_pubkey_map_chain_cnt_est( FD_CONTACT_INFO_TABLE_SIZE );
143149
ulong sock_chain_cnt = fd_gui_peers_node_sock_map_chain_cnt_est ( FD_CONTACT_INFO_TABLE_SIZE );
144150

@@ -147,7 +153,9 @@ fd_gui_peers_new( void * shmem,
147153
void * _live_table = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_live_table_align(), fd_gui_peers_live_table_footprint ( FD_CONTACT_INFO_TABLE_SIZE ) );
148154
void * _bw_tracking = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_bandwidth_tracking_align(), fd_gui_peers_bandwidth_tracking_footprint( FD_CONTACT_INFO_TABLE_SIZE ) );
149155
void * _info_pool = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_node_info_pool_align(), fd_gui_peers_node_info_pool_footprint ( FD_CONTACT_INFO_TABLE_SIZE ) );
156+
void * _vote_pool = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_node_vote_pool_align(), fd_gui_peers_node_vote_pool_footprint ( FD_RUNTIME_MAX_VOTE_ACCOUNTS ) );
150157
void * _info_map = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_node_info_map_align(), fd_gui_peers_node_info_map_footprint ( info_chain_cnt ) );
158+
void * _vote_map = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_node_vote_map_align(), fd_gui_peers_node_vote_map_footprint ( vote_chain_cnt ) );
151159
void * _pubkey_map = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_node_pubkey_map_align(), fd_gui_peers_node_pubkey_map_footprint ( pubkey_chain_cnt ) );
152160
void * _sock_map = FD_SCRATCH_ALLOC_APPEND( l, fd_gui_peers_node_sock_map_align(), fd_gui_peers_node_sock_map_footprint ( sock_chain_cnt ) );
153161
ctx->client_viewports = FD_SCRATCH_ALLOC_APPEND( l, alignof(fd_gui_peers_ws_conn_t), max_ws_conn_cnt*sizeof(fd_gui_peers_ws_conn_t) );
@@ -178,7 +186,9 @@ fd_gui_peers_new( void * shmem,
178186
fd_gui_peers_bandwidth_tracking_seed( ctx->contact_info_table, FD_CONTACT_INFO_TABLE_SIZE, 42UL );
179187

180188
ctx->node_info_pool = fd_gui_peers_node_info_pool_join ( fd_gui_peers_node_info_pool_new ( _info_pool, FD_CONTACT_INFO_TABLE_SIZE ) );
189+
ctx->node_vote_pool = fd_gui_peers_node_vote_pool_join ( fd_gui_peers_node_vote_pool_new ( _vote_pool, FD_RUNTIME_MAX_VOTE_ACCOUNTS ) );
181190
ctx->node_info_map = fd_gui_peers_node_info_map_join ( fd_gui_peers_node_info_map_new ( _info_map, info_chain_cnt, 42UL ) );
191+
ctx->node_vote_map = fd_gui_peers_node_vote_map_join ( fd_gui_peers_node_vote_map_new ( _vote_map, vote_chain_cnt, 42UL ) );
182192
ctx->node_pubkey_map = fd_gui_peers_node_pubkey_map_join( fd_gui_peers_node_pubkey_map_new( _pubkey_map, pubkey_chain_cnt, 42UL ) );
183193
ctx->node_sock_map = fd_gui_peers_node_sock_map_join ( fd_gui_peers_node_sock_map_new ( _sock_map, sock_chain_cnt, 42UL ) );
184194

@@ -626,8 +636,9 @@ fd_gui_peers_handle_gossip_update( fd_gui_peers_ctx_t * peers,
626636
memset( &peer->gossip_tx, 0, sizeof(peer->gossip_tx) );
627637
memset( &peer->gossvf_rx_sum, 0, sizeof(peer->gossvf_rx_sum) );
628638
memset( &peer->gossip_tx_sum, 0, sizeof(peer->gossip_tx_sum) );
629-
peer->has_vote_info = 0;
630-
peer->stake = ULONG_MAX;
639+
640+
fd_gui_peers_vote_t * vote = fd_gui_peers_node_vote_map_ele_query( peers->node_vote_map, &update->contact_info.contact_info->pubkey, NULL, peers->node_vote_pool );
641+
peer->stake = FD_LIKELY( vote ) ? vote->stake : ULONG_MAX;
631642

632643
fd_gui_config_parse_info_t * info = fd_gui_peers_node_info_map_ele_query( peers->node_info_map, &update->contact_info.contact_info->pubkey, NULL, peers->node_info_pool );
633644
if( FD_LIKELY( info ) ) fd_memcpy( peer->name, info->name, sizeof(info->name) );
@@ -722,9 +733,17 @@ void
722733
fd_gui_peers_handle_vote_update( fd_gui_peers_ctx_t * peers,
723734
fd_gui_peers_vote_t * votes,
724735
ulong vote_cnt,
725-
long now,
726736
fd_pubkey_t * identity ) {
727-
(void)now;
737+
if( FD_UNLIKELY( !fd_gui_peers_node_vote_pool_free( peers->node_vote_pool ) ) ) {
738+
FD_LOG_WARNING(( "pool mem exhausted" ));
739+
return;
740+
}
741+
742+
if( FD_UNLIKELY( vote_cnt>FD_RUNTIME_MAX_VOTE_ACCOUNTS ) ) {
743+
FD_LOG_WARNING(( "max vote accounts exceeded" ));
744+
return;
745+
}
746+
728747
fd_gui_peers_vote_t * votes_sorted = votes;
729748
fd_gui_peers_vote_t * votes_scratch = peers->votes_scratch;
730749
fd_memcpy( votes_sorted, votes, vote_cnt*sizeof(fd_gui_peers_vote_t) );
@@ -785,37 +804,47 @@ fd_gui_peers_handle_vote_update( fd_gui_peers_ctx_t * peers,
785804
fd_http_server_ws_broadcast( peers->http );
786805
}
787806

788-
ulong peer_idx = fd_gui_peers_node_pubkey_map_idx_query( peers->node_pubkey_map, &votes_sorted[ i ].node_account, ULONG_MAX, peers->contact_info_table );
789-
if( FD_UNLIKELY( peer_idx==ULONG_MAX ) ) continue; /* peer not on gossip */
790-
791-
fd_gui_peers_node_t * peer = peers->contact_info_table + peer_idx;
792-
793807
/* TODO: we only publish updates when stake changes, otherwise we'd
794808
have to republish for every peer every slot, which ends up being
795809
too much bandwidth because we republish all the peer info.
796810
Ideally, we decouple the vote updates from the reset of the peer
797811
info which would let us make updates quickly. */
798-
int is_delinquent = ((long)last_vote_slot_p67 - (long)votes_sorted[ i ].last_vote_slot) > 150L;
799-
int vote_eq = peer->has_vote_info
800-
&& !memcmp( peer->vote_account.uc, votes_sorted[ i ].vote_account.uc, sizeof(fd_pubkey_t) )
801-
&& peer->stake ==votes_sorted[ i ].stake
802-
// && peer->last_vote_slot ==votes_sorted[ i ].last_vote_slot
803-
// && peer->last_vote_timestamp ==votes_sorted[ i ].last_vote_timestamp
804-
// && peer->epoch_credits ==votes_sorted[ i ].epoch_credits
805-
&& peer->commission ==votes_sorted[ i ].commission
806-
&& peer->epoch ==votes_sorted[ i ].epoch
807-
&& peer->delinquent ==is_delinquent;
812+
votes_sorted[ i ].delinquent = ((long)last_vote_slot_p67 - (long)votes_sorted[ i ].last_vote_slot) > 150L;
813+
814+
fd_gui_peers_vote_t * vote = fd_gui_peers_node_vote_map_ele_query( peers->node_vote_map, &votes_sorted[ i ].node_account, NULL, peers->node_vote_pool );
815+
816+
int vote_eq = !!vote
817+
&& !memcmp( vote->vote_account.uc, votes_sorted[ i ].vote_account.uc, sizeof(fd_pubkey_t) )
818+
&& vote->stake ==votes_sorted[ i ].stake
819+
// && vote->last_vote_slot ==votes_sorted[ i ].last_vote_slot
820+
// && vote->last_vote_timestamp ==votes_sorted[ i ].last_vote_timestamp
821+
// && vote->epoch_credits ==votes_sorted[ i ].epoch_credits
822+
// && vote->commission ==votes_sorted[ i ].commission
823+
&& vote->epoch ==votes_sorted[ i ].epoch
824+
&& vote->delinquent ==votes_sorted[ i ].delinquent;
808825

809826
if( FD_LIKELY( vote_eq ) ) continue; /* nop */
810827

811-
peer->has_vote_info = 1;
812-
peer->vote_account = votes_sorted[ i ].vote_account;
813-
peer->last_vote_slot = votes_sorted[ i ].last_vote_slot;
814-
peer->last_vote_timestamp = votes_sorted[ i ].last_vote_timestamp;
815-
peer->epoch_credits = votes_sorted[ i ].epoch_credits;
816-
peer->commission = votes_sorted[ i ].commission;
817-
peer->epoch = votes_sorted[ i ].epoch;
818-
peer->delinquent = is_delinquent;
828+
if ( FD_UNLIKELY( !vote ) ) {
829+
vote = fd_gui_peers_node_vote_pool_ele_acquire( peers->node_vote_pool );
830+
fd_memcpy( &vote->node_account, &votes_sorted[ i ].node_account, sizeof(fd_pubkey_t) );
831+
fd_gui_peers_node_vote_map_ele_insert( peers->node_vote_map, vote, peers->node_vote_pool );
832+
}
833+
834+
/* update map entry */
835+
fd_memcpy( vote->vote_account.uc, votes_sorted[ i ].vote_account.uc, sizeof(fd_pubkey_t) );
836+
vote->stake = votes_sorted[ i ].stake;
837+
vote->last_vote_slot = votes_sorted[ i ].last_vote_slot;
838+
vote->last_vote_timestamp = votes_sorted[ i ].last_vote_timestamp;
839+
vote->epoch_credits = votes_sorted[ i ].epoch_credits;
840+
vote->commission = votes_sorted[ i ].commission;
841+
vote->epoch = votes_sorted[ i ].epoch;
842+
vote->delinquent = votes_sorted[ i ].delinquent;
843+
844+
ulong peer_idx = fd_gui_peers_node_pubkey_map_idx_query( peers->node_pubkey_map, &votes_sorted[ i ].node_account, ULONG_MAX, peers->contact_info_table );
845+
if( FD_UNLIKELY( peer_idx==ULONG_MAX ) ) continue; /* peer not on gossip */
846+
847+
fd_gui_peers_node_t * peer = peers->contact_info_table + peer_idx;
819848

820849
if( FD_UNLIKELY( peer->stake!=votes_sorted[ i ].stake ) ) {
821850
fd_gui_peers_live_table_idx_remove( peers->live_table, peer_idx, peers->contact_info_table );

src/disco/gui/fd_gui_peers.h

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,10 @@ struct fd_gui_peers_vote {
114114
uchar commission;
115115
ulong epoch;
116116
ulong epoch_credits;
117+
int delinquent;
118+
119+
struct { ulong next; } map;
120+
struct { ulong next; } pool;
117121
};
118122

119123
typedef struct fd_gui_peers_vote fd_gui_peers_vote_t;
@@ -129,17 +133,8 @@ struct fd_gui_peers_node {
129133
fd_gui_peers_metric_rate_t gossvf_rx_sum; /* sum of gossvf_rx */
130134
fd_gui_peers_metric_rate_t gossip_tx_sum; /* sum of gossip_tx */
131135

132-
int has_vote_info;
133-
fd_pubkey_t vote_account;
134-
ulong stake; /* if has_vote_info==0 then stake==ULONG_MAX */
135-
ulong last_vote_slot;
136-
long last_vote_timestamp;
137-
uchar commission;
138-
ulong epoch;
139-
ulong epoch_credits;
140-
uchar country_code_idx;
141-
int delinquent;
142-
136+
ulong stake; /* default ULONG_MAX */
137+
uchar country_code_idx;
143138
struct {
144139
ulong next;
145140
ulong prev;
@@ -233,16 +228,29 @@ typedef struct fd_gui_peers_gossip_stats fd_gui_peers_gossip_stats_t;
233228
#define POOL_NEXT pool.next
234229
#include "../../util/tmpl/fd_pool.c"
235230

231+
#define POOL_NAME fd_gui_peers_node_vote_pool
232+
#define POOL_T fd_gui_peers_vote_t
233+
#define POOL_NEXT pool.next
234+
#include "../../util/tmpl/fd_pool.c"
235+
236236
#define MAP_NAME fd_gui_peers_node_info_map
237237
#define MAP_ELE_T fd_gui_config_parse_info_t
238238
#define MAP_KEY_T fd_pubkey_t
239239
#define MAP_KEY pubkey
240240
#define MAP_IDX_T ulong
241241
#define MAP_NEXT map.next
242-
#define MAP_PREV map.prev
243242
#define MAP_KEY_HASH(k,s) (fd_hash( (s), (k)->uc, sizeof(fd_pubkey_t) ))
244243
#define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->uc, (k1)->uc, 32UL))
245-
#define MAP_OPTIMIZE_RANDOM_ACCESS_REMOVAL 1
244+
#include "../../util/tmpl/fd_map_chain.c"
245+
246+
#define MAP_NAME fd_gui_peers_node_vote_map
247+
#define MAP_ELE_T fd_gui_peers_vote_t
248+
#define MAP_KEY_T fd_pubkey_t
249+
#define MAP_KEY node_account
250+
#define MAP_IDX_T ulong
251+
#define MAP_NEXT map.next
252+
#define MAP_KEY_HASH(k,s) (fd_hash( (s), (k)->uc, sizeof(fd_pubkey_t) ))
253+
#define MAP_KEY_EQ(k0,k1) (!memcmp((k0)->uc, (k1)->uc, 32UL))
246254
#include "../../util/tmpl/fd_map_chain.c"
247255

248256
#define MAP_NAME fd_gui_peers_node_pubkey_map
@@ -331,7 +339,9 @@ struct fd_gui_peers_ctx {
331339
long next_gossip_stats_update_nanos; /* ns timestamp when we'll next broadcast out gossip stats message */
332340

333341
fd_gui_config_parse_info_t * node_info_pool;
342+
fd_gui_peers_vote_t * node_vote_pool;
334343
fd_gui_peers_node_info_map_t * node_info_map;
344+
fd_gui_peers_node_vote_map_t * node_vote_map;
335345
fd_gui_peers_node_pubkey_map_t * node_pubkey_map;
336346
fd_gui_peers_node_sock_map_t * node_sock_map;
337347
fd_gui_peers_live_table_t * live_table;
@@ -410,7 +420,6 @@ void
410420
fd_gui_peers_handle_vote_update( fd_gui_peers_ctx_t * peers,
411421
fd_gui_peers_vote_t * votes,
412422
ulong vote_cnt,
413-
long now,
414423
fd_pubkey_t * identity );
415424

416425
void

src/disco/gui/fd_gui_printf.c

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1162,21 +1162,22 @@ peers_printf_node( fd_gui_peers_ctx_t * peers,
11621162

11631163
jsonp_close_object( peers->http );
11641164

1165-
if( FD_LIKELY( !peer->has_vote_info ) ) {
1165+
fd_gui_peers_vote_t * vote = fd_gui_peers_node_vote_map_ele_query( peers->node_vote_map, &peer->contact_info.pubkey, NULL, peers->node_vote_pool );
1166+
if( FD_LIKELY( !vote ) ) {
11661167
jsonp_open_array( peers->http, "vote" );
11671168
jsonp_close_array( peers->http );
11681169
} else {
11691170
jsonp_open_array( peers->http, "vote" );
11701171
jsonp_open_object( peers->http, NULL );
11711172
char vote_account_base58[ FD_BASE58_ENCODED_32_SZ ];
1172-
fd_base58_encode_32( peer->vote_account.uc, NULL, vote_account_base58 );
1173+
fd_base58_encode_32( vote->vote_account.uc, NULL, vote_account_base58 );
11731174
jsonp_string( peers->http, "vote_account", vote_account_base58 );
1174-
jsonp_ulong_as_str( peers->http, "activated_stake", peer->stake );
1175-
jsonp_ulong( peers->http, "last_vote", peer->last_vote_slot );
1176-
jsonp_ulong( peers->http, "epoch_credits", peer->epoch_credits );
1177-
jsonp_ulong( peers->http, "commission", peer->commission );
1175+
jsonp_ulong_as_str( peers->http, "activated_stake", vote->stake );
1176+
jsonp_ulong( peers->http, "last_vote", vote->last_vote_slot );
1177+
jsonp_ulong( peers->http, "epoch_credits", vote->epoch_credits );
1178+
jsonp_ulong( peers->http, "commission", vote->commission );
11781179
jsonp_ulong( peers->http, "root_slot", 0UL );
1179-
jsonp_bool( peers->http, "delinquent", peer->delinquent );
1180+
jsonp_bool( peers->http, "delinquent", vote->delinquent );
11801181
jsonp_close_object( peers->http );
11811182
jsonp_close_array( peers->http );
11821183
}

0 commit comments

Comments
 (0)