public TransactionBuildingContext CreateMemento()
            {
                var memento = new TransactionBuildingContext(Builder);

                memento.RestoreMemento(this);
                return(memento);
            }
 Money SetChange(TransactionBuildingContext ctx)
 {
     if (ctx.ChangeAmount == Money.Zero)
     {
         return(Money.Zero);
     }
     ctx.Transaction.AddOutput(new TxOut(ctx.ChangeAmount, ctx.Group.ChangeScript[(int)ChangeType.Uncolored]));
     return(ctx.ChangeAmount);
 }
        private IEnumerable <ICoin> BuildTransaction(
            TransactionBuildingContext ctx,
            BuilderGroup group,
            IEnumerable <Builder> builders,
            IEnumerable <ICoin> coins)
        {
            var originalCtx = ctx.CreateMemento();
            var target      = builders.Concat(ctx.AdditionalBuilders).Select(b => b(ctx)).Sum();

            if (ctx.CoverOnly != null)
            {
                target = ctx.CoverOnly + ctx.ChangeAmount;
            }
            var selection = CoinSelector.Select(coins, target);

            if (selection == null)
            {
                throw new NotEnoughFundsException("Not enough fund to cover the target");
            }
            var total  = selection.Select(s => s.Amount).Sum();
            var change = total - target;

            if (change < Money.Zero)
            {
                throw new NotEnoughFundsException("Not enough fund to cover the target");
            }
            if (change > ctx.Dust)
            {
                if (group.ChangeScript[(int)ctx.ChangeType] == null)
                {
                    throw new InvalidOperationException("A change address should be specified (" + ctx.ChangeType + ")");
                }

                ctx.RestoreMemento(originalCtx);
                ctx.ChangeAmount = change;
                try
                {
                    return(BuildTransaction(ctx, group, builders, coins));
                }
                finally
                {
                    ctx.ChangeAmount = Money.Zero;
                }
            }
            foreach (var coin in selection)
            {
                var input = ctx.Transaction.AddInput(new TxIn(coin.Outpoint));
                if (_LockTime != null && _LockTime.HasValue && !ctx.NonFinalSequenceSet)
                {
                    input.Sequence          = 0;
                    ctx.NonFinalSequenceSet = true;
                }
            }
            return(selection);
        }
        Money SetColoredChange(TransactionBuildingContext ctx)
        {
            if (ctx.ChangeAmount == Money.Zero)
            {
                return(Money.Zero);
            }
            var marker = ctx.GetColorMarker(false);
            var txout  = ctx.Transaction.AddOutput(new TxOut(ColoredDust, ctx.Group.ChangeScript[(int)ChangeType.Colored]));

            marker.SetQuantity(ctx.Transaction.Outputs.Count - 2, ctx.ChangeAmount);
            ctx.AdditionalFees += ColoredDust;
            return(ctx.ChangeAmount);
        }
        public Transaction BuildTransaction(bool sign, SigHash sigHash)
        {
            TransactionBuildingContext ctx = new TransactionBuildingContext(this);

            if (_CompletedTransaction != null)
            {
                ctx.Transaction = _CompletedTransaction;
            }
            if (_LockTime != null && _LockTime.HasValue)
            {
                ctx.Transaction.LockTime = _LockTime.Value;
            }
            foreach (var group in _BuilderGroups)
            {
                ctx.Group = group;
                ctx.AdditionalBuilders.Clear();
                ctx.AdditionalFees = Money.Zero;

                ctx.ChangeType = ChangeType.Colored;
                foreach (var builder in group.IssuanceBuilders)
                {
                    builder(ctx);
                }

                var buildersByAsset = group.BuildersByAsset.ToList();
                foreach (var builders in buildersByAsset)
                {
                    var coins = group.Coins.Values.OfType <ColoredCoin>().Where(c => c.Asset.Id == builders.Key).OfType <ICoin>();

                    ctx.Dust      = Money.Zero;
                    ctx.CoverOnly = null;
                    var btcSpent = BuildTransaction(ctx, group, builders.Value, coins)
                                   .OfType <IColoredCoin>().Select(c => c.Bearer.Amount).Sum();
                    ctx.AdditionalFees -= btcSpent;
                }

                ctx.AdditionalBuilders.Add(_ => _.AdditionalFees);
                ctx.Dust       = Money.Dust;
                ctx.CoverOnly  = group.CoverOnly;
                ctx.ChangeType = ChangeType.Uncolored;
                BuildTransaction(ctx, group, group.Builders, group.Coins.Values.OfType <Coin>());
            }
            ctx.Finish();

            if (sign)
            {
                SignTransactionInPlace(ctx.Transaction, sigHash);
            }
            return(ctx.Transaction);
        }
 public void RestoreMemento(TransactionBuildingContext memento)
 {
     _Marker        = memento._Marker == null ? null : new ColorMarker(memento._Marker.GetScript());
     Transaction    = memento.Transaction.Clone();
     AdditionalFees = memento.AdditionalFees;
 }
