Add cn_bpp_verify for Bulletproofs++ (1 delta, bpp_crypto_trait_ZC_out) used by zc_outs_range_proof in post-HF4 transactions. Distinguish from cn_bppe_verify (2 deltas, bpp_crypto_trait_Zarcanum) used for Zarcanum PoS proofs. Key changes: - Add deserialise_bpp() and cn_bpp_verify() to bridge.cpp - Add VerifyBPP() Go wrapper in proof.go - Wire BPPE and BGE stubs into real C++ verify functions - Add try/catch to all proof verifiers (C++ throws on invalid points) - Add nil/empty input guards to all Go proof functions - Test with real BPP proof from testnet block 101 coinbase tx The BPP proof from tx 543bc3c2... (first post-HF4 coinbase) verifies successfully through the full CGo pipeline, confirming wire format deserialisation matches the C++ daemon output. Co-Authored-By: Charon <charon@lethean.io> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
89 lines
2.5 KiB
Go
89 lines
2.5 KiB
Go
// SPDX-Licence-Identifier: EUPL-1.2
|
|
|
|
package crypto
|
|
|
|
/*
|
|
#include "bridge.h"
|
|
*/
|
|
import "C"
|
|
|
|
import "unsafe"
|
|
|
|
// 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
|
|
}
|
|
|
|
// VerifyZarcanum verifies a Zarcanum PoS proof.
|
|
// Currently returns false — bridge API needs extending to pass kernel_hash,
|
|
// ring, last_pow_block_id, stake_ki, and pos_difficulty.
|
|
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
|
|
}
|