Skip to content

Commit 51b41c0

Browse files
jherrera-jumpmmcgee-jump
authored andcommitted
gui: validator names / icons pt2
1 parent e4beb82 commit 51b41c0

File tree

11 files changed

+265
-154
lines changed

11 files changed

+265
-154
lines changed

book/api/websocket.md

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1417,7 +1417,8 @@ identity is no longer in these three data sources, it will be removed.
14171417
"name": "ExampleStake Firedancer 🔥💃",
14181418
"details": "A longer description of the validator, perhaps describing the team behind it or how the node is operated",
14191419
"website": "https://github.com/firedancer-io/firedancer",
1420-
"icon_url": "https://docs.firedancer.io/fire.svg"
1420+
"icon_url": "https://docs.firedancer.io/fire.svg",
1421+
"keybase_username": ""
14211422
}
14221423
}
14231424
],
@@ -1449,29 +1450,30 @@ identity is no longer in these three data sources, it will be removed.
14491450
| delinquent | `boolean` | Whether the vote account is delinquent or not. A vote account is considered delinquent if it has not had a vote land on chain for any of the last 127 (inclusive) confirmed slots, according to this validator. If there have been less than 128 confirmed slots on the chain (it is a new chain), a validator is considered delinquent only if it has not voted yet at all |
14501451

14511452
**`PeerUpdateInfo`**
1452-
| Field | Type | Description |
1453-
|----------|----------|-------------|
1454-
| name | `string\|null` | Self reported name of the validator, could be any string or null if there is no name set |
1455-
| details | `string\|null` | Self reported detailed description of the validator, could be any string or null if there is no details set |
1456-
| website | `string\|null` | Self reported website of the validator, could be any string and need not be a valid URI, or could be null if there is no website set |
1457-
| icon_url | `string\|null` | Self reported URL of the validator icon, could be any string and need not be a valid URI, or could be null if there is no icon URI set |
1453+
| Field | Type | Description |
1454+
|------------------|----------|-------------|
1455+
| name | `string` | Self reported name of the validator, could be any string or empty string if there is no name set |
1456+
| details | `string` | Self reported detailed description of the validator, could be any string or empty string if there is no details set |
1457+
| website | `string` | Self reported website of the validator, could be any string and need not be a valid URI, or could be empty string if there is no website set |
1458+
| icon_url | `string` | Self reported URL of the validator icon, could be any string and need not be a valid URI, or could be empty string if there is no icon URI set |
1459+
| keybase_username | `string` | Self reported keybase username of the validator, could be any string or empty string if there is no username set. Keybase is a public, legacy storage for icon images. Although this method for publiscising an icon is deprecated, it is included for completeness as many validators have not migrated to `iconUrl` |
14581460

14591461
**`PeerUpdate`**
1460-
| Field | Type | Description
1461-
|----------|--------|------------
1462-
| identity | `string` | Identity public key of the validator, encoded in base58 |
1463-
| gossip | `PeerUpdateGossip\|null` | Information reported for the validator identity over the gossip network. This is authenticated and the gossip node must have been in possession of the private key to publish gossip data as this identity. Gossip information is not validated or checked for correctness and could be set to any values by the peer |
1462+
| Field | Type | Description |
1463+
|----------|---------------------------|-------------|
1464+
| identity | `string` | Identity public key of the validator, encoded in base58 |
1465+
| gossip | `PeerUpdateGossip\|null` | Information reported for the validator identity over the gossip network. This is authenticated and the gossip node must have been in possession of the private key to publish gossip data as this identity. Gossip information is not validated or checked for correctness and could be set to any values by the peer |
14641466
| vote | `PeerUpdateVoteAccount[]` | Information about the vote account(s) associated with this identity key, if there are any. It is extremely unusual for multiple vote accounts to report the same identity key. Vote account information like stake and commission is derived from the accounts on chain and cannot be corrupt, invalid, or incorrect |
1465-
| info | `PeerUpdateInfo\|null` | If the validator has published self reported identifying information to the chain. This is authenticated and the operator must have been in possession of the private key to publish info as this identity. Information is not validated or checked for correctness and could be set to any values by the peer |
1467+
| info | `PeerUpdateInfo\|null` | If the validator has published self reported identifying information to the chain. This is authenticated and the operator must have been in possession of the private key to publish info as this identity. Information is not validated or checked for correctness and could be set to any values by the peer |
14661468

