public ElementsUtxoData(OutPoint outpoint, ConfidentialAsset asset, long amount) : base(outpoint, amount)
 {
     if (asset is null)
     {
         throw new ArgumentNullException(nameof(asset));
     }
     if (asset.HasBlinding())
     {
         throw new InvalidOperationException("asset is blinded.");
     }
     unblindedAsset    = asset.ToHexString();
     value             = new ConfidentialValue(amount);
     assetBlindFactor  = new BlindFactor();
     amountBlindFactor = new BlindFactor();
 }
示例#2
0
        /// <summary>
        /// get total amount from utxo list on asset.
        /// </summary>
        /// <param name="utxoList">utxo list</param>
        /// <param name="asset">asset</param>
        /// <returns>asset total amount</returns>
        public static long GetTotalAmount(ElementsUtxoData[] utxoList, ConfidentialAsset asset)
        {
            if ((utxoList is null) || (asset is null))
            {
                return(0);
            }
            long amount = 0;

            foreach (var utxo in utxoList)
            {
                if (utxo.GetAsset() == asset.ToHexString())
                {
                    amount += utxo.GetAmount();
                }
            }
            return(amount);
        }
示例#3
0
        /// <summary>
        /// Select coins for elements.
        /// </summary>
        /// <param name="utxoList">utxo list</param>
        /// <param name="targetAssetAmountMap">target amount of asset. Amount more than the specified amount is set in txout. default is 0 (disable).</param>
        /// <param name="feeAsset">asset by fee</param>
        /// <param name="txFeeAmount">transaction fee</param>
        /// <param name="effectiveFeeRate">fee rate</param>
        /// <param name="exponent">blinding exponent</param>
        /// <param name="minimumBits">blinding minimum bits</param>
        /// <param name="longTermFeeRate">long-term fee rate</param>
        /// <param name="dustFeeRate">dust fee rate</param>
        /// <param name="knapsackMinChange">knapsack min change value. knapsack logic's threshold. Recommended value is 1.</param>
        /// <returns>select utxo list.</returns>
        public ElementsUtxoData[] SelectCoinsForElements(
            ElementsUtxoData[] utxoList,
            IDictionary <ConfidentialAsset, long> targetAssetAmountMap,
            ConfidentialAsset feeAsset, long txFeeAmount,
            double effectiveFeeRate, int exponent, int minimumBits,
            double longTermFeeRate,
            double dustFeeRate, long knapsackMinChange)
        {
            if (utxoList is null)
            {
                throw new ArgumentNullException(nameof(utxoList));
            }
            if (utxoList.Length <= 0)
            {
                throw new InvalidOperationException("utxoList is empty.");
            }
            if (targetAssetAmountMap is null)
            {
                throw new ArgumentNullException(nameof(targetAssetAmountMap));
            }
            if (targetAssetAmountMap.Count <= 0)
            {
                throw new InvalidOperationException("targetAssetAmountMap is empty.");
            }
            if (feeAsset is null)
            {
                throw new ArgumentNullException(nameof(feeAsset));
            }
            if (feeAsset.HasBlinding())
            {
                throw new InvalidOperationException(
                          "fee asset has blinding. fee asset is unblind only.");
            }
            using (var handle = new ErrorHandle())
            {
                var ret = NativeMethods.CfdInitializeCoinSelection(
                    handle.GetHandle(), (uint)utxoList.Length, (uint)targetAssetAmountMap.Count,
                    feeAsset.ToHexString(), txFeeAmount,
                    effectiveFeeRate, longTermFeeRate, dustFeeRate, knapsackMinChange,
                    out IntPtr coinSelectHandle);
                if (ret != CfdErrorCode.Success)
                {
                    handle.ThrowError(ret);
                }
                try
                {
                    for (uint index = 0; index < utxoList.Length; ++index)
                    {
                        string desc = (utxoList[index].GetDescriptor() is null) ?
                                      "" : utxoList[index].GetDescriptor().ToString();
                        ret = NativeMethods.CfdAddCoinSelectionUtxoTemplate(
                            handle.GetHandle(), coinSelectHandle, index,
                            utxoList[index].GetOutPoint().GetTxid().ToHexString(),
                            utxoList[index].GetOutPoint().GetVout(),
                            utxoList[index].GetAmount(),
                            utxoList[index].GetAsset(),
                            desc, utxoList[index].GetScriptSigTemplate().ToHexString());
                        if (ret != CfdErrorCode.Success)
                        {
                            handle.ThrowError(ret);
                        }
                    }
                    long targetAmountAll = 0;
                    uint assetIndex      = 0;
                    foreach (var key in targetAssetAmountMap.Keys)
                    {
                        ret = NativeMethods.CfdAddCoinSelectionAmount(
                            handle.GetHandle(), coinSelectHandle, assetIndex,
                            targetAssetAmountMap[key], key.ToHexString());
                        if (ret != CfdErrorCode.Success)
                        {
                            handle.ThrowError(ret);
                        }
                        ++assetIndex;
                        targetAmountAll += targetAssetAmountMap[key];
                    }

                    if (exponent >= -1)
                    {
                        ret = NativeMethods.CfdSetOptionCoinSelection(handle.GetHandle(), coinSelectHandle,
                                                                      Exponent, exponent, 0, false);
                        if (ret != CfdErrorCode.Success)
                        {
                            handle.ThrowError(ret);
                        }
                    }
                    if (minimumBits >= 0)
                    {
                        ret = NativeMethods.CfdSetOptionCoinSelection(handle.GetHandle(), coinSelectHandle,
                                                                      MinimumBits, minimumBits, 0, false);
                        if (ret != CfdErrorCode.Success)
                        {
                            handle.ThrowError(ret);
                        }
                    }

                    ret = NativeMethods.CfdFinalizeCoinSelection(
                        handle.GetHandle(), coinSelectHandle, out long utxoFeeAmount);
                    if (ret != CfdErrorCode.Success)
                    {
                        handle.ThrowError(ret);
                    }

                    uint[] collectIndexes = new uint[utxoList.Length];
                    uint   collectCount   = 0;
                    if ((utxoFeeAmount > 0) || (targetAmountAll > 0))
                    {
                        for (uint index = 0; index < utxoList.Length; ++index)
                        {
                            ret = NativeMethods.CfdGetSelectedCoinIndex(
                                handle.GetHandle(), coinSelectHandle,
                                index, out int utxoIndex);
                            if (ret != CfdErrorCode.Success)
                            {
                                handle.ThrowError(ret);
                            }
                            if (utxoIndex < 0)
                            {
                                break;
                            }
                            if (utxoList.Length <= utxoIndex)
                            {
                                throw new InvalidProgramException("utxoIndex maximum over.");
                            }
                            ++collectCount;
                            collectIndexes[index] = (uint)utxoIndex;
                        }
                    }

                    /*
                     * assetIndex = 0;
                     * collectAmountList = new Dictionary<ConfidentialAsset, long>();
                     * foreach (var key in targetAssetAmountMap.Keys)
                     * {
                     * ret = NativeMethods.CfdGetSelectedCoinAssetAmount(
                     *  handle.GetHandle(), coinSelectHandle, assetIndex,
                     *  out long collectAmount);
                     * if (ret != CfdErrorCode.Success)
                     * {
                     *  handle.ThrowError(ret);
                     * }
                     ++assetIndex;
                     * collectAmountList.Add(key, collectAmount);
                     * }
                     */

                    ElementsUtxoData[] selectedUtxoList = new ElementsUtxoData[collectCount];
                    for (uint index = 0; index < collectCount; ++index)
                    {
                        selectedUtxoList[index] = utxoList[collectIndexes[index]];
                    }
                    lastSelectedUtxoFee = utxoFeeAmount;
                    return(selectedUtxoList);
                }
                finally
                {
                    NativeMethods.CfdFreeCoinSelectionHandle(handle.GetHandle(), coinSelectHandle);
                }
            }
        }