Line data Source code
1 : #include "fd_tower_serdes.h" 2 : #include "fd_tower.h" 3 : 4 : #define SHORTVEC 0 5 : 6 2259 : #define DE( T, name ) do { \ 7 2259 : if( FD_UNLIKELY( buf_sz<sizeof(T) ) ) return -1; \ 8 2259 : serde->name = *(T const *)fd_type_pun_const( buf ); \ 9 1782 : buf += sizeof(T); \ 10 1782 : buf_sz -= sizeof(T); \ 11 1782 : } while(0) 12 : 13 681 : #define SER( T, name ) do { \ 14 681 : if( FD_UNLIKELY( off+sizeof(T)>buf_max ) ) return -1; \ 15 681 : FD_STORE( T, buf+off, serde->name ); \ 16 681 : off += sizeof(T); \ 17 681 : } while(0) 18 : 19 : static int 20 507 : de_short_u16( ushort * dst, uchar const ** src, ulong * src_sz ) { 21 507 : uchar const * s = *src; 22 507 : if( FD_UNLIKELY( *src_sz<1 ) ) return -1; 23 498 : if( FD_LIKELY( !( 0x80U & s[0] ) ) ) { 24 492 : *dst = (ushort)s[0]; 25 492 : *src += 1; 26 492 : *src_sz -= 1; 27 492 : return 0; 28 492 : } 29 6 : if( FD_UNLIKELY( *src_sz<2 ) ) return -1; 30 3 : if( FD_LIKELY( !( 0x80U & s[1] ) ) ) { 31 0 : if( FD_UNLIKELY( !s[1] ) ) return -1; /* non-canonical: value fits in 1 byte */ 32 0 : *dst = (ushort)( (ulong)(s[0]&0x7FUL) + (((ulong)s[1])<<7) ); 33 0 : *src += 2; 34 0 : *src_sz -= 2; 35 0 : return 0; 36 0 : } 37 3 : if( FD_UNLIKELY( *src_sz<3 ) ) return -1; 38 3 : if( FD_UNLIKELY( 0x80U & s[2] ) ) return -1; /* 3rd byte is final; continuation bit is invalid */ 39 3 : if( FD_UNLIKELY( !s[2] ) ) return -1; /* non-canonical: value fits in 2 bytes */ 40 3 : ulong val = (ulong)(s[0]&0x7FUL) + (((ulong)(s[1]&0x7FUL))<<7) + (((ulong)s[2])<<14); 41 3 : if( FD_UNLIKELY( val>USHORT_MAX ) ) return -1; 42 3 : *dst = (ushort)val; 43 3 : *src += 3; 44 3 : *src_sz -= 3; 45 3 : return 0; 46 3 : } 47 : 48 : static int 49 564 : de_var_int( ulong * dst, uchar const ** src, ulong * src_sz ) { 50 564 : *dst = 0; 51 564 : ulong bit = 0; 52 627 : while( FD_LIKELY( bit < 64 ) ) { 53 624 : if( FD_UNLIKELY( !*src_sz ) ) return -1; 54 615 : uchar byte = **src; 55 615 : (*src)++; (*src_sz)--; 56 615 : *dst |= (ulong)(byte & 0x7FUL) << bit; 57 615 : if( FD_LIKELY( (byte & 0x80U) == 0U ) ) { 58 552 : if( FD_UNLIKELY( (*dst>>bit) != byte ) ) return -1; 59 552 : if( FD_UNLIKELY( byte==0U && (bit!=0U || *dst!=0UL) ) ) return -1; 60 549 : return 0; 61 552 : } 62 63 : bit += 7; 63 63 : } 64 3 : return -1; 65 564 : } 66 : 67 : static ulong 68 75 : ser_short_u16( uchar * dst, ushort val ) { 69 75 : if ( FD_LIKELY( val < 0x80U ) ) { 70 75 : dst[0] = (uchar)val; 71 75 : return 1; 72 75 : } 73 0 : else if( FD_LIKELY( val < 0x4000U ) ) { 74 0 : dst[0] = (uchar)((val & 0x7FUL) | 0x80U); 75 0 : dst[1] = (uchar)(val >> 7); 76 0 : return 2; 77 0 : } 78 0 : else { 79 0 : dst[0] = (uchar)((val & 0x7FUL) | 0x80U); 80 0 : dst[1] = (uchar)(((val >> 7) & 0x7FUL) | 0x80U); 81 0 : dst[2] = (uchar)(val >> 14); 82 0 : return 3; 83 0 : } 84 75 : } 85 : 86 : static ulong 87 342 : ser_var_int( uchar * dst, ulong val ) { 88 342 : ulong off = 0; 89 369 : while( FD_LIKELY( val >= 0x80UL ) ) { 90 27 : dst[off] = (uchar)((val & 0x7FUL) | 0x80U); 91 27 : val >>= 7; 92 27 : off += 1; 93 27 : } 94 342 : dst[off] = (uchar)val; 95 342 : return off + 1; 96 342 : } 97 : 98 : int 99 : fd_compact_tower_sync_de( fd_compact_tower_sync_serde_t * serde, 100 : uchar const * buf, 101 561 : ulong buf_sz ) { 102 561 : DE( ulong, root ); 103 507 : if( FD_UNLIKELY( de_short_u16( &serde->lockouts_cnt, &buf, &buf_sz ) ) ) return -1; 104 495 : if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1; 105 1035 : for( ulong i = 0; i < serde->lockouts_cnt; i++ ) { 106 564 : if( FD_UNLIKELY( de_var_int( &serde->lockouts[i].offset, &buf, &buf_sz ) ) ) return -1; 107 549 : DE( uchar, lockouts[i].confirmation_count ); 108 549 : } 109 471 : DE( fd_hash_t, hash ); 110 279 : DE( uchar, timestamp_option ); 111 273 : if( FD_UNLIKELY( serde->timestamp_option!=1 && serde->timestamp_option!=0 ) ) return -1; 112 267 : if( FD_LIKELY( serde->timestamp_option ) ) { 113 159 : DE( long, timestamp ); 114 159 : } 115 240 : DE( fd_hash_t, block_id ); 116 45 : return 0; 117 240 : } 118 : 119 : int 120 : fd_compact_tower_sync_ser( fd_compact_tower_sync_serde_t const * serde, 121 : uchar * buf, 122 : ulong buf_max, 123 75 : ulong * buf_sz ) { 124 75 : ulong off = 0; 125 75 : SER( ulong, root ); 126 75 : off += ser_short_u16( buf+off, serde->lockouts_cnt ); 127 75 : if( FD_UNLIKELY( serde->lockouts_cnt > FD_TOWER_VOTE_MAX ) ) return -1; 128 417 : for( ulong i = 0; i < serde->lockouts_cnt; i++ ) { 129 342 : off += ser_var_int( buf+off, serde->lockouts[i].offset ); 130 342 : SER( uchar, lockouts[i].confirmation_count ); 131 342 : } 132 75 : SER( fd_hash_t, hash ); 133 75 : SER( uchar, timestamp_option ); 134 75 : if( FD_LIKELY( serde->timestamp_option ) ) { 135 39 : SER( long, timestamp ); 136 39 : } 137 75 : SER( fd_hash_t, block_id ); 138 75 : if( FD_LIKELY( buf_sz ) ) *buf_sz = off; 139 75 : return 0; 140 75 : } 141 : 142 : static fd_vote_acc_vote_t const * 143 9 : v4_off( fd_vote_acc_t const * voter ) { 144 9 : return (fd_vote_acc_vote_t const *)( voter->v4.bls_pubkey_compressed + voter->v4.has_bls_pubkey_compressed * sizeof(voter->v4.bls_pubkey_compressed) + sizeof(ulong) ); 145 9 : } 146 : 147 : ulong 148 726 : fd_vote_acc_vote_cnt( uchar const * vote_account_data ) { 149 726 : fd_vote_acc_t const * voter = (fd_vote_acc_t const *)fd_type_pun_const( vote_account_data ); 150 726 : switch( voter->kind ) { 151 6 : case FD_VOTE_ACC_V4: return fd_ulong_load_8( voter->v4.bls_pubkey_compressed + voter->v4.has_bls_pubkey_compressed * sizeof(voter->v4.bls_pubkey_compressed) ); 152 612 : case FD_VOTE_ACC_V3: return voter->v3.votes_cnt; 153 108 : case FD_VOTE_ACC_V2: return voter->v2.votes_cnt; 154 0 : default: FD_LOG_HEXDUMP_CRIT(( "bad voter", vote_account_data, 3762 )); 155 726 : } 156 726 : } 157 : 158 : /* fd_vote_acc_vote_slot takes a voter's vote account data and returns the 159 : voter's most recent vote slot in the tower. Returns ULONG_MAX if 160 : they have an empty tower. */ 161 : 162 : ulong 163 15 : fd_vote_acc_vote_slot( uchar const * vote_account_data ) { 164 15 : fd_vote_acc_t const * voter = (fd_vote_acc_t const *)fd_type_pun_const( vote_account_data ); 165 15 : ulong cnt = fd_vote_acc_vote_cnt( vote_account_data ); 166 15 : switch( voter->kind ) { 167 3 : case FD_VOTE_ACC_V4: return cnt ? v4_off( voter )[cnt-1].slot : ULONG_MAX; 168 9 : case FD_VOTE_ACC_V3: return cnt ? voter->v3.votes[cnt-1].slot : ULONG_MAX; 169 3 : case FD_VOTE_ACC_V2: return cnt ? voter->v2.votes[cnt-1].slot : ULONG_MAX; 170 0 : default: FD_LOG_HEXDUMP_CRIT(( "bad voter", vote_account_data, 3762 )); 171 15 : } 172 15 : } 173 : 174 : /* fd_vote_acc_root_slot takes a voter's vote account data and returns the 175 : voter's root slot. Returns ULONG_MAX if they don't have a root. */ 176 : 177 : ulong 178 147 : fd_vote_acc_root_slot( uchar const * vote_account_data ) { 179 147 : fd_vote_acc_t const * voter = (fd_vote_acc_t const *)fd_type_pun_const( vote_account_data ); 180 147 : ulong cnt = fd_vote_acc_vote_cnt( vote_account_data ); 181 147 : switch( voter->kind ) { 182 3 : case FD_VOTE_ACC_V4: { uchar root_option = fd_uchar_load_1_fast( (uchar *)&v4_off( voter )[cnt] ); return root_option ? fd_ulong_load_8_fast( (uchar *)&v4_off( voter )[cnt] + 1UL ) : ULONG_MAX; } 183 138 : case FD_VOTE_ACC_V3: { uchar root_option = fd_uchar_load_1_fast( (uchar *)&voter->v3.votes[cnt] ); return root_option ? fd_ulong_load_8_fast( (uchar *)&voter->v3.votes[cnt] + 1UL ) : ULONG_MAX; } 184 6 : case FD_VOTE_ACC_V2: { uchar root_option = fd_uchar_load_1_fast( (uchar *)&voter->v2.votes[cnt] ); return root_option ? fd_ulong_load_8_fast( (uchar *)&voter->v2.votes[cnt] + 1UL ) : ULONG_MAX; } 185 0 : default: FD_LOG_CRIT(( "unhandled kind %u", voter->kind )); 186 147 : } 187 147 : } 188 : 189 : int 190 : fd_txn_parse_simple_vote( fd_txn_t const * txn, 191 : uchar const * payload, 192 : fd_pubkey_t * opt_identity, 193 : fd_pubkey_t * opt_vote_acct, 194 0 : ulong * opt_vote_slot ) { 195 0 : fd_txn_instr_t const * instr = &txn->instr[ 0 ]; 196 0 : ulong required_accts = txn->signature_cnt==1 ? 2UL : 3UL; 197 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; 198 : 199 0 : uchar const * instr_data = payload + instr->data_off; 200 0 : uint kind = fd_uint_load_4_fast( instr_data ); 201 : /* Older vote instruction kinds are deprecated / ignored */ 202 0 : if( FD_UNLIKELY( kind == FD_VOTE_IX_KIND_TOWER_SYNC || kind == FD_VOTE_IX_KIND_TOWER_SYNC_SWITCH ) ) { 203 0 : fd_compact_tower_sync_serde_t compact_tower_sync_serde[ 1 ]; 204 0 : int err = fd_compact_tower_sync_de( compact_tower_sync_serde, instr_data + sizeof(uint), instr->data_sz - sizeof(uint) ); 205 0 : if( FD_LIKELY( !err ) ) { 206 0 : if( !!opt_vote_slot ) { 207 0 : *opt_vote_slot = compact_tower_sync_serde->root; 208 0 : for( ulong i = 0; i < compact_tower_sync_serde->lockouts_cnt; i++ ) *opt_vote_slot += compact_tower_sync_serde->lockouts[ i ].offset; 209 0 : } 210 0 : fd_pubkey_t const * accs = (fd_pubkey_t const *)fd_type_pun_const( payload + txn->acct_addr_off ); 211 0 : if( !!opt_vote_acct ) { 212 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 */ 213 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 */ 214 0 : } 215 0 : if( !!opt_identity ) { 216 0 : *opt_identity = *(fd_pubkey_t const *)fd_type_pun_const( &accs[ 0 ] ); 217 0 : } 218 0 : return 1; 219 0 : } 220 0 : } 221 0 : return 0; 222 0 : }