@@ -27,6 +27,14 @@ const ThreadPool = bls.ThreadPool;
2727const DST = bls .DST ;
2828const MAX_AGGREGATE_PER_JOB = bls .MAX_AGGREGATE_PER_JOB ;
2929
30+ /// In upstream lodestar we split batchable sets into chunks of minimum size 16.
31+ /// Cost savings after ~16 are not significant.
32+ /// In metrics, we can observe that sas fleet receives on average ~30 signature sets,
33+ /// so a safe bound is about 32.
34+ ///
35+ /// See: packages/beacon-node/src/chain/bls/multithread/worker.ts
36+ const BATCH_VERIFY_SIZE = 32 ;
37+
3038/// Cached thread pool reference for parallel verification.
3139/// Initialized lazily on first use, torn down via `deinitThreadPool`.
3240var thread_pool : ? * ThreadPool = null ;
@@ -429,17 +437,40 @@ pub fn verifyMultipleAggregateSignatures(sets: js.Array, pks_validate: ?js.Boole
429437 const n_elems = try sets .length ();
430438 if (n_elems == 0 ) return js .Boolean .from (false );
431439
432- const msgs = try allocator .alloc ([32 ]u8 , n_elems );
433- defer allocator .free (msgs );
434-
435- const pks = try allocator .alloc (* NativePublicKey , n_elems );
436- defer allocator .free (pks );
437-
438- const sigs = try allocator .alloc (* NativeSignature , n_elems );
439- defer allocator .free (sigs );
440-
441- const rands = try allocator .alloc ([32 ]u8 , n_elems );
442- defer allocator .free (rands );
440+ var msgs_stack : [BATCH_VERIFY_SIZE ][]const u8 = undefined ;
441+ var pks_stack : [BATCH_VERIFY_SIZE ]* NativePublicKey = undefined ;
442+ var sigs_stack : [BATCH_VERIFY_SIZE ]* NativeSignature = undefined ;
443+ var rands_stack : [BATCH_VERIFY_SIZE ][32 ]u8 = undefined ;
444+
445+ var msgs_heap : ? [][]const u8 = null ;
446+ defer if (msgs_heap ) | buf | allocator .free (buf );
447+ var pks_heap : ? []* NativePublicKey = null ;
448+ defer if (pks_heap ) | buf | allocator .free (buf );
449+ var sigs_heap : ? []* NativeSignature = null ;
450+ defer if (sigs_heap ) | buf | allocator .free (buf );
451+ var rands_heap : ? [][32 ]u8 = null ;
452+ defer if (rands_heap ) | buf | allocator .free (buf );
453+
454+ const msgs = if (n_elems <= BATCH_VERIFY_SIZE ) msgs_stack [0.. n_elems ] else blk : {
455+ const buf = try allocator .alloc ([]const u8 , n_elems );
456+ msgs_heap = buf ;
457+ break :blk buf ;
458+ };
459+ const pks = if (n_elems <= BATCH_VERIFY_SIZE ) pks_stack [0.. n_elems ] else blk : {
460+ const buf = try allocator .alloc (* NativePublicKey , n_elems );
461+ pks_heap = buf ;
462+ break :blk buf ;
463+ };
464+ const sigs = if (n_elems <= BATCH_VERIFY_SIZE ) sigs_stack [0.. n_elems ] else blk : {
465+ const buf = try allocator .alloc (* NativeSignature , n_elems );
466+ sigs_heap = buf ;
467+ break :blk buf ;
468+ };
469+ const rands = if (n_elems <= BATCH_VERIFY_SIZE ) rands_stack [0.. n_elems ] else blk : {
470+ const buf = try allocator .alloc ([32 ]u8 , n_elems );
471+ rands_heap = buf ;
472+ break :blk buf ;
473+ };
443474
444475 var seed_bytes : [8 ]u8 = undefined ;
445476 const io = napi_io .get ();
@@ -454,7 +485,7 @@ pub fn verifyMultipleAggregateSignatures(sets: js.Array, pks_validate: ?js.Boole
454485 const msg_napi = try set .getNamedProperty ("msg" );
455486 const msg_bytes = try uint8SliceFromValue (.{ .val = msg_napi });
456487 if (msg_bytes .len != 32 ) return error .InvalidMessageLength ;
457- @memcpy ( & msgs [i ], msg_bytes [0 .. 32]) ;
488+ msgs [i ] = msg_bytes ;
458489
459490 const pk_napi = try set .getNamedProperty ("pk" );
460491 const wrapped_pk = try e .unwrap (PublicKey , pk_napi );
0 commit comments