Example #7
0
		private IEnumerable<ICoin> BuildTransaction(
			TransactionBuildingContext ctx,
			BuilderGroup group,
			IEnumerable<Builder> builders,
			IEnumerable<ICoin> coins,
			IMoney zero)
		{
			var originalCtx = ctx.CreateMemento();
			var target = builders.Concat(ctx.AdditionalBuilders).Select(b => b(ctx)).Sum(zero);
			if(ctx.CoverOnly != null)
			{
				target = ctx.CoverOnly.Add(ctx.ChangeAmount);
			}

			var unconsumed = coins.Where(c => ctx.ConsumedCoins.All(cc => cc.Outpoint != c.Outpoint));
			var selection = CoinSelector.Select(unconsumed, target);
			if(selection == null)
				throw new NotEnoughFundsException("Not enough fund to cover the target",
					group.Name,
					target.Sub(unconsumed.Select(u => u.Amount).Sum(zero))
					);
			var total = selection.Select(s => s.Amount).Sum(zero);
			var change = total.Sub(target);
			if(change.CompareTo(zero) == -1)
				throw new NotEnoughFundsException("Not enough fund to cover the target",
					group.Name,
					change.Negate()
				);
			if(change.CompareTo(ctx.Dust) == 1)
			{
				var changeScript = group.ChangeScript[(int)ctx.ChangeType];
				if(changeScript == null)
					throw new InvalidOperationException("A change address should be specified (" + ctx.ChangeType + ")");
				if(!(ctx.Dust is Money) || change.CompareTo(GetDust(changeScript)) == 1)
				{
					ctx.RestoreMemento(originalCtx);
					ctx.ChangeAmount = change;
					try
					{
						return BuildTransaction(ctx, group, builders, coins, zero);
					}
					finally
					{
						ctx.ChangeAmount = zero;
					}
				}
			}
			foreach(var coin in selection)
			{
				ctx.ConsumedCoins.Add(coin);
				var input = ctx.Transaction.Inputs.FirstOrDefault(i => i.PrevOut == coin.Outpoint);
				if(input == null)
					input = ctx.Transaction.AddInput(new TxIn(coin.Outpoint));
				if(_LockTime != null && !ctx.NonFinalSequenceSet)
				{
					input.Sequence = 0;
					ctx.NonFinalSequenceSet = true;
				}
			}
			return selection;
		}
Example #8
0
 public Money Build(TransactionBuildingContext ctx)
 {
     ctx.Transaction.Outputs.Add(_TxOut);
     return(_TxOut.Value);
 }
Example #9
0
		/// <summary>
		/// Build the transaction
		/// </summary>
		/// <param name="sign">True if signs all inputs with the available keys</param>
		/// <param name="sigHash">The type of signature</param>
		/// <returns>The transaction</returns>
		/// <exception cref="NBitcoin.NotEnoughFundsException">Not enough funds are available</exception>
		public Transaction BuildTransaction(bool sign, SigHash sigHash)
		{
			TransactionBuildingContext ctx = new TransactionBuildingContext(this);
			if(_CompletedTransaction != null)
				ctx.Transaction = _CompletedTransaction.Clone();
			if(_LockTime != null)
				ctx.Transaction.LockTime = _LockTime.Value;
			foreach(var group in _BuilderGroups)
			{
				ctx.Group = group;
				ctx.AdditionalBuilders.Clear();
				ctx.AdditionalFees = Money.Zero;

				ctx.ChangeType = ChangeType.Colored;
				foreach(var builder in group.IssuanceBuilders)
					builder(ctx);

				var buildersByAsset = group.BuildersByAsset.ToList();
				foreach(var builders in buildersByAsset)
				{
					var coins = group.Coins.Values.OfType<ColoredCoin>().Where(c => c.Amount.Id == builders.Key);

					ctx.Dust = new AssetMoney(builders.Key);
					ctx.CoverOnly = null;
					ctx.ChangeAmount = new AssetMoney(builders.Key);
					var btcSpent = BuildTransaction(ctx, group, builders.Value, coins, new AssetMoney(builders.Key))
						.OfType<IColoredCoin>().Select(c => c.Bearer.Amount).Sum();
					ctx.AdditionalFees -= btcSpent;
				}

				ctx.AdditionalBuilders.Add(_ => _.AdditionalFees);
				ctx.Dust = GetDust();
				ctx.ChangeAmount = Money.Zero;
				ctx.CoverOnly = group.CoverOnly;
				ctx.ChangeType = ChangeType.Uncolored;
				BuildTransaction(ctx, group, group.Builders, group.Coins.Values.OfType<Coin>(), Money.Zero);
			}
			ctx.Finish();

			if(sign)
			{
				SignTransactionInPlace(ctx.Transaction, sigHash);
			}
			return ctx.Transaction;
		}
Example #10
0
		IMoney SetColoredChange(TransactionBuildingContext ctx)
		{
			var changeAmount = (AssetMoney)ctx.ChangeAmount;
			if(changeAmount.Quantity == 0)
				return changeAmount;
			var marker = ctx.GetColorMarker(false);
			var script = ctx.Group.ChangeScript[(int)ChangeType.Colored];
			var txout = ctx.Transaction.AddOutput(new TxOut(GetDust(script), script));
			marker.SetQuantity(ctx.Transaction.Outputs.Count - 2, changeAmount.Quantity);
			ctx.AdditionalFees += txout.Value;
			return changeAmount;
		}
Example #11
0
			IMoney SetChange(TransactionBuildingContext ctx)
			{
				var changeAmount = (Money)ctx.ChangeAmount;
				if(changeAmount.Satoshi == 0)
					return Money.Zero;
				ctx.Transaction.AddOutput(new TxOut(changeAmount, ctx.Group.ChangeScript[(int)ChangeType.Uncolored]));
				return changeAmount;
			}
Example #12
0
			public void RestoreMemento(TransactionBuildingContext memento)
			{
				_Marker = memento._Marker == null ? null : new ColorMarker(memento._Marker.GetScript());
				Transaction = memento.Transaction.Clone();
				AdditionalFees = memento.AdditionalFees;
			}
Example #13
0
			public TransactionBuildingContext CreateMemento()
			{
				var memento = new TransactionBuildingContext(Builder);
				memento.RestoreMemento(this);
				return memento;
			}