github.com/decred/dcrlnd@v0.7.6/sweep/txgenerator.go (about)

     1  package sweep
     2  
     3  import (
     4  	"fmt"
     5  	"sort"
     6  	"strings"
     7  
     8  	"github.com/decred/dcrd/blockchain/standalone/v2"
     9  	"github.com/decred/dcrd/chaincfg/v3"
    10  	"github.com/decred/dcrd/dcrutil/v4"
    11  	"github.com/decred/dcrd/wire"
    12  	"github.com/decred/dcrlnd/input"
    13  	"github.com/decred/dcrlnd/lnwallet"
    14  	"github.com/decred/dcrlnd/lnwallet/chainfee"
    15  )
    16  
    17  var (
    18  	// DefaultMaxInputsPerTx specifies the default maximum number of inputs
    19  	// allowed in a single sweep tx. If more need to be swept, multiple txes
    20  	// are created and published.
    21  	DefaultMaxInputsPerTx = 100
    22  )
    23  
    24  // txInput is an interface that provides the input data required for tx
    25  // generation.
    26  type txInput interface {
    27  	input.Input
    28  	parameters() Params
    29  }
    30  
    31  // inputSet is a set of inputs that can be used as the basis to generate a tx
    32  // on.
    33  type inputSet []input.Input
    34  
    35  // generateInputPartitionings goes through all given inputs and constructs sets
    36  // of inputs that can be used to generate a sensible transaction. Each set
    37  // contains up to the configured maximum number of inputs. Negative yield
    38  // inputs are skipped. No input sets with a total value after fees below the
    39  // dust limit are returned.
    40  func generateInputPartitionings(sweepableInputs []txInput,
    41  	feePerKB chainfee.AtomPerKByte, maxInputsPerTx int,
    42  	wallet Wallet) ([]inputSet, error) {
    43  
    44  	// Sort input by yield. We will start constructing input sets starting
    45  	// with the highest yield inputs. This is to prevent the construction
    46  	// of a set with an output below the dust limit, causing the sweep
    47  	// process to stop, while there are still higher value inputs
    48  	// available. It also allows us to stop evaluating more inputs when the
    49  	// first input in this ordering is encountered with a negative yield.
    50  	//
    51  	// Yield is calculated as the difference between value and added fee
    52  	// for this input. The fee calculation excludes fee components that are
    53  	// common to all inputs, as those wouldn't influence the order. The
    54  	// single component that is differentiating is witness size.
    55  	//
    56  	// For witness size, the upper limit is taken. The actual size depends
    57  	// on the signature length, which is not known yet at this point.
    58  	yields := make(map[wire.OutPoint]int64)
    59  	for _, input := range sweepableInputs {
    60  		size, _, err := input.WitnessType().SizeUpperBound()
    61  		if err != nil {
    62  			return nil, fmt.Errorf(
    63  				"failed adding input size: %v", err)
    64  		}
    65  
    66  		yields[*input.OutPoint()] = input.SignDesc().Output.Value -
    67  			int64(feePerKB.FeeForSize(size))
    68  	}
    69  
    70  	sort.Slice(sweepableInputs, func(i, j int) bool {
    71  		// Because of the specific ordering and termination condition
    72  		// that is described above, we place force sweeps at the start
    73  		// of the list. Otherwise we can't be sure that they will be
    74  		// included in an input set.
    75  		if sweepableInputs[i].parameters().Force {
    76  			return true
    77  		}
    78  
    79  		return yields[*sweepableInputs[i].OutPoint()] >
    80  			yields[*sweepableInputs[j].OutPoint()]
    81  	})
    82  
    83  	// Select blocks of inputs up to the configured maximum number.
    84  	var sets []inputSet
    85  	for len(sweepableInputs) > 0 {
    86  		// Start building a set of positive-yield tx inputs under the
    87  		// condition that the tx will be published with the specified
    88  		// fee rate.
    89  		txInputs := newTxInputSet(wallet, feePerKB, maxInputsPerTx)
    90  
    91  		// From the set of sweepable inputs, keep adding inputs to the
    92  		// input set until the tx output value no longer goes up or the
    93  		// maximum number of inputs is reached.
    94  		txInputs.addPositiveYieldInputs(sweepableInputs)
    95  
    96  		// If there are no positive yield inputs, we can stop here.
    97  		inputCount := len(txInputs.inputs)
    98  		if inputCount == 0 {
    99  			return sets, nil
   100  		}
   101  
   102  		// Check the current output value and add wallet utxos if
   103  		// needed to push the output value to the lower limit.
   104  		if err := txInputs.tryAddWalletInputsIfNeeded(); err != nil {
   105  			return nil, err
   106  		}
   107  
   108  		// If the output value of this block of inputs does not reach
   109  		// the dust limit, stop sweeping. Because of the sorting,
   110  		// continuing with the remaining inputs will only lead to sets
   111  		// with an even lower output value.
   112  		if !txInputs.enoughInput() {
   113  			// The change output is always a p2pkh here.
   114  			dl := lnwallet.DustLimitForSize(input.P2PKHPkScriptSize)
   115  			log.Debugf("Set value %v (r=%v, c=%v) below dust "+
   116  				"limit of %v", txInputs.totalOutput(),
   117  				txInputs.requiredOutput, txInputs.changeOutput,
   118  				dl)
   119  			return sets, nil
   120  		}
   121  
   122  		log.Infof("Candidate sweep set of size=%v (+%v wallet inputs), "+
   123  			"has yield=%v, size=%v",
   124  			inputCount, len(txInputs.inputs)-inputCount,
   125  			txInputs.totalOutput()-txInputs.walletInputTotal,
   126  			txInputs.sizeEstimate(true).size())
   127  
   128  		sets = append(sets, txInputs.inputs)
   129  		sweepableInputs = sweepableInputs[inputCount:]
   130  	}
   131  
   132  	return sets, nil
   133  }
   134  
   135  // createSweepTx builds a signed tx spending the inputs to the given outputs,
   136  // sending any leftover change to the change script.
   137  func createSweepTx(inputs []input.Input, outputs []*wire.TxOut,
   138  	changePkScript []byte, currentBlockHeight uint32,
   139  	feePerKB chainfee.AtomPerKByte, signer input.Signer,
   140  	netParams *chaincfg.Params) (*wire.MsgTx, error) {
   141  
   142  	inputs, estimator := getSizeEstimate(inputs, outputs, feePerKB)
   143  	txFee := estimator.fee()
   144  
   145  	var (
   146  		// Create the sweep transaction that we will be building. We
   147  		// use version 2 as it is required for CSV.
   148  		sweepTx = wire.NewMsgTx()
   149  
   150  		// Track whether any of the inputs require a certain locktime.
   151  		locktime = int32(-1)
   152  
   153  		// We keep track of total input amount, and required output
   154  		// amount to use for calculating the change amount below.
   155  		totalInput     dcrutil.Amount
   156  		requiredOutput dcrutil.Amount
   157  
   158  		// We'll add the inputs as we go so we know the final ordering
   159  		// of inputs to sign.
   160  		idxs []input.Input
   161  	)
   162  
   163  	sweepTx.Version = 2
   164  
   165  	// We start by adding all inputs that commit to an output. We do this
   166  	// since the input and output index must stay the same for the
   167  	// signatures to be valid.
   168  	for _, o := range inputs {
   169  		if o.RequiredTxOut() == nil {
   170  			continue
   171  		}
   172  
   173  		idxs = append(idxs, o)
   174  		sweepTx.AddTxIn(&wire.TxIn{
   175  			PreviousOutPoint: *o.OutPoint(),
   176  			Sequence:         o.BlocksToMaturity(),
   177  			ValueIn:          o.SignDesc().Output.Value,
   178  		})
   179  		sweepTx.AddTxOut(o.RequiredTxOut())
   180  
   181  		if lt, ok := o.RequiredLockTime(); ok {
   182  			// If another input commits to a different locktime,
   183  			// they cannot be combined in the same transcation.
   184  			if locktime != -1 && locktime != int32(lt) {
   185  				return nil, fmt.Errorf("incompatible locktime")
   186  			}
   187  
   188  			locktime = int32(lt)
   189  		}
   190  
   191  		totalInput += dcrutil.Amount(o.SignDesc().Output.Value)
   192  		requiredOutput += dcrutil.Amount(o.RequiredTxOut().Value)
   193  	}
   194  
   195  	// Sum up the value contained in the remaining inputs, and add them to
   196  	// the sweep transaction.
   197  	for _, o := range inputs {
   198  		if o.RequiredTxOut() != nil {
   199  			continue
   200  		}
   201  
   202  		idxs = append(idxs, o)
   203  		sweepTx.AddTxIn(&wire.TxIn{
   204  			PreviousOutPoint: *o.OutPoint(),
   205  			Sequence:         o.BlocksToMaturity(),
   206  		})
   207  
   208  		if lt, ok := o.RequiredLockTime(); ok {
   209  			if locktime != -1 && locktime != int32(lt) {
   210  				return nil, fmt.Errorf("incompatible locktime")
   211  			}
   212  
   213  			locktime = int32(lt)
   214  		}
   215  
   216  		totalInput += dcrutil.Amount(o.SignDesc().Output.Value)
   217  	}
   218  
   219  	// Add the outputs given, if any.
   220  	for _, o := range outputs {
   221  		sweepTx.AddTxOut(o)
   222  		requiredOutput += dcrutil.Amount(o.Value)
   223  	}
   224  
   225  	if requiredOutput+txFee > totalInput {
   226  		return nil, fmt.Errorf("insufficient input to create sweep "+
   227  			"tx: input_sum=%v, output_sum=%v", totalInput,
   228  			requiredOutput+txFee)
   229  	}
   230  
   231  	// The value remaining after the required output and fees, go to
   232  	// change. Not that this fee is what we would have to pay in case the
   233  	// sweep tx has a change output.
   234  	changeAmt := totalInput - requiredOutput - txFee
   235  
   236  	// We'll calculate the dust limit for the given changePkScript since it
   237  	// is variable.
   238  	changeLimit := lnwallet.DustLimitForSize(int64(len(changePkScript)))
   239  
   240  	// The txn will sweep the amount after fees to the pkscript generated
   241  	// above.
   242  	if changeAmt >= changeLimit {
   243  		sweepTx.AddTxOut(&wire.TxOut{
   244  			PkScript: changePkScript,
   245  			Value:    int64(changeAmt),
   246  		})
   247  	} else {
   248  		log.Infof("Change amt %v below dustlimit %v, not adding "+
   249  			"change output", changeAmt, changeLimit)
   250  	}
   251  
   252  	// We'll default to using the current block height as locktime, if none
   253  	// of the inputs commits to a different locktime.
   254  	sweepTx.LockTime = currentBlockHeight
   255  	if locktime != -1 {
   256  		sweepTx.LockTime = uint32(locktime)
   257  	}
   258  
   259  	// Before signing the transaction, check to ensure that it meets some
   260  	// basic validity requirements.
   261  	//
   262  	// TODO(conner): add more control to sanity checks, allowing us to
   263  	// delay spending "problem" outputs, e.g. possibly batching with other
   264  	// classes if fees are too low.
   265  	btx := dcrutil.NewTx(sweepTx)
   266  	if err := standalone.CheckTransactionSanity(btx.MsgTx(), uint64(netParams.MaxTxSize)); err != nil {
   267  		return nil, fmt.Errorf("error checking sweepTx sanity: %v", err)
   268  	}
   269  
   270  	// With all the inputs in place, use each output's unique input script
   271  	// function to generate the final witness required for spending.
   272  	addInputScript := func(idx int, tso input.Input) error {
   273  		inputScript, err := tso.CraftInputScript(
   274  			signer, sweepTx, idx,
   275  		)
   276  		if err != nil {
   277  			return fmt.Errorf("error building witness for input %d of "+
   278  				"type %s: %v", idx, tso.WitnessType().String(), err)
   279  		}
   280  
   281  		sigScript, err := input.WitnessStackToSigScript(inputScript.Witness)
   282  		if err != nil {
   283  			return err
   284  		}
   285  		sweepTx.TxIn[idx].SignatureScript = sigScript
   286  
   287  		return nil
   288  	}
   289  
   290  	for idx, inp := range idxs {
   291  		if err := addInputScript(idx, inp); err != nil {
   292  			return nil, err
   293  		}
   294  	}
   295  
   296  	log.Infof("Creating sweep transaction %v for %v inputs (%s) "+
   297  		"using %v atoms/kB, tx_size=%v tx_fee=%v, parents_count=%v, "+
   298  		"parents_fee=%v parents_size=%v",
   299  		sweepTx.TxHash(), len(inputs),
   300  		inputTypeSummary(inputs), int64(feePerKB),
   301  		estimator.size(), txFee,
   302  		len(estimator.parents), estimator.parentsFee,
   303  		estimator.parentsSize,
   304  	)
   305  
   306  	return sweepTx, nil
   307  }
   308  
   309  // getSizeEstimate returns a weight estimate for the given inputs.
   310  // Additionally, it returns counts for the number of csv and cltv inputs.
   311  func getSizeEstimate(inputs []input.Input, outputs []*wire.TxOut,
   312  	feeRate chainfee.AtomPerKByte) ([]input.Input, *sizeEstimator) {
   313  
   314  	// We initialize a weight estimator so we can accurately asses the
   315  	// amount of fees we need to pay for this sweep transaction.
   316  	//
   317  	// TODO(roasbeef): can be more intelligent about buffering outputs to
   318  	// be more efficient on-chain.
   319  	sizeEstimate := newSizeEstimator(feeRate)
   320  
   321  	// Our sweep transaction will always pay to the given set of outputs.
   322  	for _, o := range outputs {
   323  		sizeEstimate.addOutput(o)
   324  	}
   325  
   326  	// If there is any leftover change after paying to the given outputs
   327  	// and required outputs, it will go to a single p2pkh address.
   328  	// This will be our change address, so ensure it contributes to our
   329  	// size estimate. Note that if we have other outputs, we might end up
   330  	// creating a sweep tx without a change output. It is okay to add the
   331  	// change output to the size estimate regardless, since the estimated
   332  	// fee will just be subtracted from this already dust output, and
   333  	// trimmed.
   334  	sizeEstimate.addP2PKHOutput()
   335  
   336  	// For each output, use its witness type to determine the estimate
   337  	// size of its witness, and add it to the proper set of spendable
   338  	// outputs.
   339  	var sweepInputs []input.Input
   340  	for i := range inputs {
   341  		inp := inputs[i]
   342  
   343  		err := sizeEstimate.add(inp)
   344  		if err != nil {
   345  			log.Warn(err)
   346  
   347  			// Skip inputs for which no size estimate can be
   348  			// given.
   349  			continue
   350  		}
   351  
   352  		// If this input comes with a committed output, add that as
   353  		// well.
   354  		if inp.RequiredTxOut() != nil {
   355  			sizeEstimate.addOutput(inp.RequiredTxOut())
   356  		}
   357  
   358  		sweepInputs = append(sweepInputs, inp)
   359  	}
   360  
   361  	return sweepInputs, sizeEstimate
   362  }
   363  
   364  // inputSummary returns a string containing a human readable summary about the
   365  // witness types of a list of inputs.
   366  func inputTypeSummary(inputs []input.Input) string {
   367  	// Sort inputs by witness type.
   368  	sortedInputs := make([]input.Input, len(inputs))
   369  	copy(sortedInputs, inputs)
   370  	sort.Slice(sortedInputs, func(i, j int) bool {
   371  		return sortedInputs[i].WitnessType().String() <
   372  			sortedInputs[j].WitnessType().String()
   373  	})
   374  
   375  	var parts []string
   376  	for _, i := range sortedInputs {
   377  		part := fmt.Sprintf("%v (%v)",
   378  			*i.OutPoint(), i.WitnessType())
   379  
   380  		parts = append(parts, part)
   381  	}
   382  	return strings.Join(parts, ", ")
   383  }