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; }
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; }
public Money Build(TransactionBuildingContext ctx) { ctx.Transaction.Outputs.Add(_TxOut); return(_TxOut.Value); }
/// <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; }
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; }
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; }
public TransactionBuildingContext CreateMemento() { var memento = new TransactionBuildingContext(Builder); memento.RestoreMemento(this); return memento; }