// SPDX-Licence-Identifier: EUPL-1.2 package crypto_test import ( "encoding/hex" "testing" "dappco.re/go/core/blockchain/crypto" ) func TestFastHash_Good_KnownVector(t *testing.T) { // Empty input → known Keccak-256 hash. input := []byte{} expected := "c5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470" got := crypto.FastHash(input) if hex.EncodeToString(got[:]) != expected { t.Fatalf("FastHash(empty)\n got: %x\n want: %s", got, expected) } } func TestFastHash_Good_HelloWorld(t *testing.T) { input := []byte("Hello, World!") got := crypto.FastHash(input) var zero [32]byte if got == zero { t.Fatal("FastHash returned zero hash") } } func TestGenerateKeys_Good_Roundtrip(t *testing.T) { pub, sec, err := crypto.GenerateKeys() if err != nil { t.Fatalf("GenerateKeys: %v", err) } if !crypto.CheckKey(pub) { t.Fatal("generated public key failed CheckKey") } pub2, err := crypto.SecretToPublic(sec) if err != nil { t.Fatalf("SecretToPublic: %v", err) } if pub != pub2 { t.Fatalf("SecretToPublic mismatch:\n GenerateKeys: %x\n SecretToPublic: %x", pub, pub2) } } func TestCheckKey_Bad_Invalid(t *testing.T) { // A random 32-byte value is overwhelmingly unlikely to be a valid curve point. bad := [32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} if crypto.CheckKey(bad) { t.Fatal("0xFF...FF should fail CheckKey") } } func TestGenerateKeys_Good_Unique(t *testing.T) { pub1, _, _ := crypto.GenerateKeys() pub2, _, _ := crypto.GenerateKeys() if pub1 == pub2 { t.Fatal("two GenerateKeys calls returned identical public keys") } } // ── Key Derivation ──────────────────────────────────────── func TestKeyDerivation_Good_Roundtrip(t *testing.T) { // Alice and Bob generate key pairs; shared derivation must match. pubA, secA, err := crypto.GenerateKeys() if err != nil { t.Fatalf("GenerateKeys (Alice): %v", err) } pubB, secB, err := crypto.GenerateKeys() if err != nil { t.Fatalf("GenerateKeys (Bob): %v", err) } // D = secA * pubB must equal secB * pubA (ECDH commutativity). dAB, err := crypto.GenerateKeyDerivation(pubB, secA) if err != nil { t.Fatalf("GenerateKeyDerivation(pubB, secA): %v", err) } dBA, err := crypto.GenerateKeyDerivation(pubA, secB) if err != nil { t.Fatalf("GenerateKeyDerivation(pubA, secB): %v", err) } if dAB != dBA { t.Fatalf("ECDH mismatch:\n dAB: %x\n dBA: %x", dAB, dBA) } } func TestDerivePublicKey_Good_OutputScanning(t *testing.T) { // Simulate one-time address generation and scanning. // Sender knows (txPub, recipientPub). Recipient knows (txPub, recipientSec). recipientPub, recipientSec, err := crypto.GenerateKeys() if err != nil { t.Fatalf("GenerateKeys (recipient): %v", err) } txPub, txSec, err := crypto.GenerateKeys() if err != nil { t.Fatalf("GenerateKeys (tx): %v", err) } // Sender derives ephemeral output key. derivSender, err := crypto.GenerateKeyDerivation(recipientPub, txSec) if err != nil { t.Fatalf("sender derivation: %v", err) } ephPub, err := crypto.DerivePublicKey(derivSender, 0, recipientPub) if err != nil { t.Fatalf("DerivePublicKey: %v", err) } if !crypto.CheckKey(ephPub) { t.Fatal("ephemeral public key failed CheckKey") } // Recipient re-derives and must get the same key. derivRecipient, err := crypto.GenerateKeyDerivation(txPub, recipientSec) if err != nil { t.Fatalf("recipient derivation: %v", err) } ephPub2, err := crypto.DerivePublicKey(derivRecipient, 0, recipientPub) if err != nil { t.Fatalf("DerivePublicKey (recipient): %v", err) } if ephPub != ephPub2 { t.Fatalf("output scanning mismatch:\n sender: %x\n recipient: %x", ephPub, ephPub2) } // Recipient derives the secret key for spending. ephSec, err := crypto.DeriveSecretKey(derivRecipient, 0, recipientSec) if err != nil { t.Fatalf("DeriveSecretKey: %v", err) } // Verify: ephSec → ephPub must match. ephPub3, err := crypto.SecretToPublic(ephSec) if err != nil { t.Fatalf("SecretToPublic(ephSec): %v", err) } if ephPub != ephPub3 { t.Fatalf("ephemeral key pair mismatch:\n derived pub: %x\n sec→pub: %x", ephPub, ephPub3) } } // ── Key Images ──────────────────────────────────────────── func TestKeyImage_Good_Roundtrip(t *testing.T) { pub, sec, err := crypto.GenerateKeys() if err != nil { t.Fatalf("GenerateKeys: %v", err) } ki, err := crypto.GenerateKeyImage(pub, sec) if err != nil { t.Fatalf("GenerateKeyImage: %v", err) } var zero [32]byte if ki == zero { t.Fatal("key image is zero") } if !crypto.ValidateKeyImage(ki) { t.Fatal("generated key image failed validation") } } func TestKeyImage_Good_Deterministic(t *testing.T) { pub, sec, err := crypto.GenerateKeys() if err != nil { t.Fatalf("GenerateKeys: %v", err) } ki1, err := crypto.GenerateKeyImage(pub, sec) if err != nil { t.Fatalf("GenerateKeyImage (1): %v", err) } ki2, err := crypto.GenerateKeyImage(pub, sec) if err != nil { t.Fatalf("GenerateKeyImage (2): %v", err) } if ki1 != ki2 { t.Fatalf("key image not deterministic:\n ki1: %x\n ki2: %x", ki1, ki2) } } func TestKeyImage_Bad_Invalid(t *testing.T) { // 0xFF...FF is not a valid curve point, so not a valid key image. bad := [32]byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF} if crypto.ValidateKeyImage(bad) { t.Fatal("0xFF...FF should fail ValidateKeyImage") } } // ── Standard Signatures ────────────────────────────────── func TestSignature_Good_Roundtrip(t *testing.T) { pub, sec, _ := crypto.GenerateKeys() // Sign a message hash. msg := crypto.FastHash([]byte("test message")) sig, err := crypto.GenerateSignature(msg, pub, sec) if err != nil { t.Fatalf("GenerateSignature: %v", err) } // Verify with correct key. if !crypto.CheckSignature(msg, pub, sig) { t.Fatal("valid signature failed verification") } } func TestSignature_Bad_WrongKey(t *testing.T) { pub, sec, _ := crypto.GenerateKeys() pub2, _, _ := crypto.GenerateKeys() msg := crypto.FastHash([]byte("test")) sig, _ := crypto.GenerateSignature(msg, pub, sec) // Verify with wrong public key should fail. if crypto.CheckSignature(msg, pub2, sig) { t.Fatal("signature verified with wrong public key") } } func TestSignature_Bad_WrongMessage(t *testing.T) { pub, sec, _ := crypto.GenerateKeys() msg1 := crypto.FastHash([]byte("message 1")) msg2 := crypto.FastHash([]byte("message 2")) sig, _ := crypto.GenerateSignature(msg1, pub, sec) if crypto.CheckSignature(msg2, pub, sig) { t.Fatal("signature verified with wrong message") } } // ── Ring Signatures (NLSAG) ───────────────────────────── func TestRingSignature_Good_Roundtrip(t *testing.T) { // Create a ring of 4 public keys. The real signer is at index 1. ringSize := 4 realIndex := 1 pubs := make([][32]byte, ringSize) var realSec [32]byte for i := 0; i < ringSize; i++ { pub, sec, _ := crypto.GenerateKeys() pubs[i] = pub if i == realIndex { realSec = sec } } // Generate key image for the real key. ki, _ := crypto.GenerateKeyImage(pubs[realIndex], realSec) // Sign. msg := crypto.FastHash([]byte("ring sig test")) sigs, err := crypto.GenerateRingSignature(msg, ki, pubs, realSec, realIndex) if err != nil { t.Fatalf("GenerateRingSignature: %v", err) } // Verify. if !crypto.CheckRingSignature(msg, ki, pubs, sigs) { t.Fatal("valid ring signature failed verification") } } func TestRingSignature_Bad_WrongMessage(t *testing.T) { pubs := make([][32]byte, 3) var sec [32]byte for i := range pubs { pub, s, _ := crypto.GenerateKeys() pubs[i] = pub if i == 0 { sec = s } } ki, _ := crypto.GenerateKeyImage(pubs[0], sec) msg1 := crypto.FastHash([]byte("msg1")) msg2 := crypto.FastHash([]byte("msg2")) sigs, _ := crypto.GenerateRingSignature(msg1, ki, pubs, sec, 0) if crypto.CheckRingSignature(msg2, ki, pubs, sigs) { t.Fatal("ring signature verified with wrong message") } } // ── CLSAG ──────────────────────────────────────────────── func TestCLSAG_GG_Good_Roundtrip(t *testing.T) { // CLSAG_GG is a 2-dimensional linkable ring signature: // Layer 0: stealth addresses (P_i), secret_x for real signer // Layer 1: commitment difference (A_i - pseudo_out), secret_f // // Ring commitments are stored premultiplied by 1/8 (on-chain form). // generate takes pseudo_out as the FULL point (not premultiplied). // verify takes pseudo_out as the PREMULTIPLIED form. // // When pseudo_out matches the real commitment: secret_f = 0. // generate pseudo_out = 8 * ring_commitment (full point). // verify pseudo_out = ring_commitment (premultiplied form, as stored). ringSize := 4 realIndex := 2 ring := make([]byte, ringSize*64) var realStealthSec [32]byte var secretF [32]byte // zero — pseudo_out matches real commitment var ki [32]byte for i := 0; i < ringSize; i++ { pub, sec, _ := crypto.GenerateKeys() copy(ring[i*64:], pub[:]) cPub, _, _ := crypto.GenerateKeys() // Store commitment as-is. CLSAG treats this as premultiplied by 1/8. copy(ring[i*64+32:], cPub[:]) if i == realIndex { realStealthSec = sec var err error ki, err = crypto.GenerateKeyImage(pub, sec) if err != nil { t.Fatalf("GenerateKeyImage: %v", err) } } } // For generate: pseudo_out = 8 * commitment (full point). var commitmentPremul [32]byte copy(commitmentPremul[:], ring[realIndex*64+32:realIndex*64+64]) pseudoOutFull, err := crypto.PointMul8(commitmentPremul) if err != nil { t.Fatalf("PointMul8: %v", err) } msg := crypto.FastHash([]byte("clsag gg test")) sig, err := crypto.GenerateCLSAGGG(msg, ring, ringSize, pseudoOutFull, ki, realStealthSec, secretF, realIndex) if err != nil { t.Fatalf("GenerateCLSAGGG: %v", err) } expectedSize := crypto.CLSAGGGSigSize(ringSize) if len(sig) != expectedSize { t.Fatalf("sig size: got %d, want %d", len(sig), expectedSize) } // For verify: pseudo_out = commitment (premultiplied form). if !crypto.VerifyCLSAGGG(msg, ring, ringSize, commitmentPremul, ki, sig) { t.Fatal("valid CLSAG_GG signature failed verification") } } func TestCLSAG_GG_Bad_WrongMessage(t *testing.T) { ringSize := 3 realIndex := 0 ring := make([]byte, ringSize*64) var realStealthSec [32]byte var secretF [32]byte var ki [32]byte for i := 0; i < ringSize; i++ { pub, sec, _ := crypto.GenerateKeys() copy(ring[i*64:], pub[:]) cPub, _, _ := crypto.GenerateKeys() copy(ring[i*64+32:], cPub[:]) if i == realIndex { realStealthSec = sec ki, _ = crypto.GenerateKeyImage(pub, sec) } } var commitmentPremul [32]byte copy(commitmentPremul[:], ring[realIndex*64+32:realIndex*64+64]) pseudoOutFull, _ := crypto.PointMul8(commitmentPremul) msg1 := crypto.FastHash([]byte("msg1")) msg2 := crypto.FastHash([]byte("msg2")) sig, _ := crypto.GenerateCLSAGGG(msg1, ring, ringSize, pseudoOutFull, ki, realStealthSec, secretF, realIndex) if crypto.VerifyCLSAGGG(msg2, ring, ringSize, commitmentPremul, ki, sig) { t.Fatal("CLSAG_GG verified with wrong message") } } func TestCLSAG_GGX_Good_SigSize(t *testing.T) { // Verify sig size calculation is consistent. if crypto.CLSAGGGXSigSize(4) != 32+4*64+64 { t.Fatalf("GGX sig size for ring=4: got %d, want %d", crypto.CLSAGGGXSigSize(4), 32+4*64+64) } } func TestCLSAG_GGXXG_Good_SigSize(t *testing.T) { // Verify sig size calculation is consistent. if crypto.CLSAGGGXXGSigSize(4) != 32+4*64+128 { t.Fatalf("GGXXG sig size for ring=4: got %d, want %d", crypto.CLSAGGGXXGSigSize(4), 32+4*64+128) } } // ── Range Proofs (BPP — Bulletproofs++) ────────────────── func TestBPP_Bad_EmptyProof(t *testing.T) { commitment := [32]byte{0x01} if crypto.VerifyBPP([]byte{}, [][32]byte{commitment}) { t.Fatal("empty BPP proof should fail") } } func TestBPP_Bad_GarbageProof(t *testing.T) { // Build a minimal valid-shaped proof: L(0) + R(0) + 6 * 32-byte fields. proof := make([]byte, 0, 2+6*32) proof = append(proof, 0x00) // varint 0: L length proof = append(proof, 0x00) // varint 0: R length proof = append(proof, make([]byte, 6*32)...) commitment := [32]byte{0x01} if crypto.VerifyBPP(proof, [][32]byte{commitment}) { t.Fatal("garbage BPP proof should fail verification") } } func TestBPP_Good_TestnetCoinbase101(t *testing.T) { // Real BPP range proof from testnet block 101 (first post-HF4 coinbase). // TX hash: 543bc3c29e9f4c5d1fc566be03fb4da1f2ce2d70d4312fdcc3e4eed7ca3b61e0 // // This is the zc_outs_range_proof.bpp field, verified by bpp_crypto_trait_ZC_out. // Commitments are the amount_commitments_for_rp_aggregation (E'_j) from the // aggregation proof, NOT the raw output amount_commitments. proofHex := "07" + // L[0..6] "47c3d2db565bf368c9879dd1b08899a5e69bc956bc0a89b0cb456a54e066aa85" + "89c6a14ac578409af177d9605f03fe61dfae8067338a40ca551b34580350f694" + "bf389b62ae684146d33632e1ca5bf51dc6fed884780443489ee8fd68a535ddcf" + "5299c9ca2f400fd0a978b1c0ef55a03922549ef78b8b5cd268bd4df5eb32f1fc" + "4d4543466be7e9b9ceb6051955a815427bd773ad9a5cc9fec49fae4c0ab46fc7" + "53d1c86e8c04cea4b903fc8c42f404bc8c85b25eb2468f8de75171e2bf802ae9" + "6daa934cdb68b0609eb8942b3691c2bf1a122068bcd18d8ef4dd08abb2929780" + "07" + // R count // R[0..6] "599626cfd549b317eb92667ab1783a730cab6528326b581034a60b8f2582ff61" + "d8ac1ec8395699a170dc9ca5ae2a87cd70388083475e38763aa327e31b27bd69" + "1ebd37e22b4eda43001e908fdb211de548ed942766139c8197201d9cc53c744a" + "88ee20995c5f0b64d1bc48293f9c3b8799b2866e473915871df9d55ac065c58e" + "bd51887763709d9e9992d317c12bd27ad933452d06b821b4ee282de78e7cc561" + "02ad119d2f1fb9a79a709614cb24dcb83119bb5734a70c923c4c9586afe1e5bd" + "fedc244f7568a3cd9de95d9d240fb01ee0e0695f6d2066d085457054e78dead4" + // A0 "b47ed0fcf2de7c5a9802352f7ab65667c468e32329964723a2084ae0dd38ae97" + // A "79ff7e0870d6b664275b8207e545f4f80537e7cc4c9f81098eefa42e5efc1c85" + // B "86bc9e7a48652c1f1e1e28e86b6e7ea80079dd6db7c78d083235ede6ae359631" + // r "dbcb543d3a6e8b8692fb013832acb9b93ee717c72e832fd78c3a2d5f4fcb380f" + // s "e3e21825ce80bda2c30b499ae87ce5e9daedde3f8885bcd7463fccaa88d37500" + // delta "7c8c94c0f95faa4d0c9e14811442ecb3dc78bd46dafba93e648324b7d0a36d0c" proof, err := hex.DecodeString(proofHex) if err != nil { t.Fatalf("decode proof hex: %v", err) } // Aggregation proof commitments (E'_j, premultiplied by 1/8). var c0, c1 [32]byte h0, _ := hex.DecodeString("c61a3937e37ff91acd74bd2877bb47e236c36315744a8031339a02c41481d52b") h1, _ := hex.DecodeString("5157b6954e712187a14edcd6faf0e6adbb8ec66f2d9260bd238106e076d3098e") copy(c0[:], h0) copy(c1[:], h1) if !crypto.VerifyBPP(proof, [][32]byte{c0, c1}) { t.Fatal("real testnet BPP proof should verify successfully") } } func TestBPP_Bad_TestnetWrongCommitment(t *testing.T) { // Same proof as above but with corrupted commitment — must fail. proofHex := "07" + "47c3d2db565bf368c9879dd1b08899a5e69bc956bc0a89b0cb456a54e066aa85" + "89c6a14ac578409af177d9605f03fe61dfae8067338a40ca551b34580350f694" + "bf389b62ae684146d33632e1ca5bf51dc6fed884780443489ee8fd68a535ddcf" + "5299c9ca2f400fd0a978b1c0ef55a03922549ef78b8b5cd268bd4df5eb32f1fc" + "4d4543466be7e9b9ceb6051955a815427bd773ad9a5cc9fec49fae4c0ab46fc7" + "53d1c86e8c04cea4b903fc8c42f404bc8c85b25eb2468f8de75171e2bf802ae9" + "6daa934cdb68b0609eb8942b3691c2bf1a122068bcd18d8ef4dd08abb2929780" + "07" + "599626cfd549b317eb92667ab1783a730cab6528326b581034a60b8f2582ff61" + "d8ac1ec8395699a170dc9ca5ae2a87cd70388083475e38763aa327e31b27bd69" + "1ebd37e22b4eda43001e908fdb211de548ed942766139c8197201d9cc53c744a" + "88ee20995c5f0b64d1bc48293f9c3b8799b2866e473915871df9d55ac065c58e" + "bd51887763709d9e9992d317c12bd27ad933452d06b821b4ee282de78e7cc561" + "02ad119d2f1fb9a79a709614cb24dcb83119bb5734a70c923c4c9586afe1e5bd" + "fedc244f7568a3cd9de95d9d240fb01ee0e0695f6d2066d085457054e78dead4" + "b47ed0fcf2de7c5a9802352f7ab65667c468e32329964723a2084ae0dd38ae97" + "79ff7e0870d6b664275b8207e545f4f80537e7cc4c9f81098eefa42e5efc1c85" + "86bc9e7a48652c1f1e1e28e86b6e7ea80079dd6db7c78d083235ede6ae359631" + "dbcb543d3a6e8b8692fb013832acb9b93ee717c72e832fd78c3a2d5f4fcb380f" + "e3e21825ce80bda2c30b499ae87ce5e9daedde3f8885bcd7463fccaa88d37500" + "7c8c94c0f95faa4d0c9e14811442ecb3dc78bd46dafba93e648324b7d0a36d0c" proof, _ := hex.DecodeString(proofHex) // Corrupted commitment (flipped first byte). var c0, c1 [32]byte h0, _ := hex.DecodeString("d61a3937e37ff91acd74bd2877bb47e236c36315744a8031339a02c41481d52b") h1, _ := hex.DecodeString("5157b6954e712187a14edcd6faf0e6adbb8ec66f2d9260bd238106e076d3098e") copy(c0[:], h0) copy(c1[:], h1) if crypto.VerifyBPP(proof, [][32]byte{c0, c1}) { t.Fatal("BPP proof with corrupted commitment should fail") } } // ── Range Proofs (BPPE — Bulletproofs++ Enhanced) ──────── func TestBPPE_Bad_EmptyProof(t *testing.T) { // Empty proof must return false (not crash). commitment := [32]byte{0x01} if crypto.VerifyBPPE([]byte{}, [][32]byte{commitment}) { t.Fatal("empty BPPE proof should fail") } } func TestBPPE_Bad_GarbageProof(t *testing.T) { // Garbage bytes should deserialise (valid varint + blobs) but fail verification. // Build a minimal valid-shaped proof: L(0 entries) + R(0 entries) + 7 * 32-byte fields. proof := make([]byte, 0, 2+7*32) proof = append(proof, 0x00) // varint 0: L length proof = append(proof, 0x00) // varint 0: R length proof = append(proof, make([]byte, 7*32)...) commitment := [32]byte{0x01} if crypto.VerifyBPPE(proof, [][32]byte{commitment}) { t.Fatal("garbage BPPE proof should fail verification") } } func TestBGE_Bad_EmptyProof(t *testing.T) { ctx := [32]byte{0x01} ring := [][32]byte{{0x02}} if crypto.VerifyBGE(ctx, ring, []byte{}) { t.Fatal("empty BGE proof should fail") } } func TestBGE_Bad_GarbageProof(t *testing.T) { // Build a minimal valid-shaped proof: A(32) + B(32) + Pk(0) + f(0) + y(32) + z(32). proof := make([]byte, 0, 4*32+2) proof = append(proof, make([]byte, 32)...) // A proof = append(proof, make([]byte, 32)...) // B proof = append(proof, 0x00) // varint 0: Pk length proof = append(proof, 0x00) // varint 0: f length proof = append(proof, make([]byte, 32)...) // y proof = append(proof, make([]byte, 32)...) // z ctx := [32]byte{0x01} ring := [][32]byte{{0x02}} if crypto.VerifyBGE(ctx, ring, proof) { t.Fatal("garbage BGE proof should fail verification") } } func TestZarcanumCompatibilityWrapper_Bad_EmptyProof(t *testing.T) { hash := [32]byte{0x01} if crypto.VerifyZarcanum(hash, []byte{0x00}) { t.Fatal("compatibility wrapper should reject malformed proof data") } } func TestZarcanumWithContext_Bad_MinimalProof(t *testing.T) { var ctx crypto.ZarcanumVerificationContext ctx.ContextHash = [32]byte{0x01} ctx.KernelHash = [32]byte{0x02} ctx.LastPowBlockIDHashed = [32]byte{0x03} ctx.StakeKeyImage = [32]byte{0x04} ctx.PosDifficulty = 1 ctx.Ring = []crypto.ZarcanumRingMember{{ StealthAddress: [32]byte{0x11}, AmountCommitment: [32]byte{0x22}, BlindedAssetID: [32]byte{0x33}, ConcealingPoint: [32]byte{0x44}, }} // Minimal structurally valid proof blob: // 10 scalars/points + empty BPPE + pseudo_out_amount_commitment + // CLSAG_GGXXG with one ring entry and zeroed scalars. proof := make([]byte, 0, 10*32+2+32+2+32+1+128) proof = append(proof, make([]byte, 10*32)...) proof = append(proof, 0x00) // BPPE L length proof = append(proof, 0x00) // BPPE R length proof = append(proof, make([]byte, 7*32)...) proof = append(proof, make([]byte, 32)...) proof = append(proof, 0x01) // CLSAG_GGXXG r_g length proof = append(proof, make([]byte, 32)...) proof = append(proof, 0x01) // CLSAG_GGXXG r_x length proof = append(proof, make([]byte, 32)...) proof = append(proof, make([]byte, 128)...) ctx.Proof = proof if crypto.VerifyZarcanumWithContext(ctx) { t.Fatal("minimal Zarcanum proof should fail verification") } } func TestDoubleSchnorr_Bad_EmptyProof(t *testing.T) { var hash, a, b [32]byte if crypto.VerifyDoubleSchnorr(hash, true, a, b, nil) { t.Fatal("empty double-Schnorr proof should fail") } } func TestDoubleSchnorr_Good_Roundtrip(t *testing.T) { hash := crypto.FastHash([]byte("double-schnorr")) _, secretA, err := crypto.GenerateKeys() if err != nil { t.Fatalf("GenerateKeys(secretA): %v", err) } pubA, err := crypto.SecretToPublic(secretA) if err != nil { t.Fatalf("SecretToPublic(secretA): %v", err) } _, secretB, err := crypto.GenerateKeys() if err != nil { t.Fatalf("GenerateKeys(secretB): %v", err) } pubB, err := crypto.SecretToPublic(secretB) if err != nil { t.Fatalf("SecretToPublic(secretB): %v", err) } proof, err := crypto.GenerateDoubleSchnorr(hash, false, secretA, secretB) if err != nil { t.Fatalf("GenerateDoubleSchnorr: %v", err) } if !crypto.VerifyDoubleSchnorr(hash, false, pubA, pubB, proof[:]) { t.Fatal("generated double-Schnorr proof failed verification") } }