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>
81 lines
2.6 KiB
Go
81 lines
2.6 KiB
Go
// Copyright (c) 2017-2026 Lethean (https://lt.hn)
|
|
//
|
|
// Licensed under the European Union Public Licence (EUPL) version 1.2.
|
|
// SPDX-License-Identifier: EUPL-1.2
|
|
|
|
package chain
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"forge.lthn.ai/core/go-blockchain/consensus"
|
|
"forge.lthn.ai/core/go-blockchain/types"
|
|
)
|
|
|
|
// GetRingOutputs fetches the public keys for the given global output indices
|
|
// at the specified amount. This implements the consensus.RingOutputsFn
|
|
// signature for use during signature verification.
|
|
func (c *Chain) GetRingOutputs(amount uint64, offsets []uint64) ([]types.PublicKey, error) {
|
|
pubs := make([]types.PublicKey, len(offsets))
|
|
for i, gidx := range offsets {
|
|
txHash, outNo, err := c.GetOutput(amount, gidx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ring output %d (amount=%d, gidx=%d): %w", i, amount, gidx, err)
|
|
}
|
|
|
|
tx, _, err := c.GetTransaction(txHash)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ring output %d: tx %s: %w", i, txHash, err)
|
|
}
|
|
|
|
if int(outNo) >= len(tx.Vout) {
|
|
return nil, fmt.Errorf("ring output %d: tx %s has %d outputs, want index %d",
|
|
i, txHash, len(tx.Vout), outNo)
|
|
}
|
|
|
|
switch out := tx.Vout[outNo].(type) {
|
|
case types.TxOutputBare:
|
|
pubs[i] = out.Target.Key
|
|
default:
|
|
return nil, fmt.Errorf("ring output %d: unsupported output type %T", i, out)
|
|
}
|
|
}
|
|
return pubs, nil
|
|
}
|
|
|
|
// GetZCRingOutputs fetches ZC ring members (stealth address, amount commitment,
|
|
// blinded asset ID) for the given global output indices. This implements the
|
|
// consensus.ZCRingOutputsFn signature for post-HF4 CLSAG GGX verification.
|
|
//
|
|
// ZC outputs are indexed at amount=0 (confidential amounts).
|
|
func (c *Chain) GetZCRingOutputs(offsets []uint64) ([]consensus.ZCRingMember, error) {
|
|
members := make([]consensus.ZCRingMember, len(offsets))
|
|
for i, gidx := range offsets {
|
|
txHash, outNo, err := c.GetOutput(0, gidx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ZC ring output %d (gidx=%d): %w", i, gidx, err)
|
|
}
|
|
|
|
tx, _, err := c.GetTransaction(txHash)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("ZC ring output %d: tx %s: %w", i, txHash, err)
|
|
}
|
|
|
|
if int(outNo) >= len(tx.Vout) {
|
|
return nil, fmt.Errorf("ZC ring output %d: tx %s has %d outputs, want index %d",
|
|
i, txHash, len(tx.Vout), outNo)
|
|
}
|
|
|
|
switch out := tx.Vout[outNo].(type) {
|
|
case types.TxOutputZarcanum:
|
|
members[i] = consensus.ZCRingMember{
|
|
StealthAddress: [32]byte(out.StealthAddress),
|
|
AmountCommitment: [32]byte(out.AmountCommitment),
|
|
BlindedAssetID: [32]byte(out.BlindedAssetID),
|
|
}
|
|
default:
|
|
return nil, fmt.Errorf("ZC ring output %d: expected TxOutputZarcanum, got %T", i, out)
|
|
}
|
|
}
|
|
return members, nil
|
|
}
|