Line data Source code
1 : #include "fd_tower_serdes.h"
2 : #include "fd_tower.h"
3 :
4 0 : #define DE( T, name ) do { \
5 0 : if( FD_UNLIKELY( buf_sz<sizeof(T) ) ) return -1; \
6 0 : serde->name = FD_LOAD( T, buf ); \
7 0 : buf += sizeof(T); \
8 0 : buf_sz -= sizeof(T); \
9 0 : } while(0)
10 :
11 0 : #define SER( T, name ) do { \
12 0 : if( FD_UNLIKELY( off+sizeof(T)>buf_max ) ) return -1; \
13 0 : FD_STORE( T, buf+off, serde->name ); \
14 0 : off += sizeof(T); \
15 0 : } while(0)
16 :
17 0 : #define SER_SHORT_U16( name ) do { \
18 0 : uchar tmp[3]; \
19 0 : ulong n = ser_short_u16( tmp, serde->name ); \
20 0 : if( FD_UNLIKELY( off+n>buf_max ) ) return -1; \
21 0 : fd_memcpy( buf+off, tmp, n ); \
22 0 : off += n; \
23 0 : } while(0)
24 :
25 0 : #define SER_VAR_INT( name ) do { \
26 0 : uchar tmp[10]; \
27 0 : ulong n = ser_var_int( tmp, serde->name ); \
28 0 : if( FD_UNLIKELY( off+n>buf_max ) ) return -1; \
29 0 : fd_memcpy( buf+off, tmp, n ); \
30 0 : off += n; \
31 0 : } while(0)
32 :
33 : static int
34 0 : de_short_u16( ushort * dst, uchar const ** src, ulong * src_sz ) {
35 0 : uchar const * s = *src;
36 0 : if( FD_UNLIKELY( *src_sz<1 ) ) return -1;
37 0 : if( FD_LIKELY( !( 0x80U & s[0] ) ) ) {
38 0 : *dst = (ushort)s[0];
39 0 : *src += 1;
40 0 : *src_sz -= 1;
41 0 : return 0;
42 0 : }
43 0 : if( FD_UNLIKELY( *src_sz<2 ) ) return -1;
44 0 : if( FD_LIKELY( !( 0x80U & s[1] ) ) ) {
45 0 : if( FD_UNLIKELY( !s[1] ) ) return -1; /* non-canonical: value fits in 1 byte */
46 0 : *dst = (ushort)( (ulong)(s[0]&0x7FUL) + (((ulong)s[1])<<7) );
47 0 : *src += 2;
48 0 : *src_sz -= 2;
49 0 : return 0;
50 0 : }
51 0 : if( FD_UNLIKELY( *src_sz<3 ) ) return -1;
52 0 : if( FD_UNLIKELY( 0x80U & s[2] ) ) return -1; /* 3rd byte is final; continuation bit is invalid */
53 0 : if( FD_UNLIKELY( !s[2] ) ) return -1; /* non-canonical: value fits in 2 bytes */
54 0 : ulong val = (ulong)(s[0]&0x7FUL) + (((ulong)(s[1]&0x7FUL))<<7) + (((ulong)s[2])<<14);
55 0 : if( FD_UNLIKELY( val>USHORT_MAX ) ) return -1;
56 0 : *dst = (ushort)val;
57 0 : *src += 3;
58 0 : *src_sz -= 3;
59 0 : return 0;
60 0 : }
61 :
62 : static int
63 0 : de_var_int( ulong * dst, uchar const ** src, ulong * src_sz ) {
64 0 : *dst = 0;
65 0 : ulong bit = 0;
66 0 : while( FD_LIKELY( bit < 64 ) ) {
67 0 : if( FD_UNLIKELY( !*src_sz ) ) return -1;
68 0 : uchar byte = **src;
69 0 : (*src)++; (*src_sz)--;
70 0 : *dst |= (ulong)(byte & 0x7FUL) << bit;
71 0 : if( FD_LIKELY( (byte & 0x80U) == 0U ) ) {
72 0 : if( FD_UNLIKELY( (*dst>>bit) != byte ) ) return -1;
73 0 : if( FD_UNLIKELY( byte==0U && (bit!=0U || *dst!=0UL) ) ) return -1;
74 0 : return 0;
75 0 : }
76 0 : bit += 7;
77 0 : }
78 0 : return -1;
79 0 : }
80 :
81 : static ulong
82 0 : ser_short_u16( uchar * dst, ushort val ) {
83 0 : if ( FD_LIKELY( val < 0x80U ) ) {
84 0 : dst[0] = (uchar)val;
85 0 : return 1;
86 0 : }
87 0 : else if( FD_LIKELY( val < 0x4000U ) ) {
88 0 : dst[0] = (uchar)((val & 0x7FUL) | 0x80U);
89 0 : dst[1] = (uchar)(val >> 7);
90 0 : return 2;
91 0 : }
92 0 : else {
93 0 : dst[0] = (uchar)((val & 0x7FUL) | 0x80U);
94 0 : dst[1] = (uchar)(((val >> 7) & 0x7FUL) | 0x80U);
95 0 : dst[2] = (uchar)(val >> 14);
96 0 : return 3;
97 0 : }
98 0 : }
99 :
100 : static ulong
101 0 : ser_var_int( uchar * dst, ulong val ) {
102 0 : ulong off = 0;
103 0 : while( FD_LIKELY( val >= 0x80UL ) ) {
104 0 : dst[off] = (uchar)((val & 0x7FUL) | 0x80U);
105 0 : val >>= 7;
106 0 : off += 1;
107 0 : }
108 0 : dst[off] = (uchar)val;
109 0 : return off + 1;
110 0 : }
111 :
112 : int
113 : fd_compact_tower_sync_de( fd_compact_tower_sync_serde_t * serde,
114 : uchar const * buf,
115 0 : ulong buf_sz ) {
116 0 : DE( ulong, root );
117 0 : if( FD_UNLIKELY( de_short_u16( &serde->lockouts_cnt, &buf, &buf_sz ) ) ) return -1;
118 0 : if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1;
119 0 : for( ulong i = 0; i < serde->lockouts_cnt; i++ ) {
120 0 : if( FD_UNLIKELY( de_var_int( &serde->lockouts[i].offset, &buf, &buf_sz ) ) ) return -1;
121 0 : DE( uchar, lockouts[i].confirmation_count );
122 0 : }
123 0 : DE( fd_hash_t, hash );
124 0 : DE( uchar, timestamp_option );
125 0 : if( FD_UNLIKELY( serde->timestamp_option!=1 && serde->timestamp_option!=0 ) ) return -1;
126 0 : if( FD_LIKELY( serde->timestamp_option ) ) {
127 0 : DE( long, timestamp );
128 0 : }
129 0 : DE( fd_hash_t, block_id );
130 0 : return 0;
131 0 : }
132 :
133 : int
134 : fd_compact_tower_sync_ser( fd_compact_tower_sync_serde_t const * serde,
135 : uchar * buf,
136 : ulong buf_max,
137 0 : ulong * buf_sz ) {
138 0 : if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1;
139 0 : ulong off = 0;
140 0 : SER( ulong, root );
141 0 : SER_SHORT_U16( lockouts_cnt );
142 0 : for( ulong i = 0; i < serde->lockouts_cnt; i++ ) {
143 0 : SER_VAR_INT( lockouts[i].offset );
144 0 : SER( uchar, lockouts[i].confirmation_count );
145 0 : }
146 0 : SER( fd_hash_t, hash );
147 0 : SER( uchar, timestamp_option );
148 0 : if( FD_LIKELY( serde->timestamp_option ) ) {
149 0 : SER( long, timestamp );
150 0 : }
151 0 : SER( fd_hash_t, block_id );
152 0 : if( FD_LIKELY( buf_sz ) ) *buf_sz = off;
153 0 : return 0;
154 0 : }
155 :
156 : fd_vote_acc_desc_t *
157 : fd_vote_acc_desc( fd_vote_acc_desc_t * desc,
158 : uchar const * data,
159 0 : ulong data_sz ) {
160 0 : if( FD_UNLIKELY( data_sz < sizeof(uint) ) ) return NULL;
161 0 : uint kind = FD_LOAD( uint, data );
162 0 : fd_vote_acc_t const * voter = fd_type_pun_const( data );
163 0 : void const * vote_cnt_p;
164 0 : ulong vote_stride;
165 0 : switch( kind ) {
166 0 : case FD_VOTE_ACC_V2:
167 0 : vote_cnt_p = &voter->v2.votes_cnt;
168 0 : vote_stride = sizeof(fd_vote_acc_vote_v2_t);
169 0 : break;
170 0 : case FD_VOTE_ACC_V3:
171 0 : vote_cnt_p = &voter->v3.votes_cnt;
172 0 : vote_stride = sizeof(fd_vote_acc_vote_t);
173 0 : break;
174 0 : case FD_VOTE_ACC_V4:
175 0 : if( FD_UNLIKELY( (ulong)&voter->v4.has_bls_pubkey_compressed >= (ulong)data+data_sz ) ) return NULL;
176 0 : vote_cnt_p = voter->v4.bls_pubkey_compressed + (!!voter->v4.has_bls_pubkey_compressed * sizeof(voter->v4.bls_pubkey_compressed));
177 0 : vote_stride = sizeof(fd_vote_acc_vote_t);
178 0 : break;
179 0 : default:
180 0 : return NULL;
181 0 : }
182 0 : if( FD_UNLIKELY( (ulong)vote_cnt_p+sizeof(ulong) > (ulong)data+data_sz ) ) return NULL;
183 0 : ulong vote_cnt = FD_LOAD( ulong, vote_cnt_p );
184 : /* FIXME silent truncation is questionable behavior */
185 0 : vote_cnt = fd_ulong_min( vote_cnt, FD_TOWER_VOTE_MAX );
186 0 : ulong vote_hi_p = (ulong)vote_cnt_p + sizeof(ulong) + vote_cnt*vote_stride;
187 0 : if( FD_UNLIKELY( vote_hi_p > (ulong)data+data_sz ) ) return NULL;
188 : /* Option<ulong> "root_slot" follows vote array */
189 0 : ulong root_slot = ULONG_MAX;
190 0 : if( FD_UNLIKELY( vote_hi_p+1UL > (ulong)data+data_sz ) ) return NULL;
191 0 : int root_opt = FD_LOAD( uchar, (void const *)vote_hi_p );
192 0 : if( root_opt==1 ) {
193 0 : if( FD_UNLIKELY( vote_hi_p+9UL > (ulong)data+data_sz ) ) return NULL;
194 0 : root_slot = FD_LOAD( ulong, (void const *)(vote_hi_p+1UL) );
195 0 : } else if( FD_UNLIKELY( root_opt!=0 ) ) {
196 0 : return NULL;
197 0 : }
198 0 : *desc = (fd_vote_acc_desc_t){
199 0 : .kind = (int)kind,
200 0 : .votes_off = (ushort)( (ulong)vote_cnt_p + sizeof(ulong) - (ulong)data ),
201 0 : .vote_cnt = (uchar)vote_cnt,
202 0 : .vote_stride = (uchar)vote_stride,
203 0 : .root_slot = root_slot
204 0 : };
205 0 : return desc;
206 0 : }
207 :
208 : int
209 : fd_txn_parse_simple_vote( fd_txn_t const * txn,
210 : uchar const * payload,
211 0 : fd_compact_tower_sync_serde_t * opt_tower_sync ) {
212 0 : fd_txn_instr_t const * instr = &txn->instr[ 0 ];
213 0 : ulong required_accts = txn->signature_cnt==1 ? 2UL : 3UL;
214 0 : if( FD_UNLIKELY( !fd_txn_is_simple_vote_transaction( txn, payload ) || instr->data_sz < sizeof(uint) || txn->acct_addr_cnt < required_accts ) ) return 0;
215 :
216 0 : uchar const * instr_data = payload + instr->data_off;
217 0 : uint kind = fd_uint_load_4_fast( instr_data );
218 : /* Older vote instruction kinds are deprecated / ignored */
219 0 : if( FD_UNLIKELY( kind == FD_VOTE_IX_KIND_TOWER_SYNC || kind == FD_VOTE_IX_KIND_TOWER_SYNC_SWITCH ) ) {
220 0 : fd_compact_tower_sync_serde_t compact_tower_sync_serde[ 1 ];
221 0 : int err = fd_compact_tower_sync_de( compact_tower_sync_serde, instr_data + sizeof(uint), instr->data_sz - sizeof(uint) );
222 0 : if( FD_LIKELY( !err ) ) {
223 0 : if( !!opt_tower_sync ) *opt_tower_sync = *compact_tower_sync_serde;
224 0 : return 1;
225 0 : }
226 0 : }
227 0 : return 0;
228 0 : }
229 :
230 : int
231 : fd_compact_tower_sync_to_votes( fd_compact_tower_sync_serde_t const * serde,
232 : fd_tower_vote_t * out,
233 : ulong * out_cnt,
234 0 : ulong * out_root ) {
235 0 : if( FD_UNLIKELY( serde->lockouts_cnt>FD_TOWER_VOTE_MAX ) ) return -1;
236 :
237 0 : fd_tower_vote_t votes[ FD_TOWER_VOTE_MAX ];
238 0 : ulong slot = fd_ulong_if( serde->root==ULONG_MAX, 0UL, serde->root );
239 0 : ulong prev_conf = FD_TOWER_VOTE_MAX+1UL;
240 : /* Only the vote below is checked, Agave pops expired votes from the
241 : top only, so a deeper expired vote is a normal state. */
242 0 : for( ulong i=0UL; i<serde->lockouts_cnt; i++ ) {
243 0 : ulong offset = serde->lockouts[ i ].offset;
244 0 : ulong conf = serde->lockouts[ i ].confirmation_count;
245 0 : int repeats_slot = !offset && (i || serde->root!=ULONG_MAX);
246 0 : if( FD_UNLIKELY( repeats_slot || offset>ULONG_MAX-slot || !conf ||
247 0 : conf>=prev_conf || conf>FD_TOWER_VOTE_MAX ) ) return -1;
248 0 : if( FD_UNLIKELY( i && offset>(1UL<<prev_conf) ) ) return -1;
249 0 : slot += offset;
250 0 : votes[ i ] = (fd_tower_vote_t){ .slot=slot, .conf=conf };
251 0 : prev_conf = conf;
252 0 : }
253 0 : fd_memcpy( out, votes, serde->lockouts_cnt*sizeof(fd_tower_vote_t) );
254 0 : *out_cnt = serde->lockouts_cnt;
255 0 : *out_root = serde->root;
256 0 : return 0;
257 0 : }
258 :
259 : #undef DE
260 : #undef SER
|