Line data Source code
1 : #include "fd_txn_build.h"
2 : #include "fd_txn.h"
3 : #include "fd_compact_u16.h"
4 : #include "../../flamenco/runtime/fd_system_ids_pp.h"
5 :
6 : FD_STATIC_ASSERT( FD_TXN_ACCT_CAT_PIN > FD_TXN_ACCT_CAT_ALL, flags );
7 : FD_STATIC_ASSERT( FD_TXN_ACCT_CAT_FEE_PAYER > FD_TXN_ACCT_CAT_ALL, flags );
8 :
9 : /* Implementation */
10 :
11 : fd_txn_builder_t *
12 : fd_txn_builder_new( fd_txn_builder_t * mem,
13 0 : ulong seed ) {
14 :
15 : /* Sanity check */
16 0 : if( FD_UNLIKELY(
17 0 : !mem ||
18 0 : !fd_ulong_is_aligned( (ulong)mem, alignof(fd_txn_builder_t) ) ) ) {
19 0 : return NULL;
20 0 : }
21 :
22 0 : fd_txn_builder_t * builder = mem;
23 0 : memset( builder, 0, sizeof(fd_txn_builder_t) );
24 0 : builder->alut_i = UCHAR_MAX; /* sentinel: no ALUT open */
25 0 : FD_TEST( fd_txn_b_addr_map_new( &builder->map, FD_TXN_B_ADDR_CHAIN_CNT, seed ) );
26 :
27 0 : return builder;
28 0 : }
29 :
30 : void *
31 0 : fd_txn_builder_delete( fd_txn_builder_t * builder ) {
32 0 : FD_TEST( fd_txn_b_addr_map_delete( &builder->map ) );
33 0 : return builder;
34 0 : }
35 :
36 : static ulong
37 : fd_txn_b_acct_acquire( fd_txn_builder_t * builder,
38 0 : fd_acct_addr_t const * acct_addr ) {
39 :
40 0 : ulong idx = fd_txn_b_addr_map_idx_query( &builder->map, acct_addr, ULONG_MAX, builder->acct );
41 0 : if( idx!=ULONG_MAX ) return idx;
42 :
43 0 : if( FD_UNLIKELY( builder->acct_cnt >= FD_TXN_ACCT_ADDR_MAX ) ) return ULONG_MAX;
44 0 : idx = builder->acct_cnt;
45 0 : fd_txn_b_acct_t * acct = &builder->acct[ idx ];
46 0 : acct->key = *acct_addr;
47 0 : builder->acct_map[ idx ] = (uchar)idx;
48 0 : builder->acct_rev[ idx ] = (uchar)idx;
49 0 : builder->acct_cnt++;
50 :
51 0 : fd_txn_b_addr_map_idx_insert( &builder->map, idx, builder->acct );
52 0 : return idx;
53 0 : }
54 :
55 : fd_txn_builder_t *
56 : fd_txn_builder_fee_payer_set( fd_txn_builder_t * builder,
57 0 : void const * fee_payer ) {
58 0 : if( FD_UNLIKELY( builder->fee_payer_set ) ) return NULL;
59 0 : fd_acct_addr_t addr = FD_LOAD( fd_acct_addr_t, fee_payer );
60 0 : ulong acct_idx = fd_txn_b_acct_acquire( builder, &addr );
61 0 : if( FD_UNLIKELY( acct_idx==ULONG_MAX ) ) return NULL;
62 0 : fd_txn_b_acct_t * acct = &builder->acct[ acct_idx ];
63 0 : acct->cat &= (uchar)~FD_TXN_ACCT_CAT_ALL;
64 0 : acct->cat |= FD_TXN_ACCT_CAT_WRITABLE_SIGNER | FD_TXN_ACCT_CAT_FEE_PAYER;
65 0 : builder->fee_payer_acct = (uchar)acct_idx;
66 0 : builder->fee_payer_set = 1;
67 0 : return builder;
68 0 : }
69 :
70 : static uint
71 : fd_txn_b_acct_cat_promote( uint prev_cat,
72 0 : uint req_cat ) {
73 0 : int const req_signer = ( (req_cat & FD_TXN_ACCT_CAT_SIGNER ) == FD_TXN_ACCT_CAT_SIGNER );
74 0 : int const req_writable = ( (req_cat & FD_TXN_ACCT_CAT_WRITABLE) == FD_TXN_ACCT_CAT_WRITABLE );
75 0 : int const is_signer = !!( prev_cat & FD_TXN_ACCT_CAT_SIGNER ) || req_signer;
76 0 : int const is_writable = !!( prev_cat & FD_TXN_ACCT_CAT_WRITABLE ) || req_writable;
77 0 : uint new_raw = is_signer
78 0 : ? ( is_writable ? FD_TXN_ACCT_CAT_WRITABLE_SIGNER : FD_TXN_ACCT_CAT_READONLY_SIGNER )
79 0 : : ( is_writable ? FD_TXN_ACCT_CAT_WRITABLE_NONSIGNER_IMM : FD_TXN_ACCT_CAT_READONLY_NONSIGNER_IMM );
80 :
81 0 : return (prev_cat & ~((uint)FD_TXN_ACCT_CAT_ALL)) | new_raw;
82 0 : }
83 :
84 : fd_txn_builder_t *
85 : fd_txn_builder_instr_open( fd_txn_builder_t * builder,
86 : void const * program_id,
87 : void const * data,
88 0 : ulong data_sz ) {
89 :
90 0 : if( FD_UNLIKELY( builder->alut_set ) ) return NULL;
91 :
92 : /* Program account */
93 :
94 0 : fd_acct_addr_t addr = FD_LOAD( fd_acct_addr_t, program_id );
95 0 : ulong program_acct_idx = fd_txn_b_acct_acquire( builder, &addr );
96 0 : if( FD_UNLIKELY( program_acct_idx==ULONG_MAX ) ) return NULL;
97 0 : fd_txn_b_acct_t * program_acct = &builder->acct[ program_acct_idx ];
98 0 : program_acct->cat = (uchar)fd_txn_b_acct_cat_promote( program_acct->cat, FD_TXN_ACCT_CAT_NONE );
99 0 : program_acct->cat |= FD_TXN_ACCT_CAT_PIN;
100 :
101 : /* Instruction data */
102 :
103 0 : ulong data_off = builder->data_bump_sz;
104 0 : ulong data_end = data_off + data_sz;
105 0 : if( FD_UNLIKELY( data_end>FD_TXN_B_DATA_BUMP_MAX ) ) return NULL;
106 0 : uchar * saved_data = builder->data_bump + data_off;
107 0 : fd_memcpy( saved_data, data, data_sz );
108 0 : builder->data_bump_sz = (ushort)( (ulong)builder->data_bump_sz + data_sz );
109 :
110 : /* Instruction */
111 :
112 0 : if( FD_UNLIKELY( builder->instr_cnt >= FD_TXN_INSTR_MAX ) ) return NULL;
113 0 : fd_txn_b_instr_t * instr = &builder->instr[ builder->instr_cnt++ ];
114 0 : instr->program_id = (uchar)program_acct_idx;
115 0 : instr->instr_acct0 = builder->instr_acct_cnt;
116 0 : instr->instr_acct_cnt = 0;
117 0 : instr->data_off = (ushort)data_off;
118 0 : instr->data_sz = (ushort)data_sz;
119 :
120 0 : return builder;
121 0 : }
122 :
123 : fd_txn_builder_t *
124 : fd_txn_builder_instr_account_push(
125 : fd_txn_builder_t * builder,
126 : void const * acct_addr,
127 : uint acct_cat
128 0 : ) {
129 0 : if( FD_UNLIKELY( builder->instr_cnt==0 ) ) return NULL;
130 0 : fd_txn_b_instr_t * instr = &builder->instr[ builder->instr_cnt-1 ];
131 :
132 : /* Transaction Account */
133 :
134 0 : fd_acct_addr_t addr = FD_LOAD( fd_acct_addr_t, acct_addr );
135 0 : ulong acct_idx = fd_txn_b_acct_acquire( builder, &addr );
136 0 : if( FD_UNLIKELY( acct_idx==ULONG_MAX ) ) return NULL;
137 0 : fd_txn_b_acct_t * acct = &builder->acct[ acct_idx ];
138 :
139 : /* Instruction Account */
140 :
141 0 : if( FD_UNLIKELY( instr->instr_acct_cnt >= FD_TXN_ACCT_ADDR_MAX ) ) return NULL;
142 0 : if( FD_UNLIKELY( builder->instr_acct_cnt >= FD_TXN_ACCT_ADDR_MAX ) ) return NULL;
143 0 : ulong instr_acct_idx = builder->instr_acct_cnt++;
144 0 : ulong exp_instr_acct_idx = (ulong)instr->instr_acct0 + (instr->instr_acct_cnt++);
145 0 : FD_TEST( instr_acct_idx==exp_instr_acct_idx );
146 0 : builder->instr_acct[ instr_acct_idx ] = (uchar)acct_idx;
147 :
148 : /* Promote access category */
149 :
150 0 : acct->cat = (uchar)fd_txn_b_acct_cat_promote( acct->cat, acct_cat );
151 :
152 0 : return builder;
153 0 : }
154 :
155 : void
156 0 : fd_txn_builder_instr_close( fd_txn_builder_t * builder ) {
157 0 : (void)builder;
158 0 : }
159 :
160 : fd_txn_builder_t *
161 : fd_txn_builder_nonce_set( fd_txn_builder_t * builder,
162 : void const * nonce_account,
163 0 : void const * nonce_authority ) {
164 : /* The advance nonce instruction must be the first instruction in the
165 : transaction, so reject if any instruction was already opened. */
166 0 : if( FD_UNLIKELY( builder->instr_cnt!=0 ) ) return NULL;
167 :
168 0 : static fd_acct_addr_t const system_prog_id = { .b = { SYS_PROG_ID } };
169 0 : static fd_acct_addr_t const recent_blockhashes_id = { .b = { SYSVAR_RECENT_BLKHASH_ID } };
170 0 : uchar const data[4] = { 0x04, 0x00, 0x00, 0x00 };
171 0 : if( FD_UNLIKELY( !fd_txn_builder_instr_open(
172 0 : builder, &system_prog_id, data, sizeof(data) ) ) ) return NULL;
173 0 : if( FD_UNLIKELY( !fd_txn_builder_instr_account_push(
174 0 : builder, nonce_account, FD_TXN_ACCT_CAT_WRITABLE ) ) ) return NULL;
175 0 : if( FD_UNLIKELY( !fd_txn_builder_instr_account_push(
176 0 : builder, &recent_blockhashes_id, FD_TXN_ACCT_CAT_NONE ) ) ) return NULL;
177 0 : if( FD_UNLIKELY( !fd_txn_builder_instr_account_push(
178 0 : builder, nonce_authority, FD_TXN_ACCT_CAT_SIGNER ) ) ) return NULL;
179 0 : fd_txn_builder_instr_close( builder );
180 0 : return builder;
181 0 : }
182 :
183 : fd_txn_builder_t *
184 : fd_txn_builder_alut_open( fd_txn_builder_t * builder,
185 0 : void const * alut_addr ) {
186 : /* The builder only serializes a single ALUT, so reject a second open. */
187 0 : if( FD_UNLIKELY( builder->alut_i!=UCHAR_MAX ) ) return NULL;
188 0 : fd_acct_addr_t addr = FD_LOAD( fd_acct_addr_t, alut_addr );
189 0 : ulong alut_i = fd_txn_b_acct_acquire( builder, &addr );
190 0 : if( FD_UNLIKELY( alut_i==ULONG_MAX ) ) return NULL;
191 0 : fd_txn_b_acct_t * alut_acct = &builder->acct[ alut_i ];
192 0 : alut_acct->cat = (uchar)fd_txn_b_acct_cat_promote( alut_acct->cat, FD_TXN_ACCT_CAT_NONE );
193 0 : alut_acct->cat |= FD_TXN_ACCT_CAT_PIN; /* ALUT itself cannot be in ALUT */
194 0 : builder->alut_i = (uchar)alut_i;
195 0 : return builder;
196 0 : }
197 :
198 : void
199 : fd_txn_builder_alut_address_push(
200 : fd_txn_builder_t * builder,
201 : void const * acct_addr,
202 : uint const alut_j
203 0 : ) {
204 0 : uint const alut_i = builder->alut_i;
205 :
206 : /* Requires an opened ALUT. alut_j is serialized as a u8. */
207 0 : if( FD_UNLIKELY( alut_i==UCHAR_MAX ) ) return;
208 0 : if( FD_UNLIKELY( alut_j>UCHAR_MAX ) ) return;
209 :
210 0 : fd_acct_addr_t addr = FD_LOAD( fd_acct_addr_t, acct_addr );
211 0 : ulong const acct_idx = fd_txn_b_addr_map_idx_query(
212 0 : &builder->map, &addr, ULONG_MAX, builder->acct );
213 0 : if( FD_UNLIKELY( acct_idx==ULONG_MAX ) ) return;
214 :
215 : /* Can account be demoted? */
216 :
217 0 : fd_txn_b_acct_t * acct = &builder->acct[ acct_idx ];
218 0 : if( FD_UNLIKELY( acct->cat & FD_TXN_ACCT_CAT_IS_PIN ) ) return;
219 :
220 : /* Already demoted to an ALUT? Ignore subsequent calls. */
221 :
222 0 : if( FD_UNLIKELY( acct->cat & FD_TXN_ACCT_CAT_ALT ) ) return;
223 :
224 : /* Demote account to ALUT */
225 :
226 0 : if( acct->cat & FD_TXN_ACCT_CAT_WRITABLE ) {
227 0 : acct->cat = FD_TXN_ACCT_CAT_WRITABLE_ALT;
228 0 : } else {
229 0 : acct->cat = FD_TXN_ACCT_CAT_READONLY_ALT;
230 0 : }
231 0 : acct->alut_i = (uchar)alut_i;
232 0 : acct->alut_j = alut_j;
233 :
234 0 : builder->alut_set = 1;
235 0 : }
236 :
237 : void
238 0 : fd_txn_builder_alut_close( fd_txn_builder_t * builder ) {
239 0 : (void)builder;
240 0 : }
241 :
242 : static inline uint
243 0 : fd_txn_b_acct_prio( fd_txn_b_acct_t const * acct ) {
244 0 : uint const cat = acct->cat;
245 0 : return (cat & FD_TXN_ACCT_CAT_FEE_PAYER) ? 0U : (uint)( fd_uint_find_lsb( cat & FD_TXN_ACCT_CAT_ALL )+1 );
246 0 : }
247 :
248 0 : #define FD_TXN_B_ACCT_PRIO_CNT 7UL
249 :
250 : /* fd_txn_b_bake prepares a transaction for building. For now, only
251 : sorts accounts. */
252 :
253 : static void
254 : fd_txn_b_bake( fd_txn_builder_t * builder,
255 0 : ulong cnt[ FD_TXN_B_ACCT_PRIO_CNT ] ) {
256 0 : ulong const acct_cnt = builder->acct_cnt;
257 :
258 0 : fd_memset( cnt, 0, FD_TXN_B_ACCT_PRIO_CNT*sizeof(ulong) );
259 0 : for( ulong i=0UL; i<acct_cnt; i++ ) cnt[ fd_txn_b_acct_prio( &builder->acct[ builder->acct_map[ i ] ] ) ]++;
260 :
261 0 : ulong off[ FD_TXN_B_ACCT_PRIO_CNT ];
262 0 : off[ 0 ] = 0UL;
263 0 : for( ulong i=1UL; i<FD_TXN_B_ACCT_PRIO_CNT; i++ ) off[ i ] = off[ i-1UL ] + cnt[ i-1UL ];
264 :
265 0 : uchar scratch[ FD_TXN_ACCT_ADDR_MAX ];
266 0 : for( ulong i=0UL; i<acct_cnt; i++ ) {
267 0 : uchar const acct_i = builder->acct_map[ i ];
268 0 : scratch[ off[ fd_txn_b_acct_prio( &builder->acct[ acct_i ] ) ]++ ] = acct_i;
269 0 : }
270 :
271 0 : fd_memcpy( builder->acct_map, scratch, acct_cnt );
272 0 : for( ulong i=0UL; i<acct_cnt; i++ ) builder->acct_rev[ builder->acct_map[ i ] ] = (uchar)i;
273 0 : }
274 :
275 : static inline uchar *
276 : fd_txn_b_cu16_write( uchar * out,
277 : uchar * end,
278 0 : ulong val ) {
279 0 : if( FD_UNLIKELY( val>USHORT_MAX ) ) return NULL;
280 0 : ulong enc_sz = 1UL + ( val>0x7FUL ) + ( val>0x3FFFUL );
281 0 : if( FD_UNLIKELY( out+enc_sz>end ) ) return NULL;
282 0 : out += fd_cu16_enc( (ushort)val, out );
283 0 : return out;
284 0 : }
285 :
286 : __attribute__((always_inline)) static inline uint
287 : fd_txn_build_core( fd_txn_builder_t * builder,
288 0 : uchar out_[ FD_TXN_MTU ] ) {
289 0 : #define ACCT( i ) (&builder->acct[ builder->acct_map[ i ] ])
290 :
291 0 : ulong cnt[ FD_TXN_B_ACCT_PRIO_CNT ];
292 0 : fd_txn_b_bake( builder, cnt );
293 :
294 0 : uchar * out = out_;
295 0 : uchar * const end = out + FD_TXN_MTU;
296 :
297 0 : ulong const rw_sig_cnt = cnt[ 0 ] + cnt[ 1 ];
298 0 : ulong const ro_sig_cnt = cnt[ 2 ];
299 0 : ulong const rw_uns_cnt = cnt[ 3 ];
300 0 : ulong const ro_uns_cnt = cnt[ 4 ];
301 0 : ulong const rw_alt_cnt = cnt[ 5 ];
302 0 : ulong const ro_alt_cnt = cnt[ 6 ];
303 :
304 0 : ulong const sig_cnt = rw_sig_cnt + ro_sig_cnt;
305 0 : ulong const imm_cnt = sig_cnt + rw_uns_cnt + ro_uns_cnt;
306 0 : ulong const alt_cnt = rw_alt_cnt + ro_alt_cnt;
307 :
308 0 : if( FD_UNLIKELY( sig_cnt > FD_TXN_SIG_MAX ) ) return 0;
309 0 : if( FD_UNLIKELY( !builder->fee_payer_set ) ) return 0;
310 0 : if( FD_UNLIKELY( builder->acct_rev[ builder->fee_payer_acct ]!=0U ) ) return 0;
311 :
312 0 : out[0] = (uchar)sig_cnt;
313 0 : out++;
314 0 : fd_memset( out, 0, sig_cnt*FD_TXN_SIGNATURE_SZ );
315 0 : out += sig_cnt*FD_TXN_SIGNATURE_SZ;
316 0 : if( FD_UNLIKELY( out>end ) ) return 0;
317 :
318 0 : if( alt_cnt ) {
319 0 : if( FD_UNLIKELY( out>=end ) ) return 0;
320 0 : out[0] = 0x80; /* txn v0 */
321 0 : out++;
322 0 : }
323 :
324 0 : if( FD_UNLIKELY( out+3>end ) ) return 0;
325 0 : out[0] = (uchar)sig_cnt;
326 0 : out[1] = (uchar)ro_sig_cnt;
327 0 : out[2] = (uchar)ro_uns_cnt;
328 0 : out += 3;
329 :
330 0 : out = fd_txn_b_cu16_write( out, end, imm_cnt );
331 0 : if( FD_UNLIKELY( !out ) ) return 0;
332 :
333 0 : ulong addr_tbl_sz = imm_cnt*FD_TXN_ACCT_ADDR_SZ;
334 0 : if( FD_UNLIKELY( out+addr_tbl_sz>end ) ) return 0;
335 0 : for( ulong i=0UL; i<imm_cnt; i++ ) {
336 0 : fd_txn_b_acct_t * acct = ACCT( i );
337 0 : fd_memcpy( out, &acct->key, FD_TXN_ACCT_ADDR_SZ );
338 0 : out += FD_TXN_ACCT_ADDR_SZ;
339 0 : }
340 :
341 0 : if( FD_UNLIKELY( out+FD_TXN_ACCT_ADDR_SZ>end ) ) return 0;
342 0 : fd_memcpy( out, &builder->recent_blockhash, FD_TXN_ACCT_ADDR_SZ );
343 0 : out += FD_TXN_ACCT_ADDR_SZ;
344 :
345 0 : if( FD_UNLIKELY( out>=end ) ) return 0;
346 0 : ulong const instr_cnt = builder->instr_cnt;
347 0 : out = fd_txn_b_cu16_write( out, end, instr_cnt );
348 0 : if( FD_UNLIKELY( !out ) ) return 0;
349 :
350 0 : for( ulong j=0UL; j<instr_cnt; j++ ) {
351 0 : fd_txn_b_instr_t * instr = &builder->instr[ j ];
352 0 : ulong const instr_acct_cnt = instr->instr_acct_cnt;
353 :
354 0 : if( FD_UNLIKELY( out+1>end ) ) return 0;
355 0 : out[0] = builder->acct_rev[ instr->program_id ];
356 0 : out++;
357 :
358 0 : out = fd_txn_b_cu16_write( out, end, instr_acct_cnt );
359 0 : if( FD_UNLIKELY( !out ) ) return 0;
360 :
361 0 : if( FD_UNLIKELY( out+instr_acct_cnt>end ) ) return 0;
362 0 : for( ulong k=0UL; k<instr_acct_cnt; k++ ) {
363 0 : ulong const acct_idx = builder->acct_rev[ builder->instr_acct[ instr->instr_acct0 + k ] ];
364 0 : out[0] = (uchar)acct_idx;
365 0 : out++;
366 0 : }
367 :
368 0 : ulong const data_sz = instr->data_sz;
369 0 : out = fd_txn_b_cu16_write( out, end, data_sz );
370 0 : if( FD_UNLIKELY( !out ) ) return 0;
371 :
372 0 : if( FD_UNLIKELY( out+data_sz>end ) ) return 0;
373 0 : fd_memcpy( out, &builder->data_bump[ instr->data_off ], data_sz );
374 0 : out += data_sz;
375 0 : }
376 :
377 0 : if( alt_cnt ) {
378 0 : out = fd_txn_b_cu16_write( out, end, 1UL );
379 0 : if( FD_UNLIKELY( !out ) ) return 0;
380 :
381 0 : fd_txn_b_acct_t * alut_acct = &builder->acct[ builder->alut_i ];
382 0 : if( FD_UNLIKELY( out+FD_TXN_ACCT_ADDR_SZ>end ) ) return 0;
383 0 : fd_memcpy( out, &alut_acct->key, FD_TXN_ACCT_ADDR_SZ );
384 0 : out += FD_TXN_ACCT_ADDR_SZ;
385 :
386 0 : out = fd_txn_b_cu16_write( out, end, rw_alt_cnt );
387 0 : if( FD_UNLIKELY( !out ) ) return 0;
388 0 : if( FD_UNLIKELY( out+rw_alt_cnt>end ) ) return 0;
389 0 : for( ulong j=imm_cnt; j<imm_cnt+rw_alt_cnt; j++ ) {
390 0 : if( FD_UNLIKELY( ACCT( j )->alut_j>255U ) ) return 0;
391 0 : out[0] = (uchar)ACCT( j )->alut_j;
392 0 : out++;
393 0 : }
394 :
395 0 : out = fd_txn_b_cu16_write( out, end, ro_alt_cnt );
396 0 : if( FD_UNLIKELY( !out ) ) return 0;
397 0 : if( FD_UNLIKELY( out+ro_alt_cnt>end ) ) return 0;
398 0 : for( ulong j=imm_cnt+rw_alt_cnt; j<imm_cnt+alt_cnt; j++ ) {
399 0 : if( FD_UNLIKELY( ACCT( j )->alut_j>255U ) ) return 0;
400 0 : out[0] = (uchar)ACCT( j )->alut_j;
401 0 : out++;
402 0 : }
403 0 : }
404 :
405 0 : #undef ACCT
406 0 : return (uint)( out - out_ );
407 0 : }
408 :
409 : uint
410 : fd_txn_build_raw( fd_txn_builder_t * builder,
411 0 : uchar out[ FD_TXN_MTU ] ) {
412 0 : return fd_txn_build_core( builder, out );
413 0 : }
414 :
415 : uint
416 : fd_txn_build( fd_txn_builder_t * builder,
417 : uchar out[ FD_TXN_MTU ],
418 : fd_txn_t * restrict out_txn,
419 0 : ushort * opt_out_txn_t_sz ) {
420 0 : uint payload_sz = fd_txn_build_core( builder, out );
421 0 : if( FD_UNLIKELY( !payload_sz ) ) return 0U;
422 :
423 0 : ulong txn_t_sz = fd_txn_parse( out, payload_sz, out_txn, NULL );
424 0 : if( FD_UNLIKELY( !txn_t_sz ) ) return 0U;
425 0 : if( opt_out_txn_t_sz ) *opt_out_txn_t_sz = (ushort)txn_t_sz;
426 0 : return payload_sz;
427 0 : }
|