go-blockchain/crypto/crypto_test.go
Virgil cb43082d18
Some checks are pending
Security Scan / security (push) Waiting to run
Test / Test (push) Waiting to run
feat(crypto): add Zarcanum verification context API
Co-Authored-By: Charon <charon@lethean.io>
2026-04-04 21:28:12 +00:00

659 lines
21 KiB
Go

// 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")
}
}