import * as anchor from "@coral-xyz/anchor"; import { SystemProgram, Transaction } from "@solana/web3.js"; import { expect } from "chai"; import fetch from "node-fetch"; import { getTestContext } from "../setup"; describe("Initialize Verifying Key (API)", () => { it("Initialize VK and store on-chain via API", async () => { const ctx = getTestContext(); const { connection, program, axumBaseUrl, globalAdmin } = ctx; // Derive PDAs const [primaryMarketPda] = anchor.web3.PublicKey.findProgramAddressSync( [Buffer.from("primary_market")], program.programId ); const [verifyingKeyPda] = anchor.web3.PublicKey.findProgramAddressSync( [Buffer.from("verifying_key")], program.programId ); console.log("Primary Market PDA:", primaryMarketPda.toBase58()); console.log("Verifying Key PDA:", verifyingKeyPda.toBase58()); // Step 1: Verify primary market is initialized const primaryMarketBefore = await program.account.primaryMarket.fetch(primaryMarketPda); expect(primaryMarketBefore.verifyingKey).to.be.null; console.log("✓ Primary market exists and VK not yet initialized"); // Step 2: Create API request payload const request = { primary_market: primaryMarketPda.toBase58(), verifying_key: verifyingKeyPda.toBase58(), global_admin: globalAdmin.publicKey.toBase58(), system_program: SystemProgram.programId.toBase58(), }; console.log("Calling API to create initialize VK transaction..."); console.log("Note: This will load VK from backend's AppState (no disk I/O)"); // Step 3: Call Axum API to get unsigned transaction const createTxResponse = await fetch( `${axumBaseUrl}/api/primary-market/initialize-vk`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(request), } ); if (!createTxResponse.ok) { const error = await createTxResponse.text(); throw new Error(`Failed to create transaction: ${error}`); } const { transaction_base64, message } = await createTxResponse.json(); console.log(`API Response: ${message}`); // Step 4: Deserialize and sign transaction const txBuffer = Buffer.from(transaction_base64, "base64"); const transaction = Transaction.from(txBuffer); console.log("Transaction signer (expected):"); console.log(" Global Admin:", globalAdmin.publicKey.toBase58()); // Sign with globalAdmin (payer and authority) transaction.sign(globalAdmin); console.log("Transaction signed locally"); // Step 5: Submit signed transaction to backend const signedTxBase64 = transaction.serialize().toString("base64"); const submitTxResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction_base64: signedTxBase64 }), }); if (!submitTxResponse.ok) { const error = await submitTxResponse.text(); throw new Error(`Failed to submit transaction: ${error}`); } const { signature } = await submitTxResponse.json(); console.log(`✓ Transaction signature: ${signature}`); // Step 6: Verify verifying key account state const vkAccount = await program.account.verifyingKey.fetch(verifyingKeyPda); expect(vkAccount.isInitialized).to.be.true; expect(vkAccount.bump).to.be.a("number"); // VK components should be non-zero byte arrays expect(vkAccount.vkAlphaG1).to.be.an("array").with.lengthOf(64); expect(vkAccount.vkBetaG2).to.be.an("array").with.lengthOf(128); expect(vkAccount.vkGammaG2).to.be.an("array").with.lengthOf(128); expect(vkAccount.vkDeltaG2).to.be.an("array").with.lengthOf(128); // Check that VK components are not all zeros (actual cryptographic data) const hasNonZeroAlpha = vkAccount.vkAlphaG1.some((byte: number) => byte !== 0); const hasNonZeroBeta = vkAccount.vkBetaG2.some((byte: number) => byte !== 0); const hasNonZeroGamma = vkAccount.vkGammaG2.some((byte: number) => byte !== 0); const hasNonZeroDelta = vkAccount.vkDeltaG2.some((byte: number) => byte !== 0); expect(hasNonZeroAlpha).to.be.true; expect(hasNonZeroBeta).to.be.true; expect(hasNonZeroGamma).to.be.true; expect(hasNonZeroDelta).to.be.true; console.log("✓ Verifying key account state verified"); console.log(` - Is Initialized: ${vkAccount.isInitialized}`); console.log(` - Alpha G1 length: ${vkAccount.vkAlphaG1.length} bytes`); console.log(` - Beta G2 length: ${vkAccount.vkBetaG2.length} bytes`); console.log(` - Gamma G2 length: ${vkAccount.vkGammaG2.length} bytes`); console.log(` - Delta G2 length: ${vkAccount.vkDeltaG2.length} bytes`); console.log(` - Bump: ${vkAccount.bump}`); // Step 7: Verify primary market now references VK const primaryMarketAfter = await program.account.primaryMarket.fetch(primaryMarketPda); expect(primaryMarketAfter.verifyingKey).to.not.be.null; expect(primaryMarketAfter.verifyingKey.equals(verifyingKeyPda)).to.be.true; console.log("✓ Primary market now references verifying key"); console.log(` - VK Address: ${primaryMarketAfter.verifyingKey.toBase58()}`); // Step 8: Verify events/logs const txDetails = await connection.getTransaction(signature, { commitment: "confirmed", maxSupportedTransactionVersion: 0, }); if (txDetails?.meta?.logMessages) { const logs = txDetails.meta.logMessages; // Check for VK initialization message const hasVKInitLog = logs.some((log) => log.includes("Verifying key initialized") ); if (hasVKInitLog) { console.log("✓ Verifying key initialized log message found"); } // Check for event emission const hasEventLog = logs.some((log) => log.includes("VerifyingKeyInitialized") ); if (hasEventLog) { console.log("✓ VerifyingKeyInitialized event emitted"); } } console.log("✓ Verifying key initialized and stored on-chain successfully via API"); // Step 9: Test double initialization should fail console.log("\nTesting double initialization protection..."); const doubleInitResponse = await fetch( `${axumBaseUrl}/api/primary_market/initialize_vk`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(request), } ); // This should still create a transaction, but it will fail on-chain if (doubleInitResponse.ok) { const { transaction_base64: doubleInitTx } = await doubleInitResponse.json(); const doubleTxBuffer = Buffer.from(doubleInitTx, "base64"); const doubleTransaction = Transaction.from(doubleTxBuffer); doubleTransaction.sign(globalAdmin); const doubleSignedTxBase64 = doubleTransaction.serialize().toString("base64"); const doubleSubmitResponse = await fetch(`${axumBaseUrl}/api/submit-tx`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ transaction_base64: doubleSignedTxBase64 }), }); // Should fail with VKAlreadyInitialized error expect(doubleSubmitResponse.ok).to.be.false; const errorText = await doubleSubmitResponse.text(); console.log("✓ Double initialization correctly rejected:", errorText); } }); });