namespace Ckzg; public static partial class Ckzg { public const int BytesPerFieldElement = 32; public const int FieldElementsPerBlob = 4096; public const int BytesPerBlob = BytesPerFieldElement * FieldElementsPerBlob; public const int BytesPerCommitment = 48; public const int BytesPerProof = 48; /// /// Loads trusted setup settings from file. /// /// Settings file path /// Thrown when the file path is not correct /// Thrown when unable to load the setup /// Trusted setup settings as a pointer public static IntPtr LoadTrustedSetup(string filepath) { if (!File.Exists(filepath)) throw new ArgumentException("Trusted setup file does not exist", nameof(filepath)); IntPtr ckzgSetup = InternalLoadTrustedSetup(filepath); if (ckzgSetup == IntPtr.Zero) throw new InvalidOperationException("Unable to load trusted setup"); return ckzgSetup; } /// /// Frees memory allocated for trusted setup settings. /// /// Trusted setup settings /// Thrown when settings are not correct public static void FreeTrustedSetup(IntPtr ckzgSetup) { ThrowOnUninitializedTrustedSetup(ckzgSetup); InternalFreeTrustedSetup(ckzgSetup); } /// /// Calculates commitment for the blob. /// /// Preallocated buffer of bytes to receive the commitment /// Flatten array of blob elements /// Trusted setup settings /// Thrown when length of an argument is not correct or settings are not correct /// Thrown when the library returns unexpected Error code /// Thrown when the library has no enough memory to process public static unsafe void BlobToKzgCommitment(Span commitment, ReadOnlySpan blob, IntPtr ckzgSetup) { ThrowOnUninitializedTrustedSetup(ckzgSetup); ThrowOnInvalidLength(blob, nameof(blob), BytesPerBlob); ThrowOnInvalidLength(commitment, nameof(commitment), BytesPerCommitment); fixed (byte* commitmentPtr = commitment, blobPtr = blob) { KzgResult result = BlobToKzgCommitment(commitmentPtr, blobPtr, ckzgSetup); ThrowOnError(result); } } /// /// Compute KZG proof at point `z` for the polynomial represented by `blob`. /// /// Preallocated buffer of bytes to receive the proof /// Blob bytes /// Z point /// Trusted setup settings /// Thrown when length of an argument is not correct or settings are not correct /// Thrown when the library returns unexpected Error code /// Thrown when the library has no enough memory to process public static unsafe void ComputeKzgProof(Span proof, ReadOnlySpan blob, ReadOnlySpan z, IntPtr ckzgSetup) { ThrowOnUninitializedTrustedSetup(ckzgSetup); ThrowOnInvalidLength(proof, nameof(proof), BytesPerProof); ThrowOnInvalidLength(blob, nameof(blob), BytesPerBlob); ThrowOnInvalidLength(z, nameof(z), BytesPerFieldElement); if (z.Length != BytesPerFieldElement) throw new ArgumentException("Invalid z size", nameof(z)); fixed (byte* proofPtr = proof, blobPtr = blob, zPtr = z) { KzgResult result = ComputeKzgProof(proofPtr, blobPtr, zPtr, ckzgSetup); ThrowOnError(result); } } /// /// Given a blob, return the KZG proof that is used to verify it against the commitment. /// /// Preallocated buffer of bytes to receive the proof /// Blob bytes /// Trusted setup settings /// Thrown when length of an argument is not correct or settings are not correct /// Thrown when the library returns unexpected Error code /// Thrown when the library has no enough memory to process public static unsafe void ComputeBlobKzgProof(Span proof, ReadOnlySpan blob, IntPtr ckzgSetup) { ThrowOnUninitializedTrustedSetup(ckzgSetup); ThrowOnInvalidLength(proof, nameof(proof), BytesPerProof); ThrowOnInvalidLength(blob, nameof(blob), BytesPerBlob); fixed (byte* proofPtr = proof, blobPtr = blob) { KzgResult result = ComputeBlobKzgProof(proofPtr, blobPtr, ckzgSetup); ThrowOnError(result); } } /// /// Given a blob and a KZG proof, verify that the blob data corresponds to the provided commitment. /// /// /// /// /// Trusted setup settings /// Thrown when length of an argument is not correct or settings are not correct /// Thrown when the library returns unexpected Error code /// Thrown when the library has no enough memory to process /// Verification result public static unsafe bool VerifyKzgProof(ReadOnlySpan commitment, ReadOnlySpan z, ReadOnlySpan y, ReadOnlySpan proof, IntPtr ckzgSetup) { ThrowOnUninitializedTrustedSetup(ckzgSetup); ThrowOnInvalidLength(commitment, nameof(commitment), BytesPerCommitment); ThrowOnInvalidLength(z, nameof(z), BytesPerFieldElement); ThrowOnInvalidLength(y, nameof(y), BytesPerFieldElement); ThrowOnInvalidLength(proof, nameof(proof), BytesPerProof); fixed (byte* commitmentPtr = commitment, zPtr = z, yPtr = y, proofPtr = proof) { KzgResult kzgResult = VerifyKzgProof(out var result, commitmentPtr, zPtr, yPtr, proofPtr, ckzgSetup); ThrowOnError(kzgResult); return result; } } /// /// Given a blob and a KZG proof, verify that the blob data corresponds to the provided commitment. /// /// Blob bytes /// Commitment bytes /// Proof bytes /// Trusted setup settings /// Thrown when length of an argument is not correct or settings are not correct /// Thrown when the library returns unexpected Error code /// Thrown when the library has no enough memory to process /// Verification result public static unsafe bool VerifyBlobKzgProof(ReadOnlySpan blob, ReadOnlySpan commitment, ReadOnlySpan proof, IntPtr ckzgSetup) { ThrowOnUninitializedTrustedSetup(ckzgSetup); ThrowOnInvalidLength(blob, nameof(blob), BytesPerBlob); ThrowOnInvalidLength(commitment, nameof(proof), BytesPerCommitment); ThrowOnInvalidLength(proof, nameof(proof), BytesPerProof); fixed (byte* blobPtr = blob, commitmentPtr = commitment, proofPtr = proof) { KzgResult kzgResult = VerifyBlobKzgProof(out var result, blobPtr, commitmentPtr, proofPtr, ckzgSetup); ThrowOnError(kzgResult); return result; } } /// /// Given a list of blobs and blob KZG proofs, verify that they correspond to the provided commitments. /// /// Blobs as a flattened byte array /// Commitments as a flattened byte array /// Proofs as a flattened byte array /// The number of blobs/commitments/proofs /// Trusted setup settings /// Thrown when length of an argument is not correct or settings are not correct /// Thrown when the library returns unexpected Error code /// Thrown when the library has no enough memory to process /// Verification result public static unsafe bool VerifyBlobKzgProofBatch(ReadOnlySpan blobs, ReadOnlySpan commitments, ReadOnlySpan proofs, int count, IntPtr ckzgSetup) { ThrowOnUninitializedTrustedSetup(ckzgSetup); ThrowOnInvalidLength(blobs, nameof(blobs), BytesPerBlob * count); ThrowOnInvalidLength(commitments, nameof(proofs), BytesPerCommitment * count); ThrowOnInvalidLength(proofs, nameof(proofs), BytesPerProof * count); fixed (byte* blobsPtr = blobs, commitmentsPtr = commitments, proofsPtr = proofs) { KzgResult kzgResult = VerifyBlobKzgProofBatch(out var result, blobsPtr, commitmentsPtr, proofsPtr, count, ckzgSetup); ThrowOnError(kzgResult); return result; } } #region Argument verification helpers private static void ThrowOnError(KzgResult result) { switch (result) { case KzgResult.BadArgs: throw new ArgumentException(); case KzgResult.Malloc: throw new InsufficientMemoryException(); case KzgResult.Ok: return; default: throw new ApplicationException("KZG returned unexpected result"); } } private static void ThrowOnUninitializedTrustedSetup(IntPtr ckzgSetup) { if (ckzgSetup == IntPtr.Zero) throw new ArgumentException("Trusted setup is not initialized", nameof(ckzgSetup)); } private static void ThrowOnInvalidLength(ReadOnlySpan data, string fieldName, int expectedLength) { if (data.Length != expectedLength) throw new ArgumentException("Invalid data size", fieldName); } #endregion }