14671469
**`PeerRemove`**
1468-
| Field | Type | Description |
1469-
|----------|--------|-------------|
1470+
| Field | Type | Description |
1471+
|----------|----------|-------------|
14701472
| identity | `string` | Identity public key of the validator, encoded in base58 |
14711473

14721474
**`PeersUpdate`**
1473-
| Field | Type | Description |
1474-
|--------|--------|-------------|
1475+
| Field | Type | Description |
1476+
|--------|----------------------|-------------|
14751477
| add | `GossipPeerUpdate[]` | List of peer validators that were added since the last update, or all of the peers for the first update after connecting |
14761478
| update | `GossipPeerUpdate[]` | List of peer validators that were changed since the last update |
14771479
| remove | `GossipPeerRemove[]` | List of peer validators that were removed since the last update |

src/app/firedancer/topology.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ fd_topo_initialize( config_t * config ) {
404404
/**/ fd_topob_link( topo, "snapct_repr", "snapct_repr", 128UL, 0UL, 1UL )->permit_no_consumers = 1; /* TODO: wire in repair later */
405405
if( FD_LIKELY( config->tiles.gui.enabled ) ) {
406406
/**/ fd_topob_link( topo, "snapct_gui", "snapct_gui", 128UL, sizeof(fd_snapct_update_t), 1UL );
407-
/**/ fd_topob_link( topo, "snapin_gui", "snapin_gui", 4UL, FD_GUI_PEERS_CONFIG_KEYS_MAX_SZ+FD_GUI_PEERS_VALIDATOR_INFO_MAX_SZ, 1UL );
407+
/**/ fd_topob_link( topo, "snapin_gui", "snapin_gui", 4UL, FD_GUI_CONFIG_PARSE_CONFIG_KEYS_MAX_SZ+FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_MAX_SZ, 1UL );
408408
}
409409
if( vinyl_enabled ) {
410410
/**/ fd_topob_link( topo, "snapin_wr", "snapin_wr", 4UL, 16UL<<20, 1UL );

src/disco/gui/Local.mk

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
ifdef FD_HAS_INT128
2-
$(call add-hdrs,fd_gui.h fd_gui_printf.h fd_gui_peers.h)
3-
$(call add-objs,fd_gui fd_gui_printf fd_gui_peers fd_gui_tile generated/http_import_dist,fd_disco)
2+
$(call add-hdrs,fd_gui.h fd_gui_printf.h fd_gui_peers.h fd_gui_config_parse.h)
3+
$(call add-objs,fd_gui fd_gui_printf fd_gui_peers fd_gui_config_parse fd_gui_tile generated/http_import_dist,fd_disco)
44
$(OBJDIR)/obj/disco/gui/fd_gui_tile.o: book/public/fire.svg
55
$(call make-unit-test,test_live_table,test_live_table,fd_disco fd_util)
6+
$(call make-fuzz-test,fuzz_config_parser,fuzz_config_parser,fd_disco fd_ballet fd_util)
67
endif
78

89
src/disco/gui/dist_stable_cmp/%.zst: src/disco/gui/dist_stable/%

src/disco/gui/fd_gui.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include "../topo/fd_topo.h"
77

88
#include "../../ballet/txn/fd_txn.h"
9+
#include "../../disco/tiles.h"
910
#include "../../disco/fd_txn_p.h"
1011
#include "../../discof/restore/fd_snapct_tile.h"
1112
#include "../../discof/tower/fd_tower_tile.h"
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#include "fd_gui_config_parse.h"
2+
3+
#include "../../ballet/utf8/fd_utf8.h"
4+
5+
int
6+
fd_gui_config_parse_validator_info_check( uchar const * data,
7+
ulong sz,
8+
cJSON ** out_json,
9+
fd_pubkey_t * out_pubkey ) {
10+
/*
11+
pub struct ConfigKeys {
12+
#[cfg_attr(feature = "serde", serde(with = "short_vec"))]
13+
pub keys: Vec<(Pubkey, bool)>,
14+
}
15+
16+
The memory layout of a ConfigProgram account is a bincode serialized
17+
ConfigKeys followed immediately by a stringified json object
18+
containing the desired info.
19+
20+
The short_vec serialization format is a 1-3 bytes size field (where
21+
the highest bit in a given byte is a continuation bit) followed by
22+
serialized elements in the vector (in this case, each element is a
23+
32byte pubkey followed by a 1byte bool. For our simple parser, we
24+
only need to consider vectors smaller than 128 elements.
25+
26+
The JSON schema for a validator info object is the following
27+
28+
{
29+
"name": "<validator name>",
30+
"website": "<website url>",
31+
"details": "<validator details>",
32+
"iconUrl": "<icon url>"
33+
}
34+
35+
Since accounts are at most 10MB, we should be safely within cJSON's
36+
allocator limits.
37+
*/
38+
ulong i = 0UL;
39+
40+
#define CHECK( cond ) do { \
41+
if( FD_UNLIKELY( !(cond) ) ) { \
42+
return 0; \
43+
} \
44+
} while( 0 )
45+
46+
/* CHECK that it is safe to read at least n more bytes assuming i is
47+
the current location. n is untrusted and could trigger overflow, so
48+
don't do i+n<=payload_sz */
49+
#define CHECK_LEFT( n ) CHECK( (n)<=(sz-i) )
50+
51+
CHECK_LEFT( 1UL ); uchar ck_sz = data[ i ]; i++;
52+
if( FD_UNLIKELY( ck_sz!=2 ) ) return 0;
53+
54+
struct __attribute__((packed, aligned(1))) config_keys {
55+
fd_pubkey_t pubkey;
56+
uchar is_signer;
57+
};
58+
59+
struct config_keys * data_config_keys = (struct config_keys *)(data + i);
60+
CHECK_LEFT( (sizeof(fd_pubkey_t) + 1UL)*ck_sz ); i += (sizeof(fd_pubkey_t) + 1UL)*ck_sz;
61+
CHECK_LEFT( FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_MAX_SZ );
62+
63+
/* First entry should be Va1idator1nfo111111111111111111111111111111 */
64+
uchar expected[ 32UL ] = { 0x07, 0x51, 0x97, 0x01, 0x74, 0x48, 0xf2, 0xac, 0x5d, 0xc2, 0x3c, 0x9e, 0xbc, 0x7a, 0xc7, 0x8c, 0x0a, 0x27, 0x25, 0x7a, 0xc6, 0x14, 0x45, 0x8d, 0xe0, 0xa4, 0xf1, 0x6f, 0x80, 0x00, 0x00, 0x00 };
65+
if( FD_UNLIKELY( memcmp( data_config_keys[0].pubkey.uc, expected, sizeof(fd_pubkey_t) ) || data_config_keys[0].is_signer ) ) return 0;
66+
67+
CHECK_LEFT( sizeof(ulong) ); ulong json_str_sz = *(ulong *)(data+i); i += sizeof(ulong);
68+
69+
CHECK_LEFT( json_str_sz );
70+
cJSON * json = cJSON_ParseWithLengthOpts( (char *)(data+i), json_str_sz, NULL, 0 );
71+
if( FD_UNLIKELY( !json ) ) return 0;
72+
73+
#undef CHECK
74+
#undef CHECK_LEFT
75+
76+
*out_json = json;
77+
fd_memcpy( out_pubkey->uc, data_config_keys[1].pubkey.uc, sizeof(fd_pubkey_t) );
78+
return 1;
79+
}
80+
81+
void
82+
fd_gui_config_parse_validator_info( cJSON * json, fd_gui_config_parse_info_t * node_info ) {
83+
const cJSON * name = cJSON_GetObjectItemCaseSensitive( json, "name" );
84+
/* cJSON guarantees name->valuestring is NULL terminated */
85+
int missing_name = !cJSON_IsString( name )
86+
|| strlen(name->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ
87+
|| !fd_cstr_printf_check( node_info->name, strlen(name->valuestring)+1UL, NULL, "%s", name->valuestring )
88+
|| !fd_utf8_verify( node_info->name, strlen(node_info->name) );
89+
if( FD_UNLIKELY( missing_name ) ) node_info->name[ 0 ] = '\0';
90+
91+
const cJSON * website = cJSON_GetObjectItemCaseSensitive( json, "website" );
92+
int missing_website = !cJSON_IsString( website )
93+
|| strlen(website->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_WEBSITE_SZ
94+
|| !fd_cstr_printf_check( node_info->website, strlen(website->valuestring)+1UL, NULL, "%s", website->valuestring )
95+
|| !fd_utf8_verify( node_info->website, strlen(node_info->website) );
96+
if( FD_UNLIKELY( missing_website ) ) node_info->website[ 0 ] = '\0';
97+
98+
const cJSON * details = cJSON_GetObjectItemCaseSensitive( json, "details" );
99+
int missing_details = !cJSON_IsString( details )
100+
|| strlen(details->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_DETAILS_SZ
101+
|| !fd_cstr_printf_check( node_info->details, strlen(details->valuestring)+1UL, NULL, "%s", details->valuestring )
102+
|| !fd_utf8_verify( node_info->details, strlen(node_info->details) );
103+
if( FD_UNLIKELY( missing_details ) ) node_info->details[ 0 ] = '\0';
104+
105+
const cJSON * icon_uri = cJSON_GetObjectItemCaseSensitive( json, "iconUrl" );
106+
int missing_icon_uri = !cJSON_IsString( icon_uri )
107+
|| strlen(icon_uri->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_ICON_URI_SZ
108+
|| !fd_cstr_printf_check( node_info->icon_uri, strlen(icon_uri->valuestring)+1UL, NULL, "%s", icon_uri->valuestring )
109+
|| !fd_utf8_verify( node_info->icon_uri, strlen(node_info->icon_uri) );
110+
if( FD_UNLIKELY( missing_icon_uri ) ) node_info->icon_uri[ 0 ] = '\0';
111+
112+
const cJSON * keybase_username = cJSON_GetObjectItemCaseSensitive( json, "keybaseUsername" );
113+
int missing_keybase_username = !cJSON_IsString( keybase_username )
114+
|| strlen(keybase_username->valuestring)>FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_KEYBASE_USERNAME_SZ
115+
|| !fd_cstr_printf_check( node_info->keybase_username, strlen(keybase_username->valuestring)+1UL, NULL, "%s", keybase_username->valuestring )
116+
|| !fd_utf8_verify( node_info->keybase_username, strlen(node_info->keybase_username) );
117+
if( FD_UNLIKELY( missing_keybase_username ) ) node_info->keybase_username[ 0 ] = '\0';
118+
119+
cJSON_Delete( json );
120+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#ifndef HEADER_fd_src_disco_gui_fd_gui_config_parse_h
2+
#define HEADER_fd_src_disco_gui_fd_gui_config_parse_h
3+
4+
#include "../../ballet/json/cJSON.h"
5+
#include "../../flamenco/types/fd_types_custom.h"
6+
#include "../../util/fd_util_base.h"
7+
8+
/* https://github.com/anza-xyz/agave/blob/master/account-decoder/src/validator_info.rs */
9+
#define FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ ( 80UL) /* +1UL for NULL terminator */
10+
#define FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_WEBSITE_SZ ( 80UL)
11+
#define FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_DETAILS_SZ ( 300UL)
12+
#define FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_ICON_URI_SZ ( 80UL)
13+
#define FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_KEYBASE_USERNAME_SZ (80UL)
14+
#define FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_MAX_SZ ( 576UL) /* does not include size of ConfigKeys */
15+
16+
/* The size of a ConfigKeys of length 2, which is the expected length of ValidatorInfo */
17+
#define FD_GUI_CONFIG_PARSE_CONFIG_KEYS_MAX_SZ (1UL + (32UL + 1UL)*2UL)
18+
19+
struct fd_gui_config_parse_info {
20+
fd_pubkey_t pubkey;
21+
char name [ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_NAME_SZ + 1UL ]; /* +1UL for NULL */
22+
char website [ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_WEBSITE_SZ + 1UL ];
23+
char details [ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_DETAILS_SZ + 1UL ];
24+
char icon_uri[ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_ICON_URI_SZ + 1UL ];
25+
char keybase_username[ FD_GUI_CONFIG_PARSE_VALIDATOR_INFO_KEYBASE_USERNAME_SZ + 1UL ];
26+
27+
struct { ulong prev, next; } map;
28+
struct { ulong next; } pool;
29+
};
30+
31+
typedef struct fd_gui_config_parse_info fd_gui_config_parse_info_t;
32+
33+
FD_PROTOTYPES_BEGIN
34+
35+
int
36+
fd_gui_config_parse_validator_info_check( uchar const * data,
37+
ulong sz,
38+
cJSON ** out_json,
39+
fd_pubkey_t * out_pubkey );
40+
41+
void
42+
fd_gui_config_parse_validator_info( cJSON * json, fd_gui_config_parse_info_t * node_info );
43+
44+
FD_PROTOTYPES_END
45+
46+
#endif /* HEADER_fd_src_disco_gui_fd_gui_config_parse_h */

0 commit comments

Comments
 (0)