113 lines
4.1 KiB
Go
113 lines
4.1 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"
|
|
|
|
coreerr "dappco.re/go/core/log"
|
|
|
|
"dappco.re/go/core/blockchain/consensus"
|
|
"dappco.re/go/core/blockchain/types"
|
|
)
|
|
|
|
// GetRingOutputs fetches the public keys for the given global output indices
|
|
// at the specified spending height and amount. This implements the
|
|
// consensus.RingOutputsFn signature for use during signature verification.
|
|
func (c *Chain) GetRingOutputs(height, amount uint64, offsets []uint64) ([]types.PublicKey, error) {
|
|
publicKeys := make([]types.PublicKey, len(offsets))
|
|
for i, gidx := range offsets {
|
|
txHash, outNo, err := c.GetOutput(amount, gidx)
|
|
if err != nil {
|
|
return nil, coreerr.E("Chain.GetRingOutputs", fmt.Sprintf("ring output %d (amount=%d, gidx=%d)", i, amount, gidx), err)
|
|
}
|
|
|
|
tx, _, err := c.GetTransaction(txHash)
|
|
if err != nil {
|
|
return nil, coreerr.E("Chain.GetRingOutputs", fmt.Sprintf("ring output %d: tx %s", i, txHash), err)
|
|
}
|
|
|
|
if int(outNo) >= len(tx.Vout) {
|
|
return nil, coreerr.E("Chain.GetRingOutputs", fmt.Sprintf("ring output %d: tx %s has %d outputs, want index %d", i, txHash, len(tx.Vout), outNo), nil)
|
|
}
|
|
|
|
switch out := tx.Vout[outNo].(type) {
|
|
case types.TxOutputBare:
|
|
spendKey, err := ringOutputSpendKey(height, out.Target)
|
|
if err != nil {
|
|
return nil, coreerr.E("Chain.GetRingOutputs", fmt.Sprintf("ring output %d: %v", i, err), nil)
|
|
}
|
|
publicKeys[i] = spendKey
|
|
default:
|
|
return nil, coreerr.E("Chain.GetRingOutputs", fmt.Sprintf("ring output %d: unsupported output type %T", i, out), nil)
|
|
}
|
|
}
|
|
return publicKeys, nil
|
|
}
|
|
|
|
// ringOutputSpendKey extracts the spend key for a transparent output target.
|
|
//
|
|
// TxOutMultisig does not carry enough context here to select the exact spend
|
|
// path, so we return the first listed key as a deterministic fallback.
|
|
// TxOutHTLC selects redeem vs refund based on whether the spending height is
|
|
// before or after the contract expiration. The refund path only opens after
|
|
// the expiration height has passed.
|
|
func ringOutputSpendKey(height uint64, target types.TxOutTarget) (types.PublicKey, error) {
|
|
if t, ok := types.AsTxOutToKey(target); ok {
|
|
return t.Key, nil
|
|
}
|
|
|
|
switch t := target.(type) {
|
|
case types.TxOutMultisig:
|
|
if len(t.Keys) == 0 {
|
|
return types.PublicKey{}, coreerr.E("ringOutputSpendKey", "multisig target has no keys", nil)
|
|
}
|
|
return t.Keys[0], nil
|
|
case types.TxOutHTLC:
|
|
if height > t.Expiration {
|
|
return t.PKRefund, nil
|
|
}
|
|
return t.PKRedeem, nil
|
|
default:
|
|
return types.PublicKey{}, coreerr.E("ringOutputSpendKey", fmt.Sprintf("unsupported target type %T", target), 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, coreerr.E("Chain.GetZCRingOutputs", fmt.Sprintf("ZC ring output %d (gidx=%d)", i, gidx), err)
|
|
}
|
|
|
|
tx, _, err := c.GetTransaction(txHash)
|
|
if err != nil {
|
|
return nil, coreerr.E("Chain.GetZCRingOutputs", fmt.Sprintf("ZC ring output %d: tx %s", i, txHash), err)
|
|
}
|
|
|
|
if int(outNo) >= len(tx.Vout) {
|
|
return nil, coreerr.E("Chain.GetZCRingOutputs", fmt.Sprintf("ZC ring output %d: tx %s has %d outputs, want index %d", i, txHash, len(tx.Vout), outNo), nil)
|
|
}
|
|
|
|
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, coreerr.E("Chain.GetZCRingOutputs", fmt.Sprintf("ZC ring output %d: expected TxOutputZarcanum, got %T", i, out), nil)
|
|
}
|
|
}
|
|
return members, nil
|
|
}
|