diff --git a/.github/workflows/csharp-bindings-build.yml b/.github/workflows/csharp-bindings-build.yml index 50b88ae..002adf3 100644 --- a/.github/workflows/csharp-bindings-build.yml +++ b/.github/workflows/csharp-bindings-build.yml @@ -1,11 +1,17 @@ name: C# bindings test and build on: + workflow_dispatch: + inputs: + version: + description: "Binding version override" push: branches: - main pull_request: branches: - main +env: + binding_build_number_based_version: 0.1.1.${{ github.run_number }} jobs: build-ckzg: @@ -13,29 +19,48 @@ jobs: strategy: matrix: target: - - native: linux-x64 - host: ubuntu-latest - - native: linux-arm64 - host: ubuntu-latest - - native: osx-arm64 + - arch: x86_64-linux-gnu + location: linux-x64 + host: ubuntu-22.04 + ext: .so + reqs: + - arch: aarch64-linux-gnu + location: linux-arm64 + host: ubuntu-22.04 + ext: .so + reqs: sudo apt install -y clang binutils-aarch64-linux-gnu libc6-arm64-cross libc6-dev-arm64-cross crossbuild-essential-arm64 + - arch: arm64-darwin + location: osx-arm64 host: macos-latest - - native: osx-x64 + ext: .so + reqs: + - arch: x86_64-darwin + location: osx-x64 host: macos-latest - - native: win-x64 + ext: .so + reqs: + #TODO: support arch: x86_64-v2-win + - arch: + location: win-x64 host: windows-latest + ext: .dll + reqs: steps: - uses: ilammy/msvc-dev-cmd@v1 - uses: actions/checkout@v3 with: submodules: recursive - - name: Build native library for ${{ matrix.target.native }} - run: cd bindings/csharp && make -B ckzg CSHARP_PLATFORM=${{ matrix.target.native }} + - name: Install requirements + if: ${{ matrix.target.reqs }} + run: ${{ matrix.target.reqs }} + - name: Build native library for ${{ matrix.target.location }} using native capabilities + run: make ckzg -C bindings/csharp CC=clang EXTENSION=${{matrix.target.ext}} LOCATION=${{matrix.target.location}} ARCH=${{matrix.target.arch}} - name: Upload artifacts uses: actions/upload-artifact@v3 with: - name: ckzg-library-${{ matrix.target.native }} - path: bindings/csharp/Ckzg.Bindings/runtimes/${{ matrix.target.native }}/native - + name: ckzg-library-wrapper-${{ matrix.target.location }} + path: bindings/csharp/Ckzg.Bindings/runtimes/${{ matrix.target.location }}/native + build-ckzg-dotnet: runs-on: ubuntu-latest needs: build-ckzg @@ -43,37 +68,35 @@ jobs: - uses: actions/checkout@v3 - uses: actions/download-artifact@v3 with: - name: ckzg-library-linux-x64 + name: ckzg-library-wrapper-linux-x64 path: bindings/csharp/Ckzg.Bindings/runtimes/linux-x64/native - uses: actions/download-artifact@v3 with: - name: ckzg-library-osx-x64 + name: ckzg-library-wrapper-osx-x64 path: bindings/csharp/Ckzg.Bindings/runtimes/osx-x64/native - uses: actions/download-artifact@v3 with: - name: ckzg-library-win-x64 + name: ckzg-library-wrapper-win-x64 path: bindings/csharp/Ckzg.Bindings/runtimes/win-x64/native - uses: actions/download-artifact@v3 with: - name: ckzg-library-osx-arm64 + name: ckzg-library-wrapper-osx-arm64 path: bindings/csharp/Ckzg.Bindings/runtimes/osx-arm64/native - uses: actions/download-artifact@v3 with: - name: ckzg-library-linux-arm64 + name: ckzg-library-wrapper-linux-arm64 path: bindings/csharp/Ckzg.Bindings/runtimes/linux-arm64/native - - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} + - name: Setup .NET Core SDK uses: actions/setup-dotnet@v3 - with: - dotnet-version: ${{ matrix.dotnet-version }} - name: Install dependencies run: cd bindings/csharp && dotnet restore - name: Test run: cd bindings/csharp && dotnet test --configuration Release --no-restore - name: Build - run: cd bindings/csharp && dotnet build -p:Version=0.1.1.${{github.run_number}} --configuration Release --no-restore + run: cd bindings/csharp && dotnet build -p:Version=${{ inputs.version || env.binding_build_number_based_version }} --configuration Release --no-restore - name: Upload package uses: actions/upload-artifact@v3 with: - name: Ckzg.Bindings + name: Ckzg.Bindings-${{ inputs.version || env.binding_build_number_based_version }} path: bindings/csharp/build/Ckzg.Bindings.*.nupkg diff --git a/bindings/csharp/Ckzg.Bindings/Ckzg.Bindings.cs b/bindings/csharp/Ckzg.Bindings/Ckzg.Bindings.cs new file mode 100644 index 0000000..d64f58a --- /dev/null +++ b/bindings/csharp/Ckzg.Bindings/Ckzg.Bindings.cs @@ -0,0 +1,62 @@ +using System.Runtime.InteropServices; +using System.Runtime.Loader; + +namespace Ckzg; + +public static partial class Ckzg +{ + static Ckzg() + { + AssemblyLoadContext.Default.ResolvingUnmanagedDll += (_, path) => + path.Contains("ckzg", StringComparison.OrdinalIgnoreCase) + ? NativeLibrary.Load($"runtimes/{( + RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : + RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "osx" : + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win" : "")}-{RuntimeInformation.ProcessArchitecture switch + { + Architecture.X64 => "x64", + Architecture.Arm64 => "arm64", + _ => "" + }}/native/{path}.{(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dll" : "so")}") + : IntPtr.Zero; + } + + [DllImport("ckzg", EntryPoint = "load_trusted_setup_wrap")] + private static extern IntPtr InternalLoadTrustedSetup(string filename); + + [DllImport("ckzg", EntryPoint = "free_trusted_setup_wrap", CallingConvention = CallingConvention.Cdecl)] + private static extern void InternalFreeTrustedSetup(IntPtr ts); + + [DllImport("ckzg", EntryPoint = "blob_to_kzg_commitment", CallingConvention = CallingConvention.Cdecl)] + private static extern unsafe KzgResult BlobToKzgCommitment(byte* commitment, byte* blob, IntPtr ts); + + [DllImport("ckzg", EntryPoint = "compute_kzg_proof", CallingConvention = CallingConvention.Cdecl)] + private static extern unsafe KzgResult ComputeKzgProof(byte* proof, byte* blob, byte* z, IntPtr ts); + + [DllImport("ckzg", EntryPoint = "compute_blob_kzg_proof", CallingConvention = CallingConvention.Cdecl)] + private static extern unsafe KzgResult ComputeBlobKzgProof(byte* proof, byte* blob, IntPtr ts); + + [DllImport("ckzg", EntryPoint = "verify_kzg_proof", CallingConvention = CallingConvention.Cdecl)] + private static extern unsafe KzgResult VerifyKzgProof(out bool result, byte* commitment, byte* z, + byte* y, byte* proof, IntPtr ts); + + [DllImport("ckzg", EntryPoint = "verify_blob_kzg_proof", CallingConvention = CallingConvention.Cdecl)] + private static extern unsafe KzgResult VerifyBlobKzgProof(out bool result, byte* blob, byte* commitment, + byte* proof, IntPtr ts); + + [DllImport("ckzg", EntryPoint = "verify_blob_kzg_proof_batch", CallingConvention = CallingConvention.Cdecl)] + private static extern unsafe KzgResult VerifyBlobKzgProofBatch(out bool result, byte* blobs, byte* commitments, + byte* proofs, int count, IntPtr ts); + + private enum KzgResult + { + // Success! + Ok, + // The supplied data is invalid in some way. + BadArgs, + // Internal error - this should never occur. + Error, + // Could not allocate memory. + Malloc + } +} \ No newline at end of file diff --git a/bindings/csharp/Ckzg.Bindings/Ckzg.Bindings.csproj b/bindings/csharp/Ckzg.Bindings/Ckzg.Bindings.csproj index 255ae46..1a551e3 100644 --- a/bindings/csharp/Ckzg.Bindings/Ckzg.Bindings.csproj +++ b/bindings/csharp/Ckzg.Bindings/Ckzg.Bindings.csproj @@ -1,32 +1,32 @@ - - Library - net6.0 - Ckzg - enable - enable - preview - True - True - Ckzg.Bindings - LICENSE - 0.1.0.0 - + + Library + net6.0 + Ckzg + enable + enable + preview + True + True + Ckzg.Bindings + LICENSE + 0.1.0.0 + - - - True - \ - - + + + True + \ + + - - - - - - - + + + + + + + diff --git a/bindings/csharp/Ckzg.Bindings/Ckzg.cs b/bindings/csharp/Ckzg.Bindings/Ckzg.cs index 9d37ade..179cadd 100644 --- a/bindings/csharp/Ckzg.Bindings/Ckzg.cs +++ b/bindings/csharp/Ckzg.Bindings/Ckzg.cs @@ -1,115 +1,222 @@ -using System.Runtime.InteropServices; -using System.Runtime.Loader; - namespace Ckzg; -public class Ckzg +public static partial class Ckzg { public const int BytesPerFieldElement = 32; - public const int BytesPerBlob = BytesPerFieldElement * 4096; + public const int FieldElementsPerBlob = 4096; + public const int BytesPerBlob = BytesPerFieldElement * FieldElementsPerBlob; public const int BytesPerCommitment = 48; public const int BytesPerProof = 48; - public enum Ret - { - Ok, - BadArgs, - Error, - Malloc - } - - static Ckzg() => AssemblyLoadContext.Default.ResolvingUnmanagedDll += (assembly, path) => NativeLibrary.Load($"runtimes/{( - RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? "linux" : - RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "osx" : - RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "win" : "")}-{RuntimeInformation.ProcessArchitecture switch - { - Architecture.X64 => "x64", - Architecture.Arm64 => "arm64", - _ => "" - }}/native/{path}.{(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "dll" : "so")}"); - /// - /// Load trusted setup settings from file + /// Loads trusted setup settings from file. /// /// Settings file path - /// Trusted setup settings as a pointer or 0 in case of failure - [DllImport("ckzg", EntryPoint = "load_trusted_setup_wrap")] - public static extern IntPtr LoadTrustedSetup(string filename); + /// 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 + /// Frees memory allocated for trusted setup settings. /// - /// Trusted setup settings - [DllImport("ckzg", EntryPoint = "free_trusted_setup_wrap", CallingConvention = CallingConvention.Cdecl)] - public static extern void FreeTrustedSetup(IntPtr ts); + /// Trusted setup settings + /// Thrown when settings are not correct + + public static void FreeTrustedSetup(IntPtr ckzgSetup) + { + ThrowOnUninitializedTrustedSetup(ckzgSetup); + InternalFreeTrustedSetup(ckzgSetup); + } /// - /// Calculates commitment for the blob + /// Calculates commitment for the blob. /// - /// Preallocated buffer of bytes to receive the commitment + /// Preallocated buffer of bytes to receive the commitment /// Flatten array of blob elements - /// Trusted setup settings - /// Returns error code or 0 if successful - [DllImport("ckzg", EntryPoint = "blob_to_kzg_commitment", CallingConvention = CallingConvention.Cdecl)] - public unsafe static extern Ret BlobToKzgCommitment(byte* commitment, byte* blob, IntPtr ts); + /// 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`. + /// Compute KZG proof at point `z` for the polynomial represented by `blob`. /// - /// Preallocated buffer of bytes to receive the proof - /// Blob byte array - /// - /// Trusted setup settings - /// Returns error code or 0 if successful - [DllImport("ckzg", EntryPoint = "compute_kzg_proof", CallingConvention = CallingConvention.Cdecl)] - public unsafe static extern Ret ComputeKzgProof(byte* proof, byte* blob, byte* z_bytes, IntPtr ts); + /// 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. + /// 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 byte array - /// Trusted setup settings - /// Returns error code or 0 if successful - [DllImport("ckzg", EntryPoint = "compute_blob_kzg_proof", CallingConvention = CallingConvention.Cdecl)] - public unsafe static extern Ret ComputeBlobKzgProof(byte* proof, byte* blob, IntPtr ts); + /// 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. /// - /// True if the proof is valid - /// - /// - /// - /// - /// Trusted setup settings - /// - [DllImport("ckzg", EntryPoint = "verify_kzg_proof", CallingConvention = CallingConvention.Cdecl)] - public unsafe static extern Ret VerifyKzgProof(bool* result, byte* commitment_bytes, byte* z_bytes, byte* y_bytes, byte* proof_bytes, IntPtr ts); - - /// - /// Given a blob and a KZG proof, verify that the blob data corresponds to the provided commitment. - /// - /// True if the proof is valid /// /// /// - /// Trusted setup settings - /// - [DllImport("ckzg", EntryPoint = "verify_blob_kzg_proof", CallingConvention = CallingConvention.Cdecl)] - public unsafe static extern Ret VerifyBlobKzgProof(bool* result, byte* blob, byte* commitment_bytes, byte* proof_bytes, IntPtr ts); + /// 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 list of blobs and blob KZG proofs, verify that they correspond to the provided commitments. + /// 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. /// - /// True if the proofs are valid /// 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 - /// - [DllImport("ckzg", EntryPoint = "verify_blob_kzg_proof_batch", CallingConvention = CallingConvention.Cdecl)] - public unsafe static extern Ret VerifyBlobKzgProofBatch(bool* result, byte* blobs, byte* commitments_bytes, byte* proofs_bytes, int count, IntPtr ts); -} + /// 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 +} \ No newline at end of file diff --git a/bindings/csharp/Ckzg.Test/Ckzg.Test.csproj b/bindings/csharp/Ckzg.Test/Ckzg.Test.csproj index dd34fe9..a0863ed 100644 --- a/bindings/csharp/Ckzg.Test/Ckzg.Test.csproj +++ b/bindings/csharp/Ckzg.Test/Ckzg.Test.csproj @@ -1,29 +1,31 @@ - - net6.0 - enable - enable - True - false - + + net7.0 + enable + enable + True + false + - - - - - - - + + + + + + + + + - - - + + + - - - Always - - + + + Always + + diff --git a/bindings/csharp/Ckzg.Test/E2eTests.cs b/bindings/csharp/Ckzg.Test/E2eTests.cs deleted file mode 100644 index 526e187..0000000 --- a/bindings/csharp/Ckzg.Test/E2eTests.cs +++ /dev/null @@ -1,218 +0,0 @@ -using NUnit.Framework; -using System.IO; - -namespace Ckzg.Test; - -[TestFixture] -public class BasicKzgTests -{ - private IntPtr ts; - - const string TestDir = "../../../../../../tests"; - string BlobToKZGCommitmentTests = Path.Join(TestDir, "blob_to_kzg_commitment"); - string ComputeKzgProofTests = Path.Join(TestDir, "compute_kzg_proof"); - string ComputeBlobKzgProofTests = Path.Join(TestDir, "compute_blob_kzg_proof"); - string VerifyKzgProofTests = Path.Join(TestDir, "verify_kzg_proof"); - string VerifyBlobKzgProofTests = Path.Join(TestDir, "verify_blob_kzg_proof"); - string VerifyBlobKzgProofBatchTests = Path.Join(TestDir, "verify_blob_kzg_proof_batch"); - - public static byte[] StringToByteArray(string hex) - { - return Enumerable.Range(0, hex.Length) - .Where(x => x % 2 == 0) - .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) - .ToArray(); - } - - public static byte[] GetBytes(String path) - { - string hex = System.IO.File.ReadAllText(path); - return StringToByteArray(hex); - } - - public static byte[] GetFlatBytes(String path) - { - List files = Directory.GetFiles(path).ToList(); - files.Sort(); - List filesBytes = new List(); - foreach (String file in files) - { - filesBytes.Add(GetBytes(file)); - } - - byte[] flatBytes = new byte[filesBytes.Sum(b => b.Length)]; - int offset = 0; - foreach (byte[] bytes in filesBytes) - { - System.Buffer.BlockCopy(bytes, 0, flatBytes, offset, bytes.Length); - offset += bytes.Length; - } - - return flatBytes; - } - - public static bool GetBoolean(String path) - { - return System.IO.File.ReadAllText(path).Contains("true"); - } - - [SetUp] - public void Setup() - { - ts = Ckzg.LoadTrustedSetup("trusted_setup.txt"); - Assert.That(ts, Is.Not.EqualTo(IntPtr.Zero)); - } - - [TearDown] - public void Teardown() - { - Ckzg.FreeTrustedSetup(ts); - } - - [TestCase] - public unsafe void TestBlobToKzgCommitment() - { - foreach (String test in Directory.GetDirectories(BlobToKZGCommitmentTests)) - { - byte[] commitment = new byte[48]; - byte[] blob = GetBytes(Path.Join(test, "blob.txt")); - fixed (byte *pCommitment = commitment, pBlob = blob) - { - Ckzg.Ret ret = Ckzg.BlobToKzgCommitment(pCommitment, pBlob, ts); - if (ret == Ckzg.Ret.Ok) - { - byte[] expectedCommitment = GetBytes(Path.Join(test, "commitment.txt")); - Assert.That(commitment, Is.EqualTo(expectedCommitment)); - } - else - { - Assert.False(System.IO.File.Exists(Path.Join(test, "commitment.txt"))); - } - } - } - } - - [TestCase] - public unsafe void TestComputeKzgProof() - { - foreach (String test in Directory.GetDirectories(ComputeKzgProofTests)) - { - byte[] proof = new byte[48]; - byte[] blob = GetBytes(Path.Join(test, "blob.txt")); - byte[] inputPoint = GetBytes(Path.Join(test, "input_point.txt")); - fixed (byte *pProof = proof, pBlob = blob, pInputPoint = inputPoint) - { - Ckzg.Ret ret = Ckzg.ComputeKzgProof(pProof, pBlob, pInputPoint, ts); - if (ret == Ckzg.Ret.Ok) - { - byte[] expectedProof = GetBytes(Path.Join(test, "proof.txt")); - Assert.That(proof, Is.EqualTo(expectedProof)); - } - else - { - Assert.False(System.IO.File.Exists(Path.Join(test, "proof.txt"))); - } - } - } - } - - [TestCase] - public unsafe void TestComputeBlobKzgProof() - { - foreach (String test in Directory.GetDirectories(ComputeBlobKzgProofTests)) - { - byte[] proof = new byte[48]; - byte[] blob = GetBytes(Path.Join(test, "blob.txt")); - fixed (byte *pProof = proof, pBlob = blob) - { - Ckzg.Ret ret = Ckzg.ComputeBlobKzgProof(pProof, pBlob, ts); - if (ret == Ckzg.Ret.Ok) - { - byte[] expectedProof = GetBytes(Path.Join(test, "proof.txt")); - Assert.That(proof, Is.EqualTo(expectedProof)); - } - else - { - Assert.False(System.IO.File.Exists(Path.Join(test, "proof.txt"))); - } - } - } - } - - [TestCase] - public unsafe void TestVerifyKzgProof() - { - foreach (String test in Directory.GetDirectories(VerifyKzgProofTests)) - { - bool ok = false; - byte[] commitment = GetBytes(Path.Join(test, "commitment.txt")); - byte[] inputPoint = GetBytes(Path.Join(test, "input_point.txt")); - byte[] claimedValue = GetBytes(Path.Join(test, "claimed_value.txt")); - byte[] proof = GetBytes(Path.Join(test, "proof.txt")); - fixed (byte *pCommitment = commitment, pInputPoint = inputPoint, pClaimedValue = claimedValue, pProof = proof) - { - Ckzg.Ret ret = Ckzg.VerifyKzgProof(&ok, pCommitment, pInputPoint, pClaimedValue, pProof, ts); - if (ret == Ckzg.Ret.Ok) - { - bool expectedOk = GetBoolean(Path.Join(test, "ok.txt")); - Assert.That(ok, Is.EqualTo(expectedOk)); - } - else - { - Assert.False(System.IO.File.Exists(Path.Join(test, "ok.txt"))); - } - } - } - } - - [TestCase] - public unsafe void TestVerifyBlobKzgProof() - { - foreach (String test in Directory.GetDirectories(VerifyBlobKzgProofTests)) - { - bool ok = false; - byte[] blob = GetBytes(Path.Join(test, "blob.txt")); - byte[] commitment = GetBytes(Path.Join(test, "commitment.txt")); - byte[] proof = GetBytes(Path.Join(test, "proof.txt")); - fixed (byte *pBlob = blob, pCommitment = commitment, pProof = proof) - { - Ckzg.Ret ret = Ckzg.VerifyBlobKzgProof(&ok, pBlob, pCommitment, pProof, ts); - if (ret == Ckzg.Ret.Ok) - { - bool expectedOk = GetBoolean(Path.Join(test, "ok.txt")); - Assert.That(ok, Is.EqualTo(expectedOk)); - } - else - { - Assert.False(System.IO.File.Exists(Path.Join(test, "ok.txt"))); - } - } - } - } - - [TestCase] - public unsafe void TestVerifyBlobKzgProofBatch() - { - foreach (String test in Directory.GetDirectories(VerifyBlobKzgProofBatchTests)) - { - bool ok = false; - byte[] blobs = GetFlatBytes(Path.Join(test, "blobs")); - byte[] commitments = GetFlatBytes(Path.Join(test, "commitments")); - byte[] proofs = GetFlatBytes(Path.Join(test, "proofs")); - int count = blobs.Length / Ckzg.BytesPerBlob; - fixed (byte *pBlobs = blobs, pCommitments = commitments, pProofs = proofs) - { - Ckzg.Ret ret = Ckzg.VerifyBlobKzgProofBatch(&ok, pBlobs, pCommitments, pProofs, count, ts); - if (ret == Ckzg.Ret.Ok) - { - bool expectedOk = GetBoolean(Path.Join(test, "ok.txt")); - Assert.That(ok, Is.EqualTo(expectedOk)); - } - else - { - Assert.False(System.IO.File.Exists(Path.Join(test, "ok.txt"))); - } - } - } - } -} \ No newline at end of file diff --git a/bindings/csharp/Ckzg.Test/ReferenceTests.cs b/bindings/csharp/Ckzg.Test/ReferenceTests.cs new file mode 100644 index 0000000..65b31ea --- /dev/null +++ b/bindings/csharp/Ckzg.Test/ReferenceTests.cs @@ -0,0 +1,337 @@ +using Microsoft.Extensions.FileSystemGlobbing; +using NUnit.Framework; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; + +namespace Ckzg.Test; + +[TestFixture] +public class ReferenceTests +{ + [OneTimeSetUp] + public void Setup() + { + _ts = Ckzg.LoadTrustedSetup("trusted_setup.txt"); + _deserializer = new DeserializerBuilder().WithNamingConvention(CamelCaseNamingConvention.Instance).Build(); + } + + [OneTimeTearDown] + public void Teardown() + { + Ckzg.FreeTrustedSetup(_ts); + } + + [TestCase] + public void TestSetupLoaded() + { + Assert.That(_ts, Is.Not.EqualTo(IntPtr.Zero)); + } + + private IntPtr _ts; + private const string TestDir = "../../../../../../tests"; + private readonly string _blobToKzgCommitmentTests = Path.Join(TestDir, "blob_to_kzg_commitment"); + private readonly string _computeKzgProofTests = Path.Join(TestDir, "compute_kzg_proof"); + private readonly string _computeBlobKzgProofTests = Path.Join(TestDir, "compute_blob_kzg_proof"); + private readonly string _verifyKzgProofTests = Path.Join(TestDir, "verify_kzg_proof"); + private readonly string _verifyBlobKzgProofTests = Path.Join(TestDir, "verify_blob_kzg_proof"); + private readonly string _verifyBlobKzgProofBatchTests = Path.Join(TestDir, "verify_blob_kzg_proof_batch"); + private IDeserializer _deserializer; + + #region Helper Functions + + private static byte[] GetBytes(string hex) + { + return Convert.FromHexString(hex[2..]); + } + + private static byte[] GetFlatBytes(List strings) + { + List stringBytes = strings.Select(GetBytes).ToList(); + + byte[] flatBytes = new byte[stringBytes.Sum(b => b.Length)]; + int offset = 0; + foreach (byte[] bytes in stringBytes) + { + Buffer.BlockCopy(bytes, 0, flatBytes, offset, bytes.Length); + offset += bytes.Length; + } + + return flatBytes; + } + + #endregion + + #region BlobToKzgCommitment + + private class BlobToKzgCommitmentInput + { + public string Blob { get; set; } = null!; + } + + private class BlobToKzgCommitmentTest + { + public BlobToKzgCommitmentInput Input { get; set; } = null!; + public string? Output { get; set; } = null!; + } + + [TestCase] + public void TestBlobToKzgCommitment() + { + Matcher matcher = new(); + matcher.AddIncludePatterns(new[] { "*/*/data.yaml" }); + + foreach (string testFile in matcher.GetResultsInFullPath(_blobToKzgCommitmentTests)) + { + string yaml = File.ReadAllText(testFile); + BlobToKzgCommitmentTest test = _deserializer.Deserialize(yaml); + Assert.That(test, Is.Not.EqualTo(null)); + + byte[] commitment = new byte[48]; + byte[] blob = GetBytes(test.Input.Blob); + + try + { + Ckzg.BlobToKzgCommitment(commitment, blob, _ts); + string? commitmentStr = test.Output; + Assert.That(commitmentStr, Is.Not.EqualTo(null)); + byte[] expectedCommitment = GetBytes(commitmentStr); + Assert.That(commitment, Is.EqualTo(expectedCommitment)); + } + catch + { + Assert.That(test.Output, Is.EqualTo(null)); + } + } + } + + #endregion + + #region ComputeKzgProof + + private class ComputeKzgProofInput + { + public string Blob { get; set; } = null!; + public string Z { get; set; } = null!; + } + + private class ComputeKzgProofTest + { + public ComputeKzgProofInput Input { get; set; } = null!; + public string? Output { get; set; } = null!; + } + + [TestCase] + public void TestComputeKzgProof() + { + Matcher matcher = new(); + matcher.AddIncludePatterns(new[] { "*/*/data.yaml" }); + + foreach (string testFile in matcher.GetResultsInFullPath(_computeKzgProofTests)) + { + string yaml = File.ReadAllText(testFile); + ComputeKzgProofTest test = _deserializer.Deserialize(yaml); + Assert.That(test, Is.Not.EqualTo(null)); + + byte[] proof = new byte[48]; + byte[] blob = GetBytes(test.Input.Blob); + byte[] z = GetBytes(test.Input.Z); + + try + { + Ckzg.ComputeKzgProof(proof, blob, z, _ts); + string? proofStr = test.Output; + Assert.That(proofStr, Is.Not.EqualTo(null)); + byte[] expectedProof = GetBytes(proofStr); + Assert.That(proof, Is.EqualTo(expectedProof)); + } + catch + { + Assert.That(test.Output, Is.EqualTo(null)); + } + } + } + + #endregion + + #region ComputeBlobKzgProof + + private class ComputeBlobKzgProofInput + { + public string Blob { get; set; } = null!; + } + + private class ComputeBlobKzgProofTest + { + public ComputeBlobKzgProofInput Input { get; set; } = null!; + public string? Output { get; set; } = null!; + } + + [TestCase] + public void TestComputeBlobKzgProof() + { + Matcher matcher = new(); + matcher.AddIncludePatterns(new[] { "*/*/data.yaml" }); + + foreach (string testFile in matcher.GetResultsInFullPath(_computeBlobKzgProofTests)) + { + string yaml = File.ReadAllText(testFile); + ComputeBlobKzgProofTest test = _deserializer.Deserialize(yaml); + Assert.That(test, Is.Not.EqualTo(null)); + + byte[] proof = new byte[48]; + byte[] blob = GetBytes(test.Input.Blob); + + try + { + Ckzg.ComputeBlobKzgProof(proof, blob, _ts); + string? proofStr = test.Output; + Assert.That(proofStr, Is.Not.EqualTo(null)); + byte[] expectedProof = GetBytes(proofStr); + Assert.That(proof, Is.EqualTo(expectedProof)); + } + catch + { + Assert.That(test.Output, Is.EqualTo(null)); + } + } + } + + #endregion + + #region VerifyKzgProof + + private class VerifyKzgProofInput + { + public string Commitment { get; set; } = null!; + public string Z { get; set; } = null!; + public string Y { get; set; } = null!; + public string Proof { get; set; } = null!; + } + + private class VerifyKzgProofTest + { + public VerifyKzgProofInput Input { get; set; } = null!; + public bool? Output { get; set; } = null!; + } + + [TestCase] + public void TestVerifyKzgProof() + { + Matcher matcher = new(); + matcher.AddIncludePatterns(new[] { "*/*/data.yaml" }); + + foreach (string testFile in matcher.GetResultsInFullPath(_verifyKzgProofTests)) + { + string yaml = File.ReadAllText(testFile); + VerifyKzgProofTest test = _deserializer.Deserialize(yaml); + Assert.That(test, Is.Not.EqualTo(null)); + + byte[] commitment = GetBytes(test.Input.Commitment); + byte[] z = GetBytes(test.Input.Z); + byte[] y = GetBytes(test.Input.Y); + byte[] proof = GetBytes(test.Input.Proof); + + try + { + bool isCorrect = Ckzg.VerifyKzgProof(commitment, z, y, proof, _ts); + Assert.That(isCorrect, Is.EqualTo(test.Output)); + } + catch + { + Assert.That(test.Output, Is.EqualTo(null)); + } + } + } + + #endregion + + #region VerifyBlobKzgProof + + private class VerifyBlobKzgProofInput + { + public string Blob { get; set; } = null!; + public string Commitment { get; set; } = null!; + public string Proof { get; set; } = null!; + } + + private class VerifyBlobKzgProofTest + { + public VerifyBlobKzgProofInput Input { get; set; } = null!; + public bool? Output { get; set; } = null!; + } + + [TestCase] + public void TestVerifyBlobKzgProof() + { + Matcher matcher = new(); + matcher.AddIncludePatterns(new[] { "*/*/data.yaml" }); + + foreach (string testFile in matcher.GetResultsInFullPath(_verifyBlobKzgProofTests)) + { + string yaml = File.ReadAllText(testFile); + VerifyBlobKzgProofTest test = _deserializer.Deserialize(yaml); + Assert.That(test, Is.Not.EqualTo(null)); + + byte[] blob = GetBytes(test.Input.Blob); + byte[] commitment = GetBytes(test.Input.Commitment); + byte[] proof = GetBytes(test.Input.Proof); + try + { + bool isCorrect = Ckzg.VerifyBlobKzgProof(blob, commitment, proof, _ts); + Assert.That(isCorrect, Is.EqualTo(test.Output)); + } + catch + { + Assert.That(test.Output, Is.EqualTo(null)); + } + } + } + + #endregion + + #region VerifyBlobKzgProofBatch + + private class VerifyBlobKzgProofBatchInput + { + public List Blobs { get; set; } = null!; + public List Commitments { get; set; } = null!; + public List Proofs { get; set; } = null!; + } + + private class VerifyBlobKzgProofBatchTest + { + public VerifyBlobKzgProofBatchInput Input { get; set; } = null!; + public bool? Output { get; set; } = null!; + } + + [TestCase] + public void TestVerifyBlobKzgProofBatch() + { + Matcher matcher = new(); + matcher.AddIncludePatterns(new[] { "*/*/data.yaml" }); + + foreach (string testFile in matcher.GetResultsInFullPath(_verifyBlobKzgProofBatchTests)) + { + string yaml = File.ReadAllText(testFile); + VerifyBlobKzgProofBatchTest test = _deserializer.Deserialize(yaml); + Assert.That(test, Is.Not.EqualTo(null)); + + byte[] blobs = GetFlatBytes(test.Input.Blobs); + byte[] commitments = GetFlatBytes(test.Input.Commitments); + byte[] proofs = GetFlatBytes(test.Input.Proofs); + int count = blobs.Length / Ckzg.BytesPerBlob; + + try + { + bool isCorrect = Ckzg.VerifyBlobKzgProofBatch(blobs, commitments, proofs, count, _ts); + Assert.That(isCorrect, Is.EqualTo(test.Output)); + } + catch + { + Assert.That(test.Output, Is.EqualTo(null)); + } + } + } + + #endregion +} \ No newline at end of file diff --git a/bindings/csharp/Makefile b/bindings/csharp/Makefile index d5fa3dc..af2a782 100644 --- a/bindings/csharp/Makefile +++ b/bindings/csharp/Makefile @@ -1,9 +1,9 @@ ifeq ($(OS),Windows_NT) BLST_BUILDSCRIPT = ./build.bat BLST_OBJ = blst.lib - CSHARP_PLATFORM ?= win-x64 + LOCATION ?= win-x64 CLANG_EXECUTABLE = clang - CKZG_LIBRARY_PATH = Ckzg.Bindings\runtimes\$(CSHARP_PLATFORM)\native\ckzg.dll + CKZG_LIBRARY_PATH = Ckzg.Bindings\runtimes\$(LOCATION)\native\ckzg.dll else BLST_BUILDSCRIPT = ./build.sh BLST_OBJ = libblst.a @@ -13,20 +13,20 @@ else UNAME_M := $(shell uname -m) ifeq ($(UNAME_S),Linux) ifeq ($(UNAME_M),x86_64) - CSHARP_PLATFORM ?= linux-x64 + LOCATION ?= linux-x64 else - CSHARP_PLATFORM ?= linux-arm64 + LOCATION ?= linux-arm64 endif endif ifeq ($(UNAME_S),Darwin) ifeq ($(UNAME_M),arm64) - CSHARP_PLATFORM ?= osx-arm64 + LOCATION ?= osx-arm64 else - CSHARP_PLATFORM ?= osx-x64 + LOCATION ?= osx-x64 endif endif - CKZG_LIBRARY_PATH = Ckzg.Bindings/runtimes/$(CSHARP_PLATFORM)/native/ckzg.so + CKZG_LIBRARY_PATH = Ckzg.Bindings/runtimes/$(LOCATION)/native/ckzg.so endif FIELD_ELEMENTS_PER_BLOB ?= 4096 @@ -36,13 +36,17 @@ TARGETS = ckzg.c ../../src/c_kzg_4844.c ../../blst/$(BLST_OBJ) CFLAGS += -O2 -Wall -Wextra -shared CFLAGS += -DFIELD_ELEMENTS_PER_BLOB=$(FIELD_ELEMENTS_PER_BLOB) CFLAGS += ${addprefix -I,${INCLUDE_DIRS}} +ifdef ARCH + CFLAGS += --target=$(ARCH) + BLST_BUILDSCRIPT_FLAGS += --target=$(ARCH) +endif .PHONY: all all: blst ckzg ckzg-dotnet .PHONY: blst blst: - cd ../../blst && $(BLST_BUILDSCRIPT) + cd ../../blst && $(BLST_BUILDSCRIPT) $(BLST_BUILDSCRIPT_FLAGS) .PHONY: ckzg ckzg: blst diff --git a/bindings/csharp/kzg_tests.c b/bindings/csharp/kzg_tests.c deleted file mode 100644 index 9f39542..0000000 --- a/bindings/csharp/kzg_tests.c +++ /dev/null @@ -1,45 +0,0 @@ -// RUN: make run-test -#include "ckzg.h" -#include - -void calculate_proof_and_commitment(char * trusted_setup_path){ - KZGSettings *s = load_trusted_setup_wrap(trusted_setup_path); - size_t n = 1; - uint8_t *commitment = (uint8_t *)calloc(48, 1); - uint8_t *proof = (uint8_t *)calloc(48, 1); - uint8_t *blob = (uint8_t *)calloc(4096, 32); - uint8_t *blobHash = (uint8_t *)calloc(32, 1); - n = 0; - for(int i = 0; i < 5875; i++){ - if((n + 1) % 32 == 0)n++; - blob[n] = i % 250; - n++; - } - int res0 = compute_aggregate_kzg_proof(proof, blob, 1, s); - int res1 = blob_to_kzg_commitment(commitment, blob, s); - - FILE *f = fopen("output.txt", "wt"); - // commitment - for(int i = 0; i < 48; i++){ - fprintf(f, "%02x", commitment[i]); - } - fprintf(f, "\n"); - - // proof - for(int i = 0; i < 48; i++){ - fprintf(f, "%02x", proof[i]); - } - fprintf(f, "\n"); - - fclose(f); - free(blob); - free(commitment); - free(proof); - free_trusted_setup_wrap(s); -} - - -int main() { - calculate_proof_and_commitment("../../src/trusted_setup.txt"); - return 0; -} \ No newline at end of file