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 }