// SPDX-Licence-Identifier: EUPL-1.2 package crypto /* #include "bridge.h" */ import "C" import ( "unsafe" coreerr "dappco.re/go/core/log" ) // ZarcanumRingMember is one flat ring entry for Zarcanum verification. // All fields are stored premultiplied by 1/8, matching the on-chain form. type ZarcanumRingMember struct { StealthAddress [32]byte AmountCommitment [32]byte BlindedAssetID [32]byte ConcealingPoint [32]byte } // ZarcanumVerificationContext groups the full context required by the // upstream C++ verifier. type ZarcanumVerificationContext struct { ContextHash [32]byte KernelHash [32]byte Ring []ZarcanumRingMember LastPowBlockIDHashed [32]byte StakeKeyImage [32]byte Proof []byte PosDifficulty uint64 } // GenerateDoubleSchnorr creates a generic_double_schnorr_sig from zarcanum.h. // aIsX selects the generator pair: // // false -> (G, G) // true -> (X, G) func GenerateDoubleSchnorr(hash [32]byte, aIsX bool, secretA [32]byte, secretB [32]byte) ([96]byte, error) { var proof [96]byte var flag C.int if aIsX { flag = 1 } rc := C.cn_double_schnorr_generate( flag, (*C.uint8_t)(unsafe.Pointer(&hash[0])), (*C.uint8_t)(unsafe.Pointer(&secretA[0])), (*C.uint8_t)(unsafe.Pointer(&secretB[0])), (*C.uint8_t)(unsafe.Pointer(&proof[0])), C.size_t(len(proof)), ) if rc != 0 { return proof, coreerr.E("GenerateDoubleSchnorr", "double_schnorr_generate failed", nil) } return proof, nil } // VerifyBPP verifies a Bulletproofs++ range proof (1 delta). // Used for zc_outs_range_proof in post-HF4 transactions. // proof is the wire-serialised bpp_signature blob. // commitments are the amount_commitments_for_rp_aggregation (E'_j, premultiplied by 1/8). // Uses bpp_crypto_trait_ZC_out (generators UGX, N=64, values_max=32). func VerifyBPP(proof []byte, commitments [][32]byte) bool { if len(proof) == 0 || len(commitments) == 0 { return false } n := len(commitments) flat := make([]byte, n*32) for i, c := range commitments { copy(flat[i*32:], c[:]) } return C.cn_bpp_verify( (*C.uint8_t)(unsafe.Pointer(&proof[0])), C.size_t(len(proof)), (*C.uint8_t)(unsafe.Pointer(&flat[0])), C.size_t(n), ) == 0 } // VerifyBPPE verifies a Bulletproofs++ Enhanced range proof (2 deltas). // Used for Zarcanum PoS E_range_proof. // proof is the wire-serialised bppe_signature blob. // commitments are the output amount commitments (premultiplied by 1/8). // Uses bpp_crypto_trait_Zarcanum (N=128, values_max=16). func VerifyBPPE(proof []byte, commitments [][32]byte) bool { if len(proof) == 0 || len(commitments) == 0 { return false } n := len(commitments) flat := make([]byte, n*32) for i, c := range commitments { copy(flat[i*32:], c[:]) } return C.cn_bppe_verify( (*C.uint8_t)(unsafe.Pointer(&proof[0])), C.size_t(len(proof)), (*C.uint8_t)(unsafe.Pointer(&flat[0])), C.size_t(n), ) == 0 } // VerifyBGE verifies a BGE one-out-of-many proof. // context is a 32-byte hash. ring is the set of public keys. // proof is the wire-serialised BGE_proof blob. func VerifyBGE(context [32]byte, ring [][32]byte, proof []byte) bool { if len(ring) == 0 || len(proof) == 0 { return false } n := len(ring) flat := make([]byte, n*32) for i, r := range ring { copy(flat[i*32:], r[:]) } return C.cn_bge_verify( (*C.uint8_t)(unsafe.Pointer(&context[0])), (*C.uint8_t)(unsafe.Pointer(&flat[0])), C.size_t(n), (*C.uint8_t)(unsafe.Pointer(&proof[0])), C.size_t(len(proof)), ) == 0 } // VerifyDoubleSchnorr verifies a generic_double_schnorr_sig from zarcanum.h. // aIsX selects the generator pair: // // false -> (G, G) // true -> (X, G) // // The proof blob is the 96-byte wire encoding: c(32) + y0(32) + y1(32). func VerifyDoubleSchnorr(hash [32]byte, aIsX bool, a [32]byte, b [32]byte, proof []byte) bool { if len(proof) != 96 { return false } var flag C.int if aIsX { flag = 1 } return C.cn_double_schnorr_verify( flag, (*C.uint8_t)(unsafe.Pointer(&hash[0])), (*C.uint8_t)(unsafe.Pointer(&a[0])), (*C.uint8_t)(unsafe.Pointer(&b[0])), (*C.uint8_t)(unsafe.Pointer(&proof[0])), C.size_t(len(proof)), ) == 0 } // VerifyZarcanum verifies a Zarcanum PoS proof. // This compatibility wrapper remains for the historical proof blob API. // Use VerifyZarcanumWithContext for full verification. func VerifyZarcanum(hash [32]byte, proof []byte) bool { if len(proof) == 0 { return false } return C.cn_zarcanum_verify( (*C.uint8_t)(unsafe.Pointer(&hash[0])), (*C.uint8_t)(unsafe.Pointer(&proof[0])), C.size_t(len(proof)), ) == 0 } // VerifyZarcanumWithContext verifies a Zarcanum PoS proof with the full // consensus context required by the upstream verifier. // // Example: // // crypto.VerifyZarcanumWithContext(crypto.ZarcanumVerificationContext{ // ContextHash: txHash, // KernelHash: kernelHash, // Ring: ring, // LastPowBlockIDHashed: lastPowHash, // StakeKeyImage: stakeKeyImage, // PosDifficulty: posDifficulty, // Proof: proofBlob, // }) func VerifyZarcanumWithContext(ctx ZarcanumVerificationContext) bool { if len(ctx.Ring) == 0 || len(ctx.Proof) == 0 { return false } flat := make([]byte, len(ctx.Ring)*128) for i, member := range ctx.Ring { copy(flat[i*128:], member.StealthAddress[:]) copy(flat[i*128+32:], member.AmountCommitment[:]) copy(flat[i*128+64:], member.BlindedAssetID[:]) copy(flat[i*128+96:], member.ConcealingPoint[:]) } return C.cn_zarcanum_verify_full( (*C.uint8_t)(unsafe.Pointer(&ctx.ContextHash[0])), (*C.uint8_t)(unsafe.Pointer(&ctx.KernelHash[0])), (*C.uint8_t)(unsafe.Pointer(&flat[0])), C.size_t(len(ctx.Ring)), (*C.uint8_t)(unsafe.Pointer(&ctx.LastPowBlockIDHashed[0])), (*C.uint8_t)(unsafe.Pointer(&ctx.StakeKeyImage[0])), C.uint64_t(ctx.PosDifficulty), (*C.uint8_t)(unsafe.Pointer(&ctx.Proof[0])), C.size_t(len(ctx.Proof)), ) == 0 }