Line data Source code
1 : #include "fd_tower_serdes.h"
2 : #include "fd_tower.h"
3 :
4 2205 : #define DE( T, name ) do { \
5 2205 : if( FD_UNLIKELY( buf_sz<sizeof(T) ) ) return -1; \
6 2205 : serde->name = FD_LOAD( T, buf ); \
7 1728 : buf += sizeof(T); \
8 1728 : buf_sz -= sizeof(T); \
9 1728 : } while(0)
10 :
11 435 : #define SER( T, name ) do { \
12 435 : if( FD_UNLIKELY( off+sizeof(T)>buf_max ) ) return -1; \
13 435 : FD_STORE( T, buf+off, serde->name ); \
14 435 : off += sizeof(T); \
15 435 : } while(0)
16 :
17 51 : #define SER_SHORT_U16( name ) do { \
18 51 : uchar tmp[3]; \
19 51 : ulong n = ser_short_u16( tmp, serde->name ); \
20 51 : if( FD_UNLIKELY( off+n>buf_max ) ) return -1; \
21 51 : fd_memcpy( buf+off, tmp, n ); \
22 51 : off += n; \
23 51 : } while(0)
24 :
25 216 : #define SER_VAR_INT( name ) do { \
26 216 : uchar tmp[10]; \
27 216 : ulong n = ser_var_int( tmp, serde->name ); \
28 216 : if( FD_UNLIKELY( off+n>buf_max ) ) return -1; \
29 216 : fd_memcpy( buf+off, tmp, n ); \
30 216 : off += n; \
31 216 : } while(0)
32 :
33 : static int
34 507 : de_short_u16( ushort * dst, uchar const ** src, ulong * src_sz ) {
35 507 : uchar const * s = *src;
36 507 : if( FD_UNLIKELY( *src_sz<1 ) ) return -1;
37 498 : if( FD_LIKELY( !( 0x80U & s[0] ) ) ) {
38 492 : *dst = (ushort)s[0];
39 492 : *src += 1;
40 492 : *src_sz -= 1;
41 492 : return 0;
42 492 : }
43 6 : if( FD_UNLIKELY( *src_sz<2 ) ) return -1;
44 3 : 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 3 : if( FD_UNLIKELY( *src_sz<3 ) ) return -1;
52 3 : if( FD_UNLIKELY( 0x80U & s[2] ) ) return -1; /* 3rd byte is final; continuation bit is invalid */
53 3 : if( FD_UNLIKELY( !s[2] ) ) return -1; /* non-canonical: value fits in 2 bytes */
54 3 : ulong val = (ulong)(s[0]&0x7FUL) + (((ulong)(s[1]&0x7FUL))<<7) + (((ulong)s[2])<<14);
55 3 : if( FD_UNLIKELY( val>USHORT_MAX ) ) return -1;
56 3 : *dst = (ushort)val;
57 3 : *src += 3;
58 3 : *src_sz -= 3;
59 3 : return 0;
60 3 : }
61 :
62 : static int
63 510 : de_var_int( ulong * dst, uchar const ** src, ulong * src_sz ) {
64 510 : *dst = 0;
65 510 : ulong bit = 0;
66 573 : while( FD_LIKELY( bit < 64 ) ) {
67 570 : if( FD_UNLIKELY( !*src_sz ) ) return -1;
68 561 : uchar byte = **src;
69 561 : (*src)++; (*src_sz)--;
70 561 : *dst |= (ulong)(byte & 0x7FUL) << bit;
71 561 : if( FD_LIKELY( (byte & 0x80U) == 0U ) ) {
72 498 : if( FD_UNLIKELY( (*dst>>bit) != byte ) ) return -1;
73 498 : if( FD_UNLIKELY( byte==0U && (bit!=0U || *dst!=0UL) ) ) return -1;
74 495 : return 0;
75 498 : }
76 63 : bit += 7;
77 63 : }
78 3 : return -1;
79 510 : }
80 :
81 : static ulong
82 51 : ser_short_u16( uchar * dst, ushort val ) {
83 51 : if ( FD_LIKELY( val < 0x80U ) ) {
84 51 : dst[0] = (uchar)val;
85 51 : return 1;
86 51 : }
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 51 : }
99 :
100 : static ulong
101 216 : ser_var_int( uchar * dst, ulong val ) {
102 216 : ulong off = 0;
103 243 : while( FD_LIKELY( val >= 0x80UL ) ) {
104 27 : dst[off] = (uchar)((val & 0x7FUL) | 0x80U);
105 27 : val >>= 7;
106 27 : off += 1;
107 27 : }
108 216 : dst[off] = (uchar)val;
109 216 : return off + 1;
110 216 : }
111 :
112 : int
113 : fd_compact_tower_sync_de( fd_compact_tower_sync_serde_t * serde,
114 : uchar const * buf,
115 561 : ulong buf_sz ) {
116 561 : DE( ulong, root );
117 507 : if( FD_UNLIKELY( de_short_u16( &serde->lockouts_cnt, &buf, &buf_sz ) ) ) return -1;
118 495 : if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1;
119 981 : for( ulong i = 0; i < serde->lockouts_cnt; i++ ) {
120 510 : if( FD_UNLIKELY( de_var_int( &serde->lockouts[i].offset, &buf, &buf_sz ) ) ) return -1;
121 495 : DE( uchar, lockouts[i].confirmation_count );
122 495 : }
123 471 : DE( fd_hash_t, hash );
124 279 : DE( uchar, timestamp_option );
125 273 : if( FD_UNLIKELY( serde->timestamp_option!=1 && serde->timestamp_option!=0 ) ) return -1;
126 267 : if( FD_LIKELY( serde->timestamp_option ) ) {
127 159 : DE( long, timestamp );
128 159 : }
129 240 : DE( fd_hash_t, block_id );
130 45 : return 0;
131 240 : }
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 51 : ulong * buf_sz ) {
138 51 : if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1;
139 51 : ulong off = 0;
140 51 : SER( ulong, root );
141 51 : SER_SHORT_U16( lockouts_cnt );
142 267 : for( ulong i = 0; i < serde->lockouts_cnt; i++ ) {
143 216 : SER_VAR_INT( lockouts[i].offset );
144 216 : SER( uchar, lockouts[i].confirmation_count );
145 216 : }
146 51 : SER( fd_hash_t, hash );
147 51 : SER( uchar, timestamp_option );
148 51 : if( FD_LIKELY( serde->timestamp_option ) ) {
149 15 : SER( long, timestamp );
150 15 : }
151 51 : SER( fd_hash_t, block_id );
152 51 : if( FD_LIKELY( buf_sz ) ) *buf_sz = off;
153 51 : return 0;
154 51 : }
155 :
156 : fd_vote_acc_desc_t *
157 : fd_vote_acc_desc( fd_vote_acc_desc_t * desc,
158 : uchar const * data,
159 174 : ulong data_sz ) {
160 174 : if( FD_UNLIKELY( data_sz < sizeof(uint) ) ) return NULL;
161 174 : uint kind = FD_LOAD( uint, data );
162 174 : fd_vote_acc_t const * voter = fd_type_pun_const( data );
163 174 : void const * vote_cnt_p;
164 174 : ulong vote_stride;
165 174 : switch( kind ) {
166 6 : case FD_VOTE_ACC_V2:
167 6 : vote_cnt_p = &voter->v2.votes_cnt;
168 6 : vote_stride = sizeof(fd_vote_acc_vote_v2_t);
169 6 : break;
170 165 : case FD_VOTE_ACC_V3:
171 165 : vote_cnt_p = &voter->v3.votes_cnt;
172 165 : vote_stride = sizeof(fd_vote_acc_vote_t);
173 165 : break;
174 3 : case FD_VOTE_ACC_V4:
175 3 : if( FD_UNLIKELY( (ulong)&voter->v4.has_bls_pubkey_compressed >= (ulong)data+data_sz ) ) return NULL;
176 3 : vote_cnt_p = voter->v4.bls_pubkey_compressed + (!!voter->v4.has_bls_pubkey_compressed * sizeof(voter->v4.bls_pubkey_compressed));
177 3 : vote_stride = sizeof(fd_vote_acc_vote_t);
178 3 : break;
179 0 : default:
180 0 : return NULL;
181 174 : }
182 174 : if( FD_UNLIKELY( (ulong)vote_cnt_p+sizeof(ulong) > (ulong)data+data_sz ) ) return NULL;
183 174 : ulong vote_cnt = FD_LOAD( ulong, vote_cnt_p );
184 : /* FIXME silent truncation is questionable behavior */
185 174 : vote_cnt = fd_ulong_min( vote_cnt, FD_TOWER_VOTE_MAX );
186 174 : ulong vote_hi_p = (ulong)vote_cnt_p + sizeof(ulong) + vote_cnt*vote_stride;
187 174 : if( FD_UNLIKELY( vote_hi_p > (ulong)data+data_sz ) ) return NULL;
188 : /* Option<ulong> "root_slot" follows vote array */
189 174 : ulong root_slot = ULONG_MAX;
190 174 : if( FD_UNLIKELY( vote_hi_p+1UL > (ulong)data+data_sz ) ) return NULL;
191 174 : int root_opt = FD_LOAD( uchar, (void const *)vote_hi_p );
192 174 : if( root_opt==1 ) {
193 15 : if( FD_UNLIKELY( vote_hi_p+9UL > (ulong)data+data_sz ) ) return NULL;
194 15 : root_slot = FD_LOAD( ulong, (void const *)(vote_hi_p+1UL) );
195 159 : } else if( FD_UNLIKELY( root_opt!=0 ) ) {
196 0 : return NULL;
197 0 : }
198 174 : *desc = (fd_vote_acc_desc_t){
199 174 : .kind = (int)kind,
200 174 : .votes_off = (ushort)( (ulong)vote_cnt_p + sizeof(ulong) - (ulong)data ),
201 174 : .vote_cnt = (uchar)vote_cnt,
202 174 : .vote_stride = (uchar)vote_stride,
203 174 : .root_slot = root_slot
204 174 : };
205 174 : return desc;
206 174 : }
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 : #undef DE
231 : #undef SER
|