예제 #1
0
        public IEnumerable <ICoin> Select(IEnumerable <ICoin> coins, IMoney target)
        {
            var    result = new List <ICoin>();
            IMoney total  = Money.Zero;

            if (target.CompareTo(Money.Zero) == 0)
            {
                return(result);
            }


            var orderedCoinGroups = coins.GroupBy(c => new Key().ScriptPubKey)
                                    .Select(scriptPubKeyCoins => new
            {
                Amount = scriptPubKeyCoins.Select(c => c.Amount).Sum(Money.Zero),
                Coins  = scriptPubKeyCoins.ToList()
            }).OrderBy(c => c.Amount);

            //If any of your UTXO² matches the Target¹ it will be used.
            var targetCoin = orderedCoinGroups.FirstOrDefault(c => c.Amount.CompareTo(target) == 0);

            if (targetCoin != null)
            {
                return(targetCoin.Coins);
            }

            foreach (var coinGroup in orderedCoinGroups)
            {
                // If this UTXO is greater than the target, just use it.
                if (coinGroup.Amount.CompareTo(target) == 1)
                {
                    return(coinGroup.Coins);
                }

                // Build up our ongoing total
                total = total.Add(coinGroup.Amount);
                result.AddRange(coinGroup.Coins);

                // If we go over the total, return the current set.
                if (total.CompareTo(target) == 1)
                {
                    return(result);
                }
            }

            // If we didn't have enough funds, return null
            if (total.CompareTo(target) == -1)
            {
                return(null);
            }

            return(result);
        }
        public IEnumerable <ICoin> Select(IEnumerable <ICoin> coins, IMoney target)
        {
            var    result = new List <ICoin>();
            IMoney total  = Money.Zero;

            if (target.CompareTo(Money.Zero) == 0)
            {
                return(result);
            }

            foreach (ICoin coin in coins)
            {
                // Build up our ongoing total
                total = total.Add(coin.Amount);
                result.Add(coin);

                // If we make the target, return the current set.
                if (total.CompareTo(target) >= 0)
                {
                    return(result);
                }
            }

            // If we are here we didn't have enough funds.
            return(null);
        }
예제 #3
0
        public static IEnumerable <ICoin> CoinSelect(IEnumerable <ICoin> coins, IMoney target)
        {
            var rand       = new Random((int)DateTime.Now.Ticks);
            var zero       = target.Sub(target);
            var targetCoin = coins.FirstOrDefault(c => c.Amount.CompareTo(target) == 0);

            if (targetCoin != null)
            {
                return new[] { targetCoin }
            }
            ;

            var result = new List <ICoin>();
            var total  = zero;

            if (target.CompareTo(zero) == 0)
            {
                return(result);
            }

            var orderedCoins = coins.OrderBy(s => s.Amount).ToArray();

            foreach (var coin in orderedCoins)
            {
                if (coin.Amount.CompareTo(target) == -1 && total.CompareTo(target) == -1)
                {
                    total = total.Add(coin.Amount);
                    result.Add(coin);
                }
                else
                {
                    if (total.CompareTo(target) == -1 && coin.Amount.CompareTo(target) == 1)
                    {
                        return(new[] { coin });
                    }
                    else
                    {
                        var allCoins = orderedCoins.ToArray();
                        for (int _ = 0; _ < 1000; _++)
                        {
                            var selection = new List <ICoin>();
                            Utils.Shuffle(allCoins, rand);
                            var currentTotal = zero;
                            for (int i = 0; i < allCoins.Length; i++)
                            {
                                selection.Add(allCoins[i]);
                                currentTotal = currentTotal.Add(allCoins[i].Amount);

                                // if new count already greater than previous
                                if (selection.Count > result.Count)
                                {
                                    break;
                                }

                                if (currentTotal.CompareTo(target) >= 0)
                                {
                                    // if new count less than previous then use it
                                    // if new count equals to previous but sum of used inputs is less, then use it
                                    if (selection.Count < result.Count || selection.Count == result.Count && currentTotal.CompareTo(total) == -1)
                                    {
                                        result = selection;
                                        total  = currentTotal;
                                    }

                                    break;
                                }
                            }
                        }
                    }
                }
            }
            if (total.CompareTo(target) == -1)
            {
                return(null);
            }
            return(result);
        }
    }
예제 #4
0
		public IEnumerable<ICoin> Select(IEnumerable<ICoin> coins, IMoney target)
		{
			var zero = target.Sub(target);
			var targetCoin = coins
							.FirstOrDefault(c => c.Amount.CompareTo(target) == 0);
			//If any of your UTXO² matches the Target¹ it will be used.
			if(targetCoin != null)
				return new[] { targetCoin };

			List<ICoin> result = new List<ICoin>();
			IMoney total = zero;

			if(target.CompareTo(zero) == 0)
				return result;

			var orderedCoins = coins.OrderBy(s => s.Amount).ToArray();

			foreach(var coin in orderedCoins)
			{
				if(coin.Amount.CompareTo(target) == -1 && total.CompareTo(target) == -1)
				{
					total = total.Add(coin.Amount);
					result.Add(coin);
					//If the "sum of all your UTXO smaller than the Target" happens to match the Target, they will be used. (This is the case if you sweep a complete wallet.)
					if(total.CompareTo(target) == 0)
						return result;

				}
				else
				{
					if(total.CompareTo(target) == -1 && coin.Amount.CompareTo(target) == 1)
					{
						//If the "sum of all your UTXO smaller than the Target" doesn't surpass the target, the smallest UTXO greater than your Target will be used.
						return new[] { coin };
					}
					else
					{
						//						Else Bitcoin Core does 1000 rounds of randomly combining unspent transaction outputs until their sum is greater than or equal to the Target. If it happens to find an exact match, it stops early and uses that.
						//Otherwise it finally settles for the minimum of
						//the smallest UTXO greater than the Target
						//the smallest combination of UTXO it discovered in Step 4.
						var allCoins = orderedCoins.ToArray();
						IMoney minTotal = null;
						for(int _ = 0; _ < 1000; _++)
						{
							var selection = new List<ICoin>();
							Utils.Shuffle(allCoins, _Rand);
							total = zero;
							for(int i = 0; i < allCoins.Length; i++)
							{
								selection.Add(allCoins[i]);
								total = total.Add(allCoins[i].Amount);
								if(total.CompareTo(target) == 0)
									return selection;
								if(total.CompareTo(target) == 1)
									break;
							}
							if(total.CompareTo(target) == -1)
							{
								return null;
							}
							if(minTotal == null || total.CompareTo(minTotal) == -1)
							{
								minTotal = total;
							}
						}
					}
				}
			}
			if(total.CompareTo(target) == -1)
				return null;
			return result;
		}