Line data Source code
1 : #include "fd_gossip_types.h"
2 : #include "fd_gossip_private.h"
3 : #include "../../util/bits/fd_bits.h"
4 :
5 : #define SER_INIT( payload, payload_sz, offset ) \
6 0 : uchar * _payload = (payload); \
7 0 : ulong const _offset = (offset); \
8 0 : ulong _i = (offset); \
9 0 : ulong const _payload_sz = (payload_sz); \
10 0 : (void) _offset; \
11 0 : (void) _payload_sz;
12 :
13 0 : #define INC( n ) (_i += (ulong)(n))
14 :
15 : #define CUR_OFFSET ((ushort)_i)
16 0 : #define CURSOR (_payload+_i)
17 0 : #define BYTES_CONSUMED (_i-_offset)
18 : #define BYTES_REMAINING (_payload_sz-_i)
19 :
20 : static inline ushort
21 0 : varint_encode( ulong u64, uchar * out_buf ) {
22 0 : ushort i = 0UL;
23 0 : do {
24 0 : uchar byte = (uchar)(u64 & 0x7FUL);
25 0 : u64 >>= 7UL;
26 0 : if( u64 ) byte |= 0x80U;
27 0 : FD_STORE( uchar, out_buf+i, byte );
28 0 : i++;
29 0 : } while( u64 );
30 0 : return i;
31 0 : }
32 :
33 : static inline ulong
34 : encode_version( fd_contact_info_t const * contact_info,
35 : uchar * out_buf,
36 : ulong out_buf_sz,
37 0 : ushort start_offset ) {
38 0 : SER_INIT( out_buf, out_buf_sz, start_offset );
39 :
40 0 : INC( varint_encode( contact_info->version.major, CURSOR ) );
41 0 : INC( varint_encode( contact_info->version.minor, CURSOR ) );
42 0 : INC( varint_encode( contact_info->version.patch, CURSOR ) );
43 :
44 0 : FD_STORE( uint, CURSOR, contact_info->version.commit ) ; INC( 4U );
45 :
46 0 : FD_STORE( uint, CURSOR, contact_info->version.feature_set ) ; INC( 4U );
47 :
48 0 : INC( varint_encode( contact_info->version.client, CURSOR ) );
49 :
50 0 : return BYTES_CONSUMED;
51 0 : }
52 :
53 : /* The Gossip encoding of a contact info splits the sockets into
54 : two vectors: socket entries (socket_entry_t) and addrs (uint).
55 : The sockets are ordered by port values, and the port values
56 : are encoded as "offsets" to the previous socket entry's value.
57 : addrs is a list of unique IP addresses, and a socket entry's
58 : addr_index indexes into this list. To illustrate the conversion:
59 :
60 : sockets = [
61 : { IP: 192.1.1.1, Port: 1000 }, # tag gossip
62 : { 192.1.2.1, 2000 }, # tag serve_repair_quic
63 : { 0, 0 }, # NULL socket entry for tag RPC
64 : { 192.1.1.1, 500 } # tag rpc pubsub
65 : ]
66 :
67 : would be transformed to:
68 :
69 : addrs = [
70 : 192.1.1.1,
71 : 192.1.2.1
72 : ]
73 :
74 : socket_entries = [
75 : { port_offset: 500, tag: 3, addr_index: 1 }, # first entry's port_offset is the actual port value
76 : { 500, 0, 0 }, # second entry is relative to the first entry's port value
77 : { 1000, 1, 0 } # third entry is relative to the second entry's port value
78 : # null socket entry is not included
79 : ]
80 : */
81 : struct socket_entry {
82 : ushort port_offset;
83 : uchar tag;
84 : uchar addr_index;
85 : };
86 :
87 : typedef struct socket_entry socket_entry_t;
88 :
89 : struct socket_ctx {
90 : fd_ip4_port_t socket;
91 : uchar socket_tag;
92 : };
93 :
94 : typedef struct socket_ctx socket_ctx_t;
95 :
96 : #define SORT_NAME sort_socket_port
97 0 : #define SORT_KEY_T socket_ctx_t
98 0 : #define SORT_BEFORE( a, b ) ( (a).socket.port<(b).socket.port )
99 :
100 : #include "../../util/tmpl/fd_sort.c"
101 :
102 0 : #define FD_CONTACT_INFO_SOCKET_MAX (FD_CONTACT_INFO_SOCKET_LAST+1UL)
103 :
104 :
105 : static inline int
106 : contact_info_convert_sockets( fd_contact_info_t const * contact_info,
107 : socket_entry_t out_sockets_entries[ static FD_CONTACT_INFO_SOCKET_MAX ],
108 : uchar * out_socket_entries_cnt,
109 : uint out_addrs[ static FD_CONTACT_INFO_SOCKET_MAX ],
110 0 : uchar * out_addrs_cnt ) {
111 0 : if( FD_UNLIKELY( !contact_info || !out_socket_entries_cnt || !out_addrs_cnt ) ) {
112 0 : FD_LOG_WARNING(( "Invalid arguments to fd_contact_info_convert_sockets" ));
113 0 : return -1;
114 0 : }
115 :
116 0 : socket_ctx_t filled_up[ FD_CONTACT_INFO_SOCKET_MAX ];
117 0 : ulong filled_up_cnt = 0UL;
118 0 : for( ulong j=0; j<FD_CONTACT_INFO_SOCKET_MAX; j++ ) {
119 0 : if( contact_info->sockets[j].l!=0 ){
120 0 : filled_up[filled_up_cnt].socket = contact_info->sockets[j];
121 : /* Convert port to host order. Needed for sorting and because port info
122 : is encoded in host order in ContactInfo */
123 0 : filled_up[filled_up_cnt].socket.port = fd_ushort_bswap( filled_up[filled_up_cnt].socket.port );
124 0 : filled_up[filled_up_cnt].socket_tag = (uchar)j;
125 0 : filled_up_cnt++;
126 0 : }
127 0 : }
128 :
129 0 : socket_ctx_t scratch[ FD_CONTACT_INFO_SOCKET_MAX ];
130 0 : socket_ctx_t * sorted = sort_socket_port_stable_fast( filled_up, filled_up_cnt, scratch );
131 :
132 0 : uchar addrs_cnt = 0UL;
133 0 : uchar socket_entries_cnt = 0UL;
134 :
135 : /* fill in first entry */
136 0 : out_addrs[addrs_cnt++] = sorted[0].socket.addr;
137 0 : out_sockets_entries[socket_entries_cnt].port_offset = sorted[0].socket.port;
138 0 : out_sockets_entries[socket_entries_cnt].addr_index = 0U;
139 0 : out_sockets_entries[socket_entries_cnt++].tag = sorted[0].socket_tag;
140 :
141 0 : for( ulong j=1; j<filled_up_cnt; j++ ) {
142 0 : socket_ctx_t const * socket = &sorted[j];
143 :
144 0 : uchar addr_found = 0U;
145 0 : for( ulong k=0UL; k<addrs_cnt; k++ ) {
146 0 : if( out_addrs[k]==socket->socket.addr ) {
147 : /* Already have this address, set index */
148 0 : out_sockets_entries[socket_entries_cnt].addr_index = (uchar)k;
149 0 : addr_found = 1U;
150 0 : break;
151 0 : }
152 0 : }
153 0 : if( !addr_found ) {
154 : /* New address, add it */
155 0 : out_addrs[addrs_cnt++] = socket->socket.addr;
156 0 : out_sockets_entries[socket_entries_cnt].addr_index = (uchar)(addrs_cnt-1);
157 0 : }
158 :
159 0 : out_sockets_entries[socket_entries_cnt].port_offset = (ushort)(socket->socket.port-sorted[j-1].socket.port);
160 0 : out_sockets_entries[socket_entries_cnt++].tag = socket->socket_tag;
161 0 : }
162 :
163 0 : *out_addrs_cnt = addrs_cnt;
164 0 : *out_socket_entries_cnt = socket_entries_cnt;
165 0 : return 0;
166 0 : }
167 :
168 :
169 :
170 :
171 : int
172 : fd_gossip_pull_request_init( uchar * payload,
173 : ulong payload_sz,
174 : ulong num_keys,
175 : ulong num_bits,
176 : ulong mask,
177 : uint mask_bits,
178 : uchar const * contact_info_crds,
179 : ulong contact_info_crds_sz,
180 : ulong ** out_bloom_keys,
181 : ulong ** out_bloom_bits,
182 : ulong ** out_bits_set,
183 0 : ulong * out_payload_sz ) {
184 0 : FD_TEST( payload_sz<=FD_GOSSIP_MTU );
185 0 : SER_INIT( payload, payload_sz, 0U );
186 0 : FD_STORE( uint, CURSOR, FD_GOSSIP_MESSAGE_PULL_REQUEST ); INC( 4U );
187 0 : FD_STORE( ulong, CURSOR, num_keys ); INC( 8U );
188 0 : *out_bloom_keys = (ulong *)( CURSOR ) ; INC( num_keys*8U );
189 :
190 0 : if( FD_LIKELY( !!num_bits ) ) {
191 : /* Bloom bits is a bitvec<u64>, so we need to be careful about converting bloom bits count to vector lengths */
192 0 : ulong bloom_vec_len = (num_bits+63UL)/64UL;
193 0 : FD_STORE( uchar, CURSOR, 1 ) ; INC( 1U ); /* has_bits */
194 0 : FD_STORE( ulong, CURSOR, bloom_vec_len ); INC( 8U );
195 0 : *out_bloom_bits = (ulong *)( CURSOR ) ; INC( bloom_vec_len*8U );
196 0 : } else {
197 0 : FD_STORE( uchar, CURSOR, 0 ); INC( 1U ); /* has_bits */
198 0 : *out_bloom_bits = NULL;
199 0 : }
200 0 : FD_STORE( ulong, CURSOR, num_bits ); INC( 8U );
201 0 : *out_bits_set = (ulong *)(CURSOR) ; INC( 8U );
202 :
203 0 : FD_STORE( ulong, CURSOR, mask ); INC( 8U );
204 0 : FD_STORE( uint, CURSOR, mask_bits ); INC( 4U );
205 :
206 0 : if( FD_UNLIKELY( BYTES_REMAINING<contact_info_crds_sz )) {
207 0 : FD_LOG_WARNING(( "Not enough space in pull request for contact info, check bloom filter params" ));
208 0 : return -1;
209 0 : }
210 0 : fd_memcpy( CURSOR, contact_info_crds, contact_info_crds_sz );
211 0 : INC( contact_info_crds_sz );
212 0 : *out_payload_sz = BYTES_CONSUMED;
213 0 : return 0;
214 0 : }
215 :
216 : int
217 : fd_gossip_contact_info_encode( fd_contact_info_t const * contact_info,
218 : uchar * out_buf,
219 : ulong out_buf_sz,
220 0 : ulong * opt_encoded_sz ) {
221 0 : FD_TEST( out_buf_sz<=FD_GOSSIP_MTU );
222 : /* fd_contact_info_t has a fixed-size array of addresses and sockets, while
223 : the encoded representation is a variable-length array of addrs and
224 : sockets, where sockets are sorted by port offsets and index into addrs
225 : to specify address. */
226 0 : uint addrs[ FD_CONTACT_INFO_SOCKET_MAX ];
227 0 : uchar addrs_cnt;
228 0 : socket_entry_t socket_entries[ FD_CONTACT_INFO_SOCKET_MAX ];
229 0 : uchar socket_entries_cnt;
230 :
231 0 : if( FD_UNLIKELY( contact_info_convert_sockets( contact_info, socket_entries, &socket_entries_cnt, addrs, &addrs_cnt ) ) ) {
232 0 : FD_LOG_ERR(( "Failed to convert contact info sockets, check arguments to fd_contact_info_convert_sockets" ));
233 0 : }
234 :
235 0 : SER_INIT( out_buf, out_buf_sz, 0U );
236 :
237 0 : INC( 64U ); /* Reserve space for signature */
238 :
239 0 : FD_STORE( uint, CURSOR, FD_GOSSIP_VALUE_CONTACT_INFO ) ; INC( 4U );
240 0 : fd_memcpy( CURSOR, contact_info->pubkey.uc, 32UL ) ; INC( 32U );
241 :
242 0 : ulong wallclock = (ulong)FD_NANOSEC_TO_MILLI( contact_info->wallclock_nanos );
243 0 : INC( varint_encode( wallclock, CURSOR ) );
244 :
245 0 : ulong instance_creation_wallclock = (ulong)FD_NANOSEC_TO_MICRO( contact_info->instance_creation_wallclock_nanos );
246 0 : FD_STORE( ulong, CURSOR, instance_creation_wallclock ); INC( 8UL );
247 0 : FD_STORE( ushort, CURSOR, contact_info->shred_version ); INC( 2UL );
248 :
249 0 : INC( encode_version( contact_info, out_buf, out_buf_sz, CUR_OFFSET ) );
250 :
251 : /* Encode addrs and socket entries. Properties exploited:
252 : - length of either array never exceeds FD_GOSSIP_SOCKET_TAG_MAX, which
253 : is 13 < 2^7. This means we can assume the length is always encoded as
254 : a single byte varint */
255 0 : FD_STORE( uchar, CURSOR, addrs_cnt ) ; INC( 1UL );
256 0 : for( ulong j=0UL; j<addrs_cnt; j++ ) {
257 : /* Each address is 8 bytes including discriminant */
258 0 : FD_STORE( uint, CURSOR, 0U ) ; INC( 4UL ); /* Enum discriminant */
259 0 : FD_STORE( uint, CURSOR, addrs[j] ) ; INC( 4UL );
260 0 : }
261 :
262 0 : FD_STORE( uchar, CURSOR, socket_entries_cnt ) ; INC( 1UL );
263 :
264 0 : for( ulong j=0UL; j<socket_entries_cnt; j++ ) {
265 0 : FD_STORE( uchar, CURSOR, socket_entries[j].tag ) ; INC( 1UL );
266 0 : FD_STORE( uchar, CURSOR, socket_entries[j].addr_index ) ; INC( 1UL );
267 :
268 0 : INC( varint_encode( socket_entries[j].port_offset, CURSOR ) );
269 0 : }
270 :
271 : /* No extensions for now, but because of a quirk in short_vec we need to encode
272 : the length (which is 0) */
273 0 : FD_STORE( uchar, CURSOR, 0U ) ; INC( 1UL );
274 :
275 0 : if( opt_encoded_sz ) {
276 0 : *opt_encoded_sz = BYTES_CONSUMED;
277 0 : }
278 0 : return 0;
279 0 : }
280 :
281 : int
282 : fd_gossip_crds_vote_encode( uchar * out_buf,
283 : ulong out_buf_sz,
284 : uchar const * txn,
285 : ulong txn_sz,
286 : uchar const * identity_pubkey,
287 : long now,
288 0 : ulong * opt_encoded_sz ) {
289 0 : SER_INIT( out_buf, out_buf_sz, 0U );
290 0 : INC( 64U ); /* Reserve space for signature */
291 :
292 0 : FD_STORE( uint, CURSOR, FD_GOSSIP_VALUE_VOTE ) ; INC( 4U );
293 0 : FD_STORE( uchar, CURSOR, 0U ) ; INC( 1U ); /* TODO: vote tower index, unused for now */
294 0 : fd_memcpy( CURSOR, identity_pubkey, 32UL ) ; INC( 32U );
295 0 : fd_memcpy( CURSOR, txn, txn_sz ) ; INC( (ushort)txn_sz );
296 0 : FD_STORE( ulong, CURSOR, (ulong)FD_NANOSEC_TO_MILLI( now ) ); INC( 8U );
297 :
298 0 : if( opt_encoded_sz ) {
299 0 : *opt_encoded_sz = BYTES_CONSUMED; /* Return the size of the encoded vote */
300 0 : }
301 0 : return 0;
302 0 : }
|