/// <inheritdoc /> public override bool TryAggregateSignatures(ReadOnlySpan <byte> signatures, Span <byte> destination, out int bytesWritten) { // This is independent of the keys set, although other parameters (type of curve, variant, scheme, etc) are relevant. if (signatures.Length % SignatureLength != 0) { throw new ArgumentOutOfRangeException(nameof(signatures), signatures.Length, $"Signature data must be a multiple of the signature length {SignatureLength}."); } if (destination.Length < SignatureLength) { bytesWritten = 0; return(false); } EnsureInitialised(); var aggregateBlsSignature = default(Bls384Interop.BlsSignature); for (var index = 0; index < signatures.Length; index += SignatureLength) { var signatureSlice = signatures.Slice(index, SignatureLength); var blsSignature = default(Bls384Interop.BlsSignature); int signatureBytesRead; unsafe { // Using fixed pointer for input data allows us to pass a slice fixed(byte *signaturePtr = signatureSlice) { signatureBytesRead = Bls384Interop.SignatureDeserialize(ref blsSignature, signaturePtr, SignatureLength); } } if (signatureBytesRead != SignatureLength) { throw new Exception($"Error deserializing BLS signature, length: {signatureBytesRead}"); } if (index == 0) { aggregateBlsSignature = blsSignature; } else { Bls384Interop.SignatureAdd(ref aggregateBlsSignature, ref blsSignature); } } unsafe { // Using fixed pointer for output data allows us to write directly to destination fixed(byte *destinationPtr = destination) { bytesWritten = Bls384Interop.SignatureSerialize(destinationPtr, SignatureLength, ref aggregateBlsSignature); } } if (bytesWritten != SignatureLength) { throw new Exception($"Error serializing BLS signature, length: {bytesWritten}"); } return(true); }