Line data Source code
1 : #include "fd_tower_serdes.h"
2 : #include "fd_tower.h"
3 :
4 2013 : #define DE( T, name ) do { \
5 2013 : if( FD_UNLIKELY( buf_sz<sizeof(T) ) ) return -1; \
6 2013 : serde->name = *(T const *)fd_type_pun_const( buf ); \
7 1536 : buf += sizeof(T); \
8 1536 : buf_sz -= sizeof(T); \
9 1536 : } 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 : static int
18 483 : de_short_u16( ushort * dst, uchar const ** src, ulong * src_sz ) {
19 483 : uchar const * s = *src;
20 483 : if( FD_UNLIKELY( *src_sz<1 ) ) return -1;
21 474 : if( FD_LIKELY( !( 0x80U & s[0] ) ) ) {
22 468 : *dst = (ushort)s[0];
23 468 : *src += 1;
24 468 : *src_sz -= 1;
25 468 : return 0;
26 468 : }
27 6 : if( FD_UNLIKELY( *src_sz<2 ) ) return -1;
28 3 : if( FD_LIKELY( !( 0x80U & s[1] ) ) ) {
29 0 : if( FD_UNLIKELY( !s[1] ) ) return -1; /* non-canonical: value fits in 1 byte */
30 0 : *dst = (ushort)( (ulong)(s[0]&0x7FUL) + (((ulong)s[1])<<7) );
31 0 : *src += 2;
32 0 : *src_sz -= 2;
33 0 : return 0;
34 0 : }
35 3 : if( FD_UNLIKELY( *src_sz<3 ) ) return -1;
36 3 : if( FD_UNLIKELY( 0x80U & s[2] ) ) return -1; /* 3rd byte is final; continuation bit is invalid */
37 3 : if( FD_UNLIKELY( !s[2] ) ) return -1; /* non-canonical: value fits in 2 bytes */
38 3 : ulong val = (ulong)(s[0]&0x7FUL) + (((ulong)(s[1]&0x7FUL))<<7) + (((ulong)s[2])<<14);
39 3 : if( FD_UNLIKELY( val>USHORT_MAX ) ) return -1;
40 3 : *dst = (ushort)val;
41 3 : *src += 3;
42 3 : *src_sz -= 3;
43 3 : return 0;
44 3 : }
45 :
46 : static int
47 438 : de_var_int( ulong * dst, uchar const ** src, ulong * src_sz ) {
48 438 : *dst = 0;
49 438 : ulong bit = 0;
50 501 : while( FD_LIKELY( bit < 64 ) ) {
51 498 : if( FD_UNLIKELY( !*src_sz ) ) return -1;
52 489 : uchar byte = **src;
53 489 : (*src)++; (*src_sz)--;
54 489 : *dst |= (ulong)(byte & 0x7FUL) << bit;
55 489 : if( FD_LIKELY( (byte & 0x80U) == 0U ) ) {
56 426 : if( FD_UNLIKELY( (*dst>>bit) != byte ) ) return -1;
57 426 : if( FD_UNLIKELY( byte==0U && (bit!=0U || *dst!=0UL) ) ) return -1;
58 423 : return 0;
59 426 : }
60 63 : bit += 7;
61 63 : }
62 3 : return -1;
63 438 : }
64 :
65 : static ulong
66 51 : ser_short_u16( uchar * dst, ushort val ) {
67 51 : if ( FD_LIKELY( val < 0x80U ) ) {
68 51 : dst[0] = (uchar)val;
69 51 : return 1;
70 51 : }
71 0 : else if( FD_LIKELY( val < 0x4000U ) ) {
72 0 : dst[0] = (uchar)((val & 0x7FUL) | 0x80U);
73 0 : dst[1] = (uchar)(val >> 7);
74 0 : return 2;
75 0 : }
76 0 : else {
77 0 : dst[0] = (uchar)((val & 0x7FUL) | 0x80U);
78 0 : dst[1] = (uchar)(((val >> 7) & 0x7FUL) | 0x80U);
79 0 : dst[2] = (uchar)(val >> 14);
80 0 : return 3;
81 0 : }
82 51 : }
83 :
84 : static ulong
85 216 : ser_var_int( uchar * dst, ulong val ) {
86 216 : ulong off = 0;
87 243 : while( FD_LIKELY( val >= 0x80UL ) ) {
88 27 : dst[off] = (uchar)((val & 0x7FUL) | 0x80U);
89 27 : val >>= 7;
90 27 : off += 1;
91 27 : }
92 216 : dst[off] = (uchar)val;
93 216 : return off + 1;
94 216 : }
95 :
96 : int
97 : fd_compact_tower_sync_de( fd_compact_tower_sync_serde_t * serde,
98 : uchar const * buf,
99 537 : ulong buf_sz ) {
100 537 : DE( ulong, root );
101 483 : if( FD_UNLIKELY( de_short_u16( &serde->lockouts_cnt, &buf, &buf_sz ) ) ) return -1;
102 471 : if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1;
103 885 : for( ulong i = 0; i < serde->lockouts_cnt; i++ ) {
104 438 : if( FD_UNLIKELY( de_var_int( &serde->lockouts[i].offset, &buf, &buf_sz ) ) ) return -1;
105 423 : DE( uchar, lockouts[i].confirmation_count );
106 423 : }
107 447 : DE( fd_hash_t, hash );
108 255 : DE( uchar, timestamp_option );
109 249 : if( FD_UNLIKELY( serde->timestamp_option!=1 && serde->timestamp_option!=0 ) ) return -1;
110 243 : if( FD_LIKELY( serde->timestamp_option ) ) {
111 135 : DE( long, timestamp );
112 135 : }
113 216 : DE( fd_hash_t, block_id );
114 21 : return 0;
115 216 : }
116 :
117 : int
118 : fd_compact_tower_sync_ser( fd_compact_tower_sync_serde_t const * serde,
119 : uchar * buf,
120 : ulong buf_max,
121 51 : ulong * buf_sz ) {
122 51 : ulong off = 0;
123 51 : SER( ulong, root );
124 51 : off += ser_short_u16( buf+off, serde->lockouts_cnt );
125 51 : if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1;
126 267 : for( ulong i = 0; i < serde->lockouts_cnt; i++ ) {
127 216 : off += ser_var_int( buf+off, serde->lockouts[i].offset );
128 216 : SER( uchar, lockouts[i].confirmation_count );
129 216 : }
130 51 : SER( fd_hash_t, hash );
131 51 : SER( uchar, timestamp_option );
132 51 : if( FD_LIKELY( serde->timestamp_option ) ) {
133 15 : SER( long, timestamp );
134 15 : }
135 51 : SER( fd_hash_t, block_id );
136 51 : if( FD_LIKELY( buf_sz ) ) *buf_sz = off;
137 51 : return 0;
138 51 : }
139 :
140 : fd_vote_acc_desc_t *
141 : fd_vote_acc_desc( fd_vote_acc_desc_t * desc,
142 : uchar const * data,
143 156 : ulong data_sz ) {
144 156 : if( FD_UNLIKELY( data_sz < sizeof(uint) ) ) return NULL;
145 156 : uint kind = FD_LOAD( uint, data );
146 156 : fd_vote_acc_t const * voter = fd_type_pun_const( data );
147 156 : void const * vote_cnt_p;
148 156 : ulong vote_stride;
149 156 : switch( kind ) {
150 6 : case FD_VOTE_ACC_V2:
151 6 : vote_cnt_p = &voter->v2.votes_cnt;
152 6 : vote_stride = sizeof(fd_vote_acc_vote_v2_t);
153 6 : break;
154 147 : case FD_VOTE_ACC_V3:
155 147 : vote_cnt_p = &voter->v3.votes_cnt;
156 147 : vote_stride = sizeof(fd_vote_acc_vote_t);
157 147 : break;
158 3 : case FD_VOTE_ACC_V4:
159 3 : if( FD_UNLIKELY( (ulong)&voter->v4.has_bls_pubkey_compressed >= (ulong)data+data_sz ) ) return NULL;
160 3 : vote_cnt_p = voter->v4.bls_pubkey_compressed + (!!voter->v4.has_bls_pubkey_compressed * sizeof(voter->v4.bls_pubkey_compressed));
161 3 : vote_stride = sizeof(fd_vote_acc_vote_t);
162 3 : break;
163 0 : default:
164 0 : return NULL;
165 156 : }
166 156 : if( FD_UNLIKELY( (ulong)vote_cnt_p+sizeof(ulong) > (ulong)data+data_sz ) ) return NULL;
167 156 : ulong vote_cnt = FD_LOAD( ulong, vote_cnt_p );
168 : /* FIXME silent truncation is questionable behavior */
169 156 : vote_cnt = fd_ulong_min( vote_cnt, FD_TOWER_VOTE_MAX );
170 156 : ulong vote_hi_p = (ulong)vote_cnt_p + sizeof(ulong) + vote_cnt*vote_stride;
171 156 : if( FD_UNLIKELY( vote_hi_p > (ulong)data+data_sz ) ) return NULL;
172 : /* Option<ulong> "root_slot" follows vote array */
173 156 : ulong root_slot = ULONG_MAX;
174 156 : if( FD_UNLIKELY( vote_hi_p+1UL > (ulong)data+data_sz ) ) return NULL;
175 156 : int root_opt = FD_LOAD( uchar, (void const *)vote_hi_p );
176 156 : if( root_opt==1 ) {
177 15 : if( FD_UNLIKELY( vote_hi_p+9UL > (ulong)data+data_sz ) ) return NULL;
178 15 : root_slot = FD_LOAD( ulong, (void const *)(vote_hi_p+1UL) );
179 141 : } else if( FD_UNLIKELY( root_opt!=0 ) ) {
180 0 : return NULL;
181 0 : }
182 156 : *desc = (fd_vote_acc_desc_t){
183 156 : .kind = (int)kind,
184 156 : .votes_off = (ushort)( (ulong)vote_cnt_p + sizeof(ulong) - (ulong)data ),
185 156 : .vote_cnt = (uchar)vote_cnt,
186 156 : .vote_stride = (uchar)vote_stride,
187 156 : .root_slot = root_slot
188 156 : };
189 156 : return desc;
190 156 : }
191 :
192 : int
193 : fd_txn_parse_simple_vote( fd_txn_t const * txn,
194 : uchar const * payload,
195 : fd_pubkey_t * opt_identity,
196 : fd_pubkey_t * opt_vote_acct,
197 0 : ulong * opt_vote_slot ) {
198 0 : fd_txn_instr_t const * instr = &txn->instr[ 0 ];
199 0 : ulong required_accts = txn->signature_cnt==1 ? 2UL : 3UL;
200 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;
201 :
202 0 : uchar const * instr_data = payload + instr->data_off;
203 0 : uint kind = fd_uint_load_4_fast( instr_data );
204 : /* Older vote instruction kinds are deprecated / ignored */
205 0 : if( FD_UNLIKELY( kind == FD_VOTE_IX_KIND_TOWER_SYNC || kind == FD_VOTE_IX_KIND_TOWER_SYNC_SWITCH ) ) {
206 0 : fd_compact_tower_sync_serde_t compact_tower_sync_serde[ 1 ];
207 0 : int err = fd_compact_tower_sync_de( compact_tower_sync_serde, instr_data + sizeof(uint), instr->data_sz - sizeof(uint) );
208 0 : if( FD_LIKELY( !err ) ) {
209 0 : if( !!opt_vote_slot ) {
210 0 : *opt_vote_slot = fd_ulong_if( compact_tower_sync_serde->root==ULONG_MAX, 0, compact_tower_sync_serde->root );
211 0 : for( ulong i = 0; i < compact_tower_sync_serde->lockouts_cnt; i++ ) {
212 0 : if( FD_UNLIKELY( __builtin_uaddl_overflow( *opt_vote_slot, compact_tower_sync_serde->lockouts[ i ].offset, opt_vote_slot ) ) ) return 0;
213 0 : }
214 0 : }
215 0 : fd_pubkey_t const * accs = (fd_pubkey_t const *)fd_type_pun_const( payload + txn->acct_addr_off );
216 0 : if( !!opt_vote_acct ) {
217 0 : if( FD_UNLIKELY( txn->signature_cnt==1 ) ) *opt_vote_acct = *(fd_pubkey_t const *)fd_type_pun_const( &accs[ 1 ] ); /* identity and authority same, account idx 1 is the vote account address */
218 0 : else *opt_vote_acct = *(fd_pubkey_t const *)fd_type_pun_const( &accs[ 2 ] ); /* identity and authority diff, account idx 2 is the vote account address */
219 0 : }
220 0 : if( !!opt_identity ) {
221 0 : *opt_identity = *(fd_pubkey_t const *)fd_type_pun_const( &accs[ 0 ] );
222 0 : }
223 0 : return 1;
224 0 : }
225 0 : }
226 0 : return 0;
227 0 : }
228 :
229 : #undef DE
230 : #undef SER
|