Add full V2 transaction verification pipeline: parse SignaturesRaw variant vector into structured ZC signature data, verify CLSAG GGX ring signatures per ZC input, verify BPP range proofs, and verify BGE asset surjection proofs with correct ring construction (mul8 point arithmetic). Fix three wire format bugs that caused V2 parsing failures: - RefTypeGlobalIndex (tag 0x1A) uses 8-byte LE, not varint - Raw uint64_t variant (tagUint64) uses 8-byte LE, not varint - zarcanum_tx_data_v1 fee uses FIELD() → 8-byte LE, not VARINT_FIELD() Add cn_point_sub to C++ bridge and Go wrapper for BGE ring construction. Add GetZCRingOutputs to chain for fetching ZC ring member data. Co-Authored-By: Charon <charon@lethean.io>
148 lines
4.7 KiB
Go
148 lines
4.7 KiB
Go
// SPDX-Licence-Identifier: EUPL-1.2
|
|
|
|
package crypto
|
|
|
|
/*
|
|
#include "bridge.h"
|
|
*/
|
|
import "C"
|
|
|
|
import (
|
|
"fmt"
|
|
"unsafe"
|
|
)
|
|
|
|
// PointMul8 multiplies a curve point by the cofactor 8.
|
|
func PointMul8(pk [32]byte) ([32]byte, error) {
|
|
var result [32]byte
|
|
rc := C.cn_point_mul8(
|
|
(*C.uint8_t)(unsafe.Pointer(&pk[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&result[0])),
|
|
)
|
|
if rc != 0 {
|
|
return result, fmt.Errorf("crypto: point_mul8 failed")
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// PointDiv8 premultiplies a curve point by 1/8 (cofactor inverse).
|
|
// This is the on-chain storage form for commitments and key images.
|
|
func PointDiv8(pk [32]byte) ([32]byte, error) {
|
|
var result [32]byte
|
|
rc := C.cn_point_div8(
|
|
(*C.uint8_t)(unsafe.Pointer(&pk[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&result[0])),
|
|
)
|
|
if rc != 0 {
|
|
return result, fmt.Errorf("crypto: point_div8 failed")
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// PointSub computes a - b on the Ed25519 curve.
|
|
func PointSub(a, b [32]byte) ([32]byte, error) {
|
|
var result [32]byte
|
|
rc := C.cn_point_sub(
|
|
(*C.uint8_t)(unsafe.Pointer(&a[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&b[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&result[0])),
|
|
)
|
|
if rc != 0 {
|
|
return result, fmt.Errorf("crypto: point_sub failed")
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
// CLSAGGGSigSize returns the byte size of a CLSAG_GG signature for a given ring size.
|
|
func CLSAGGGSigSize(ringSize int) int {
|
|
return int(C.cn_clsag_gg_sig_size(C.size_t(ringSize)))
|
|
}
|
|
|
|
// GenerateCLSAGGG creates a CLSAG_GG ring signature.
|
|
// ring is a flat slice of [stealth_addr(32) | amount_commitment(32)] per entry.
|
|
// pseudoOut is the pseudo output commitment (not premultiplied by 1/8).
|
|
// secretX and secretF are the secret scalars for the signer.
|
|
func GenerateCLSAGGG(hash [32]byte, ring []byte, ringSize int,
|
|
pseudoOut [32]byte, ki [32]byte,
|
|
secretX [32]byte, secretF [32]byte, secretIndex int) ([]byte, error) {
|
|
|
|
sigLen := CLSAGGGSigSize(ringSize)
|
|
sig := make([]byte, sigLen)
|
|
|
|
rc := C.cn_clsag_gg_generate(
|
|
(*C.uint8_t)(unsafe.Pointer(&hash[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&ring[0])),
|
|
C.size_t(ringSize),
|
|
(*C.uint8_t)(unsafe.Pointer(&pseudoOut[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&ki[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&secretX[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&secretF[0])),
|
|
C.size_t(secretIndex),
|
|
(*C.uint8_t)(unsafe.Pointer(&sig[0])),
|
|
)
|
|
if rc != 0 {
|
|
return nil, fmt.Errorf("crypto: generate_CLSAG_GG failed")
|
|
}
|
|
return sig, nil
|
|
}
|
|
|
|
// VerifyCLSAGGG verifies a CLSAG_GG ring signature.
|
|
// ring is a flat slice of [stealth_addr(32) | amount_commitment(32)] per entry.
|
|
// pseudoOut is the pseudo output commitment (premultiplied by 1/8).
|
|
func VerifyCLSAGGG(hash [32]byte, ring []byte, ringSize int,
|
|
pseudoOut [32]byte, ki [32]byte, sig []byte) bool {
|
|
|
|
return C.cn_clsag_gg_verify(
|
|
(*C.uint8_t)(unsafe.Pointer(&hash[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&ring[0])),
|
|
C.size_t(ringSize),
|
|
(*C.uint8_t)(unsafe.Pointer(&pseudoOut[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&ki[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&sig[0])),
|
|
) == 0
|
|
}
|
|
|
|
// CLSAGGGXSigSize returns the byte size of a CLSAG_GGX signature for a given ring size.
|
|
func CLSAGGGXSigSize(ringSize int) int {
|
|
return int(C.cn_clsag_ggx_sig_size(C.size_t(ringSize)))
|
|
}
|
|
|
|
// VerifyCLSAGGGX verifies a CLSAG_GGX ring signature.
|
|
// ring is a flat slice of [stealth(32) | commitment(32) | blinded_asset_id(32)] per entry.
|
|
func VerifyCLSAGGGX(hash [32]byte, ring []byte, ringSize int,
|
|
pseudoOutCommitment [32]byte, pseudoOutAssetID [32]byte,
|
|
ki [32]byte, sig []byte) bool {
|
|
|
|
return C.cn_clsag_ggx_verify(
|
|
(*C.uint8_t)(unsafe.Pointer(&hash[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&ring[0])),
|
|
C.size_t(ringSize),
|
|
(*C.uint8_t)(unsafe.Pointer(&pseudoOutCommitment[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&pseudoOutAssetID[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&ki[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&sig[0])),
|
|
) == 0
|
|
}
|
|
|
|
// CLSAGGGXXGSigSize returns the byte size of a CLSAG_GGXXG signature for a given ring size.
|
|
func CLSAGGGXXGSigSize(ringSize int) int {
|
|
return int(C.cn_clsag_ggxxg_sig_size(C.size_t(ringSize)))
|
|
}
|
|
|
|
// VerifyCLSAGGGXXG verifies a CLSAG_GGXXG ring signature.
|
|
// ring is a flat slice of [stealth(32) | commitment(32) | blinded_asset_id(32) | concealing(32)] per entry.
|
|
func VerifyCLSAGGGXXG(hash [32]byte, ring []byte, ringSize int,
|
|
pseudoOutCommitment [32]byte, pseudoOutAssetID [32]byte,
|
|
extendedCommitment [32]byte, ki [32]byte, sig []byte) bool {
|
|
|
|
return C.cn_clsag_ggxxg_verify(
|
|
(*C.uint8_t)(unsafe.Pointer(&hash[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&ring[0])),
|
|
C.size_t(ringSize),
|
|
(*C.uint8_t)(unsafe.Pointer(&pseudoOutCommitment[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&pseudoOutAssetID[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&extendedCommitment[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&ki[0])),
|
|
(*C.uint8_t)(unsafe.Pointer(&sig[0])),
|
|
) == 0
|
|
}
|