// 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. // // keys, err := blockchain.GetRingOutputs(blockHeight, inputAmount, []uint64{0, 5, 12, 30}) 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 key, ok := (types.TxOutputBare{Target: target}).SpendKey(); ok { return 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). // // members, err := blockchain.GetZCRingOutputs([]uint64{100, 200, 300}) 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 }