Line data Source code
1 : #include "bench.h"
2 :
3 : #include "../../../../disco/topo/fd_topo.h"
4 : #include "../../../../flamenco/genesis/fd_genesis_create.h"
5 : #include "../../../../flamenco/runtime/fd_system_ids_pp.h"
6 :
7 : typedef struct {
8 : fd_rng_t rng[ 1 ];
9 : fd_sha512_t sha[ 1 ];
10 :
11 : ulong sender_idx;
12 : ulong lamport_idx;
13 : int changed_blockhash;
14 :
15 : int has_recent_blockhash;
16 : uchar recent_blockhash[ 32 ];
17 : uchar staged_blockhash[ 32 ];
18 :
19 : int transaction_mode;
20 : float contending_fraction;
21 : float cu_price_spread;
22 :
23 : ulong acct_cnt;
24 : fd_pubkey_t * acct_public_keys;
25 : fd_pubkey_t * acct_private_keys;
26 : fd_pubkey_t * token_accounts;
27 :
28 : ulong benchg_cnt;
29 : ulong benchg_idx;
30 :
31 : fd_wksp_t * mem;
32 : ulong out_chunk0;
33 : ulong out_wmark;
34 : ulong out_chunk;
35 : } fd_benchg_ctx_t;
36 :
37 : FD_STATIC_ASSERT( FD_GENESIS_TOKEN_ACCOUNTS_PER_ACCOUNT>=2UL, token_accounts );
38 :
39 : FD_FN_CONST static inline ulong
40 0 : scratch_align( void ) {
41 0 : return alignof( fd_benchg_ctx_t );
42 0 : }
43 :
44 : FD_FN_PURE static inline ulong
45 0 : scratch_footprint( fd_topo_tile_t const * tile ) {
46 0 : (void)tile;
47 0 : ulong l = FD_LAYOUT_INIT;
48 0 : l = FD_LAYOUT_APPEND( l, alignof( fd_benchg_ctx_t ), sizeof( fd_benchg_ctx_t ) );
49 0 : l = FD_LAYOUT_APPEND( l, alignof( fd_pubkey_t ), sizeof( fd_pubkey_t ) * tile->benchg.accounts_cnt );
50 0 : l = FD_LAYOUT_APPEND( l, alignof( fd_pubkey_t ), sizeof( fd_pubkey_t ) * tile->benchg.accounts_cnt );
51 0 : l = FD_LAYOUT_APPEND( l, alignof( fd_pubkey_t ), sizeof( fd_pubkey_t ) * 2UL * tile->benchg.accounts_cnt );
52 0 : return FD_LAYOUT_FINI( l, scratch_align() );
53 0 : }
54 :
55 : typedef struct __attribute__((packed)) {
56 : uchar sig_cnt; /* = 1 */
57 : uchar signature[64];
58 : uchar _sig_cnt; /* also 1 */
59 : uchar ro_signed_cnt; /* ??? */
60 : uchar ro_unsigned_cnt; /* ??? */
61 : uchar acct_addr_cnt; /* = ??? */
62 : uchar fee_payer[32];
63 : } single_signer_hdr_t;
64 :
65 : typedef struct __attribute__((packed)) {
66 : uchar sig_cnt; /* = 1 */
67 : uchar signature[64];
68 : uchar _sig_cnt; /* also 1 */
69 : uchar ro_signed_cnt; /* = 0 */
70 : uchar ro_unsigned_cnt; /* = 1 . Compute Budget Program */
71 : uchar acct_addr_cnt; /* = 2 */
72 : uchar fee_payer[32];
73 : uchar compute_budget_program[32]; /* = {COMPUTE_BUDGET_PROG_ID} */
74 : uchar recent_blockhash[32];
75 : uchar instr_cnt; /* = 2 */
76 : /* Start of instruction */
77 : struct __attribute__((packed)) {
78 : uchar prog_id; /* = 1 */
79 : uchar acct_cnt; /* = 0 */
80 : uchar data_sz; /* = 9 */
81 : uchar set_cu_price; /* = 3 */
82 : ulong micro_lamports_per_cu; /* Preferably less than 10k or so */
83 : } _1;
84 : /* Start of second instruction */
85 : struct __attribute__((packed)) {
86 : uchar prog_id; /* = 1 */
87 : uchar acct_cnt; /* = 0 */
88 : uchar data_sz; /* = 5 */
89 : uchar set_cu_limit; /* = 2 */
90 : uint cus; /* = 300 */
91 : } _2;
92 : } noop_t;
93 :
94 : typedef struct __attribute__((packed)) {
95 : uchar sig_cnt; /* = 1 */
96 : uchar signature[64];
97 : uchar _sig_cnt; /* also 1 */
98 : uchar ro_signed_cnt; /* = 0 */
99 : uchar ro_unsigned_cnt; /* = 1 . System program */
100 : uchar acct_addr_cnt; /* = 3 */
101 : uchar fee_payer[32];
102 : uchar transfer_dest[32];
103 : uchar system_program[32]; /* = { 0 0 ...} */
104 : uchar recent_blockhash[32];
105 : uchar instr_cnt; /* = 1 */
106 : /* Start of instruction */
107 : struct __attribute__((packed)) {
108 : uchar prog_id; /* = 2 */
109 : uchar acct_cnt; /* = 2 */
110 : uchar from_acct; /* = 0 */
111 : uchar to_acct; /* = 1 */
112 : uchar data_sz; /* = 12 */
113 : uint transfer; /* = 2 . SystemInstruction is bincode encoded, so the
114 : discriminant is a u32, not a u8 */
115 : ulong lamports; /* variable */
116 : } _1;
117 : } sol_transfer_t;
118 :
119 : /* SOL_TRANSFER_LAMPORTS is the base amount of a system transfer. The
120 : destination account does not exist yet, so the transfer has to leave
121 : it rent exempt (890,880 lamports for an empty account under the
122 : genesis rent parameters) or the transaction fails. */
123 :
124 0 : #define SOL_TRANSFER_LAMPORTS (10000000UL)
125 :
126 : typedef struct __attribute__((packed)) {
127 : uchar sig_cnt; /* = 1 */
128 : uchar signature[64];
129 : uchar _sig_cnt; /* also 1 */
130 : uchar ro_signed_cnt; /* = 0 */
131 : uchar ro_unsigned_cnt; /* = 2 . Compute Budget Program, SPL Token Program */
132 : uchar acct_addr_cnt; /* = 5 */
133 : uchar authority[32]; /* fee payer, also the token account owner */
134 : uchar transfer_src[32];
135 : uchar transfer_dest[32];
136 : uchar compute_budget_program[32]; /* = {COMPUTE_BUDGET_PROG_ID} */
137 : uchar token_program[32]; /* = {TOKEN_PROG_ID} */
138 : uchar recent_blockhash[32];
139 : uchar instr_cnt; /* = 2 */
140 : /* Start of instruction */
141 : struct __attribute__((packed)) {
142 : uchar prog_id; /* = 3 */
143 : uchar acct_cnt; /* = 0 */
144 : uchar data_sz; /* = 5 */
145 : uchar set_cu_limit; /* = 2 */
146 : uint cus;
147 : } _1;
148 : /* Start of second instruction */
149 : struct __attribute__((packed)) {
150 : uchar prog_id; /* = 4 */
151 : uchar acct_cnt; /* = 3 */
152 : uchar src_acct; /* = 1 */
153 : uchar dest_acct; /* = 2 */
154 : uchar authority_acct; /* = 0 */
155 : uchar data_sz; /* = 9 */
156 : uchar transfer; /* = 3 . SPL Token Instruction::Transfer */
157 : ulong amount; /* variable */
158 : } _2;
159 : } ptoken_transfer_t;
160 :
161 : /* PTOKEN_TRANSFER_CUS bounds the compute cost of a p-token transfer.
162 : Without an explicit limit every transaction would be charged the
163 : 200k CU per instruction default, which caps a block at a few hundred
164 : transactions and makes the benchmark measure nothing. A transfer
165 : measures out at just over 200 CUs, so this leaves roughly a factor of
166 : two of headroom. */
167 :
168 0 : #define PTOKEN_TRANSFER_CUS (500U)
169 :
170 : static inline void
171 : after_credit( fd_benchg_ctx_t * ctx,
172 : fd_stem_context_t * stem,
173 : int * opt_poll_in,
174 0 : int * charge_busy ) {
175 0 : (void)opt_poll_in;
176 :
177 0 : if( FD_UNLIKELY( !ctx->has_recent_blockhash ) ) return;
178 :
179 0 : *charge_busy = 1;
180 :
181 0 : int is_contending = fd_rng_float_c( ctx->rng ) < ctx->contending_fraction;
182 0 : ulong sender_idx = fd_ulong_if( is_contending, 0UL, ctx->sender_idx );
183 :
184 0 : float norm = 4.0f + fd_rng_float_norm( ctx->rng );
185 0 : if( FD_UNLIKELY( norm<0.0f ) ) norm = 0.0f;
186 0 : ulong cu_price_spread = (ulong)(ctx->cu_price_spread * norm);
187 :
188 0 : void * _txn = fd_chunk_to_laddr( ctx->mem, ctx->out_chunk );
189 :
190 0 : ulong transaction_size = 0UL;
191 0 : uchar * recent_blockhash = NULL;
192 :
193 0 : switch( ctx->transaction_mode ) {
194 0 : case BENCHG_TRANSACTION_MODE_NOOP:
195 0 : {
196 0 : noop_t * txn = (noop_t *)_txn;
197 :
198 0 : txn->sig_cnt = 1;
199 0 : txn->_sig_cnt = 1;
200 0 : txn->ro_signed_cnt = 0;
201 0 : txn->ro_unsigned_cnt = 1;
202 0 : txn->acct_addr_cnt = 2;
203 0 : memcpy( txn->compute_budget_program, (uchar const[32]) { COMPUTE_BUDGET_PROG_ID }, 32UL );
204 0 : txn->instr_cnt = 2;
205 0 : txn->_1.prog_id = 1;
206 0 : txn->_1.acct_cnt = 0;
207 0 : txn->_1.data_sz = 9;
208 0 : txn->_1.set_cu_price = 3;
209 0 : txn->_2.prog_id = 1;
210 0 : txn->_2.acct_cnt = 0;
211 0 : txn->_2.data_sz = 5;
212 0 : txn->_2.set_cu_limit = 2;
213 0 : txn->_2.cus = 300;
214 :
215 : /* Variable */
216 0 : txn->_1.micro_lamports_per_cu = ctx->lamport_idx; /* Unique per transaction so they aren't duplicates */
217 0 : txn->_1.micro_lamports_per_cu += fd_ulong_if( is_contending, 1000000UL, 0UL ); /* +300 lamports */
218 0 : txn->_1.micro_lamports_per_cu += cu_price_spread;
219 :
220 0 : transaction_size = sizeof(noop_t);
221 0 : recent_blockhash = txn->recent_blockhash;
222 0 : }
223 0 : break;
224 :
225 0 : case BENCHG_TRANSACTION_MODE_SOL_TRANSFER:
226 0 : {
227 0 : sol_transfer_t * txn = (sol_transfer_t *)_txn;
228 :
229 0 : txn->sig_cnt = 1;
230 0 : txn->_sig_cnt = 1;
231 0 : txn->ro_signed_cnt = 0;
232 0 : txn->ro_unsigned_cnt = 1;
233 0 : txn->acct_addr_cnt = 3;
234 0 : memcpy( txn->transfer_dest, ctx->acct_public_keys[ sender_idx ].uc, 32UL );
235 0 : for( ulong j=0UL; j<32UL; j++ ) txn->transfer_dest[ j ] ^= 0xFF;
236 0 : memcpy( txn->system_program, (uchar const[32]) { SYS_PROG_ID }, 32UL );
237 0 : txn->instr_cnt = 1;
238 :
239 0 : txn->_1.prog_id = 2;
240 0 : txn->_1.acct_cnt = 2;
241 0 : txn->_1.from_acct = 0;
242 0 : txn->_1.to_acct = 1;
243 0 : txn->_1.data_sz = 12;
244 0 : txn->_1.transfer = 2;
245 :
246 0 : txn->_1.lamports = SOL_TRANSFER_LAMPORTS + ctx->lamport_idx;
247 :
248 0 : transaction_size = sizeof(sol_transfer_t);
249 0 : recent_blockhash = txn->recent_blockhash;
250 0 : }
251 0 : break;
252 :
253 0 : case BENCHG_TRANSACTION_MODE_PTOKEN_TRANSFER:
254 0 : {
255 0 : ptoken_transfer_t * txn = (ptoken_transfer_t *)_txn;
256 :
257 0 : txn->sig_cnt = 1;
258 0 : txn->_sig_cnt = 1;
259 0 : txn->ro_signed_cnt = 0;
260 0 : txn->ro_unsigned_cnt = 2;
261 0 : txn->acct_addr_cnt = 5;
262 0 : memcpy( txn->transfer_src, ctx->token_accounts[ 2UL*sender_idx ].uc, 32UL );
263 0 : memcpy( txn->transfer_dest, ctx->token_accounts[ 2UL*sender_idx+1UL ].uc, 32UL );
264 0 : memcpy( txn->compute_budget_program, (uchar const[32]) { COMPUTE_BUDGET_PROG_ID }, 32UL );
265 0 : memcpy( txn->token_program, (uchar const[32]) { TOKEN_PROG_ID }, 32UL );
266 0 : txn->instr_cnt = 2;
267 :
268 0 : txn->_1.prog_id = 3;
269 0 : txn->_1.acct_cnt = 0;
270 0 : txn->_1.data_sz = 5;
271 0 : txn->_1.set_cu_limit = 2;
272 0 : txn->_1.cus = PTOKEN_TRANSFER_CUS;
273 :
274 0 : txn->_2.prog_id = 4;
275 0 : txn->_2.acct_cnt = 3;
276 0 : txn->_2.src_acct = 1;
277 0 : txn->_2.dest_acct = 2;
278 0 : txn->_2.authority_acct = 0;
279 0 : txn->_2.data_sz = 9;
280 0 : txn->_2.transfer = 3;
281 :
282 : /* Unique per transaction so they aren't duplicates */
283 0 : txn->_2.amount = ctx->lamport_idx;
284 :
285 0 : transaction_size = sizeof(ptoken_transfer_t);
286 0 : recent_blockhash = txn->recent_blockhash;
287 0 : }
288 0 : break;
289 :
290 0 : default:
291 0 : FD_LOG_ERR(( "Unknown transaction mode %i", ctx->transaction_mode ));
292 0 : break;
293 0 : }
294 :
295 0 : single_signer_hdr_t * txnh = (single_signer_hdr_t *)_txn;
296 0 : fd_memcpy( txnh->fee_payer, ctx->acct_public_keys[ sender_idx ].uc, 32UL );
297 0 : fd_memcpy( recent_blockhash, ctx->recent_blockhash, 32UL );
298 :
299 0 : fd_ed25519_sign( txnh->signature,
300 0 : &(txnh->_sig_cnt),
301 0 : transaction_size-65UL,
302 0 : ctx->acct_public_keys[ sender_idx ].uc,
303 0 : ctx->acct_private_keys[ sender_idx ].uc,
304 0 : ctx->sha );
305 :
306 0 : fd_stem_publish( stem, 0UL, 0UL, ctx->out_chunk, transaction_size, 0UL, 0UL, 0UL );
307 0 : ctx->out_chunk = fd_dcache_compact_next( ctx->out_chunk, transaction_size, ctx->out_chunk0, ctx->out_wmark );
308 :
309 0 : ctx->sender_idx = (ctx->sender_idx + 1UL) % ctx->acct_cnt;
310 0 : if( FD_UNLIKELY( !ctx->sender_idx ) ) {
311 0 : if( FD_UNLIKELY( ctx->changed_blockhash ) ) {
312 0 : ctx->lamport_idx = 1UL+ctx->benchg_idx;
313 0 : ctx->changed_blockhash = 0;
314 0 : fd_memcpy( ctx->recent_blockhash, ctx->staged_blockhash, 32UL );
315 0 : } else {
316 : /* Increments of the number of generators so there are never
317 : duplicate transactions generated. */
318 0 : ctx->lamport_idx += ctx->benchg_cnt;
319 0 : }
320 0 : }
321 0 : }
322 :
323 : static inline void
324 : during_frag( fd_benchg_ctx_t * ctx,
325 : ulong in_idx FD_PARAM_UNUSED,
326 : ulong seq FD_PARAM_UNUSED,
327 : ulong sig FD_PARAM_UNUSED,
328 : ulong chunk,
329 : ulong sz FD_PARAM_UNUSED,
330 0 : ulong ctl FD_PARAM_UNUSED ) {
331 0 : if( FD_UNLIKELY( !ctx->has_recent_blockhash ) ) {
332 0 : fd_memcpy( ctx->recent_blockhash, fd_chunk_to_laddr( ctx->mem, chunk ), 32UL );
333 0 : ctx->has_recent_blockhash = 1;
334 0 : ctx->changed_blockhash = 0;
335 0 : } else {
336 0 : if( FD_UNLIKELY( !memcmp( ctx->recent_blockhash, fd_chunk_to_laddr( ctx->mem, chunk ), 32UL ) ) ) return;
337 :
338 0 : fd_memcpy( ctx->staged_blockhash, fd_chunk_to_laddr( ctx->mem, chunk ), 32UL );
339 0 : ctx->changed_blockhash = 1;
340 0 : }
341 0 : }
342 :
343 : static void
344 : unprivileged_init( fd_topo_t const * topo,
345 0 : fd_topo_tile_t const * tile ) {
346 0 : void * scratch = fd_topo_obj_laddr( topo, tile->tile_obj_id );
347 :
348 0 : FD_SCRATCH_ALLOC_INIT( l, scratch );
349 0 : fd_benchg_ctx_t * ctx = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_benchg_ctx_t ), sizeof( fd_benchg_ctx_t ) );
350 0 : ctx->acct_public_keys = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_pubkey_t ), sizeof( fd_pubkey_t ) * tile->benchg.accounts_cnt );
351 0 : ctx->acct_private_keys = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_pubkey_t ), sizeof( fd_pubkey_t ) * tile->benchg.accounts_cnt );
352 0 : ctx->token_accounts = FD_SCRATCH_ALLOC_APPEND( l, alignof( fd_pubkey_t ), sizeof( fd_pubkey_t ) * 2UL * tile->benchg.accounts_cnt );
353 :
354 0 : FD_TEST( fd_rng_join( fd_rng_new( ctx->rng, (uint)tile->kind_id, 0UL ) ) );
355 0 : FD_TEST( fd_sha512_join( fd_sha512_new( ctx->sha ) ) );
356 :
357 0 : ctx->acct_cnt = tile->benchg.accounts_cnt;
358 0 : ctx->transaction_mode = tile->benchg.mode;
359 0 : ctx->contending_fraction = tile->benchg.contending_fraction;
360 0 : ctx->cu_price_spread = tile->benchg.cu_price_spread;
361 :
362 0 : for( ulong i=0UL; i<ctx->acct_cnt; i++ ) {
363 0 : fd_memset( ctx->acct_private_keys[ i ].uc, 0, 32UL );
364 0 : FD_STORE( ulong, ctx->acct_private_keys[ i ].uc, i );
365 0 : fd_ed25519_public_from_private( ctx->acct_public_keys[ i ].uc, ctx->acct_private_keys[ i ].uc , ctx->sha );
366 0 : }
367 :
368 0 : if( FD_UNLIKELY( ctx->transaction_mode==BENCHG_TRANSACTION_MODE_PTOKEN_TRANSFER ) ) {
369 0 : for( ulong i=0UL; i<ctx->acct_cnt; i++ ) {
370 0 : fd_genesis_token_account_address( &ctx->token_accounts[ 2UL*i ], i, 0UL );
371 0 : fd_genesis_token_account_address( &ctx->token_accounts[ 2UL*i+1UL ], i, 1UL );
372 0 : }
373 0 : }
374 :
375 0 : ctx->has_recent_blockhash = 0;
376 :
377 0 : ctx->sender_idx = 0UL;
378 0 : ctx->lamport_idx = 1UL+tile->kind_id;
379 0 : ctx->changed_blockhash = 0;
380 :
381 0 : ctx->benchg_cnt = fd_topo_tile_name_cnt( topo, "benchg" );
382 0 : ctx->benchg_idx = tile->kind_id;
383 :
384 0 : ctx->mem = topo->workspaces[ topo->objs[ topo->links[ tile->out_link_id[ 0 ] ].dcache_obj_id ].wksp_id ].wksp;
385 0 : ctx->out_chunk0 = fd_dcache_compact_chunk0( ctx->mem, topo->links[ tile->out_link_id[ 0 ] ].dcache );
386 0 : ctx->out_wmark = fd_dcache_compact_wmark ( ctx->mem, topo->links[ tile->out_link_id[ 0 ] ].dcache, topo->links[ tile->out_link_id[ 0 ] ].mtu );
387 0 : ctx->out_chunk = ctx->out_chunk0;
388 :
389 0 : ulong scratch_top = FD_SCRATCH_ALLOC_FINI( l, 1UL );
390 0 : if( FD_UNLIKELY( scratch_top > (ulong)scratch + scratch_footprint( tile ) ) )
391 0 : FD_LOG_ERR(( "scratch overflow %lu %lu %lu", scratch_top - (ulong)scratch - scratch_footprint( tile ), scratch_top, (ulong)scratch + scratch_footprint( tile ) ));
392 0 : }
393 :
394 0 : #define STEM_BURST (1UL)
395 :
396 0 : #define STEM_CALLBACK_CONTEXT_TYPE fd_benchg_ctx_t
397 0 : #define STEM_CALLBACK_CONTEXT_ALIGN alignof(fd_benchg_ctx_t)
398 :
399 0 : #define STEM_CALLBACK_AFTER_CREDIT after_credit
400 0 : #define STEM_CALLBACK_DURING_FRAG during_frag
401 :
402 : #include "../../../../disco/stem/fd_stem.c"
403 :
404 : fd_topo_run_tile_t fd_tile_benchg = {
405 : .name = "benchg",
406 : .scratch_align = scratch_align,
407 : .scratch_footprint = scratch_footprint,
408 : .unprivileged_init = unprivileged_init,
409 : .run = stem_run,
410 : };
|