208 lines
5.8 KiB
Go
208 lines
5.8 KiB
Go
// 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
|
|
}
|