public TransactionBuildingContext CreateMemento() { var memento = new TransactionBuildingContext(Builder); memento.RestoreMemento(this); return(memento); }
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); }
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 TransactionBuildingContext CreateMemento() { var memento = new TransactionBuildingContext(Builder); memento.RestoreMemento(this); return memento; }