diff --git a/breacharbiter.go b/breacharbiter.go index 45c4bafc1..269a7b124 100644 --- a/breacharbiter.go +++ b/breacharbiter.go @@ -807,13 +807,13 @@ func (bo *breachedOutput) SignDesc() *lnwallet.SignDescriptor { return &bo.signDesc } -// BuildWitness computes a valid witness that allows us to spend from the +// CraftInputScript computes a valid witness that allows us to spend from the // breached output. It does so by first generating and memoizing the witness // generation function, which parameterized primarily by the witness type and // sign descriptor. The method then returns the witness computed by invoking // this function on the first and subsequent calls. -func (bo *breachedOutput) BuildWitness(signer lnwallet.Signer, txn *wire.MsgTx, - hashCache *txscript.TxSigHashes, txinIdx int) ([][]byte, error) { +func (bo *breachedOutput) CraftInputScript(signer lnwallet.Signer, txn *wire.MsgTx, + hashCache *txscript.TxSigHashes, txinIdx int) (*lnwallet.InputScript, error) { // First, we ensure that the witness generation function has been // initialized for this breached output. @@ -1082,15 +1082,16 @@ func (b *breachArbiter) sweepSpendableOutputsTxn(txWeight int64, // First, we construct a valid witness for this outpoint and // transaction using the SpendableOutput's witness generation // function. - witness, err := so.BuildWitness(b.cfg.Signer, txn, hashCache, - idx) + inputScript, err := so.CraftInputScript( + b.cfg.Signer, txn, hashCache, idx, + ) if err != nil { return err } // Then, we add the witness to the transaction at the // appropriate txin index. - txn.TxIn[idx].Witness = witness + txn.TxIn[idx].Witness = inputScript.Witness return nil } diff --git a/lnwallet/witnessgen.go b/lnwallet/witnessgen.go index 00c3fab09..f2ff9236c 100644 --- a/lnwallet/witnessgen.go +++ b/lnwallet/witnessgen.go @@ -110,18 +110,22 @@ func (wt WitnessType) String() string { } // WitnessGenerator represents a function which is able to generate the final -// witness for a particular public key script. This function acts as an -// abstraction layer, hiding the details of the underlying script. +// witness for a particular public key script. Additionally, if required, this +// function will also return the sigScript for spending nested P2SH witness +// outputs. This function acts as an abstraction layer, hiding the details of +// the underlying script. type WitnessGenerator func(tx *wire.MsgTx, hc *txscript.TxSigHashes, - inputIndex int) ([][]byte, error) + inputIndex int) (*InputScript, error) -// GenWitnessFunc will return a WitnessGenerator function that an output -// uses to generate the witness for a sweep transaction. +// GenWitnessFunc will return a WitnessGenerator function that an output uses +// to generate the witness and optionally the sigScript for a sweep +// transaction. The sigScript will be generated if the witness type warrants +// one for spending, such as the NestedWitnessKeyHash witness type. func (wt WitnessType) GenWitnessFunc(signer Signer, descriptor *SignDescriptor) WitnessGenerator { return func(tx *wire.MsgTx, hc *txscript.TxSigHashes, - inputIndex int) ([][]byte, error) { + inputIndex int) (*InputScript, error) { desc := descriptor desc.SigHashes = hc @@ -129,34 +133,98 @@ func (wt WitnessType) GenWitnessFunc(signer Signer, switch wt { case CommitmentTimeLock: - return CommitSpendTimeout(signer, desc, tx) + witness, err := CommitSpendTimeout(signer, desc, tx) + if err != nil { + return nil, err + } + + return &InputScript{ + Witness: witness, + }, nil case CommitmentNoDelay: - return CommitSpendNoDelay(signer, desc, tx) + witness, err := CommitSpendNoDelay(signer, desc, tx) + if err != nil { + return nil, err + } + + return &InputScript{ + Witness: witness, + }, nil case CommitmentRevoke: - return CommitSpendRevoke(signer, desc, tx) + witness, err := CommitSpendRevoke(signer, desc, tx) + if err != nil { + return nil, err + } + + return &InputScript{ + Witness: witness, + }, nil case HtlcOfferedRevoke: - return ReceiverHtlcSpendRevoke(signer, desc, tx) + witness, err := ReceiverHtlcSpendRevoke(signer, desc, tx) + if err != nil { + return nil, err + } + + return &InputScript{ + Witness: witness, + }, nil case HtlcAcceptedRevoke: - return SenderHtlcSpendRevoke(signer, desc, tx) + witness, err := SenderHtlcSpendRevoke(signer, desc, tx) + if err != nil { + return nil, err + } + + return &InputScript{ + Witness: witness, + }, nil case HtlcOfferedTimeoutSecondLevel: - return HtlcSecondLevelSpend(signer, desc, tx) + witness, err := HtlcSecondLevelSpend(signer, desc, tx) + if err != nil { + return nil, err + } + + return &InputScript{ + Witness: witness, + }, nil case HtlcAcceptedSuccessSecondLevel: - return HtlcSecondLevelSpend(signer, desc, tx) + witness, err := HtlcSecondLevelSpend(signer, desc, tx) + if err != nil { + return nil, err + } + + return &InputScript{ + Witness: witness, + }, nil case HtlcOfferedRemoteTimeout: // We pass in a value of -1 for the timeout, as we // expect the caller to have already set the lock time // value. - return receiverHtlcSpendTimeout(signer, desc, tx, -1) + witness, err := receiverHtlcSpendTimeout(signer, desc, tx, -1) + if err != nil { + return nil, err + } + + return &InputScript{ + Witness: witness, + }, nil case HtlcSecondLevelRevoke: - return htlcSpendRevoke(signer, desc, tx) + witness, err := htlcSpendRevoke(signer, desc, tx) + if err != nil { + return nil, err + } + + return &InputScript{ + Witness: witness, + }, nil + default: return nil, fmt.Errorf("unknown witness type: %v", wt) diff --git a/sweep/input.go b/sweep/input.go index 57ee79393..57a954df7 100644 --- a/sweep/input.go +++ b/sweep/input.go @@ -6,7 +6,10 @@ import ( "github.com/lightningnetwork/lnd/lnwallet" ) -// Input contains all data needed to construct a sweep tx input. +// Input represents an abstract UTXO which is to be spent using a sweeping +// transaction. The method provided give the caller all information needed to +// construct a valid input within a sweeping transaction to sweep this +// lingering UTXO. type Input interface { // Outpoint returns the reference to the output being spent, used to // construct the corresponding transaction input. @@ -21,12 +24,14 @@ type Input interface { // that spends this output. SignDesc() *lnwallet.SignDescriptor - // BuildWitness returns a valid witness allowing this output to be - // spent, the witness should be attached to the transaction at the - // location determined by the given `txinIdx`. - BuildWitness(signer lnwallet.Signer, txn *wire.MsgTx, + // CraftInputScript returns a valid set of input scripts allowing this + // output to be spent. The returns input scripts should target the + // input at location txIndex within the passed transaction. The input + // scripts generated by this method support spending p2wkh, p2wsh, and + // also nested p2sh outputs. + CraftInputScript(signer lnwallet.Signer, txn *wire.MsgTx, hashCache *txscript.TxSigHashes, - txinIdx int) ([][]byte, error) + txinIdx int) (*lnwallet.InputScript, error) // BlocksToMaturity returns the relative timelock, as a number of // blocks, that must be built on top of the confirmation height before @@ -91,12 +96,12 @@ func MakeBaseInput(outpoint *wire.OutPoint, witnessType lnwallet.WitnessType, } } -// BuildWitness computes a valid witness that allows us to spend from the -// breached output. It does so by generating the witness generation function, -// which is parameterized primarily by the witness type and sign descriptor. -// The method then returns the witness computed by invoking this function. -func (bi *BaseInput) BuildWitness(signer lnwallet.Signer, txn *wire.MsgTx, - hashCache *txscript.TxSigHashes, txinIdx int) ([][]byte, error) { +// CraftInputScript returns a valid set of input scripts allowing this output +// to be spent. The returns input scripts should target the input at location +// txIndex within the passed transaction. The input scripts generated by this +// method support spending p2wkh, p2wsh, and also nested p2sh outputs. +func (bi *BaseInput) CraftInputScript(signer lnwallet.Signer, txn *wire.MsgTx, + hashCache *txscript.TxSigHashes, txinIdx int) (*lnwallet.InputScript, error) { witnessFunc := bi.witnessType.GenWitnessFunc( signer, bi.SignDesc(), @@ -138,20 +143,27 @@ func MakeHtlcSucceedInput(outpoint *wire.OutPoint, } } -// BuildWitness computes a valid witness that allows us to spend from the -// breached output. For HtlcSpendInput it will need to make the preimage part -// of the witness. -func (h *HtlcSucceedInput) BuildWitness(signer lnwallet.Signer, txn *wire.MsgTx, - hashCache *txscript.TxSigHashes, txinIdx int) ([][]byte, error) { +// CraftInputScript returns a valid set of input scripts allowing this output +// to be spent. The returns input scripts should target the input at location +// txIndex within the passed transaction. The input scripts generated by this +// method support spending p2wkh, p2wsh, and also nested p2sh outputs. +func (h *HtlcSucceedInput) CraftInputScript(signer lnwallet.Signer, txn *wire.MsgTx, + hashCache *txscript.TxSigHashes, txinIdx int) (*lnwallet.InputScript, error) { desc := h.signDesc desc.SigHashes = hashCache desc.InputIndex = txinIdx - return lnwallet.SenderHtlcSpendRedeem( - signer, &desc, txn, - h.preimage, + witness, err := lnwallet.SenderHtlcSpendRedeem( + signer, &desc, txn, h.preimage, ) + if err != nil { + return nil, err + } + + return &lnwallet.InputScript{ + Witness: witness, + }, nil } // BlocksToMaturity returns the relative timelock, as a number of blocks, that diff --git a/sweep/txgenerator.go b/sweep/txgenerator.go index e173706a2..e4ec37dfe 100644 --- a/sweep/txgenerator.go +++ b/sweep/txgenerator.go @@ -216,25 +216,29 @@ func createSweepTx(inputs []Input, outputPkScript []byte, hashCache := txscript.NewTxSigHashes(sweepTx) - // With all the inputs in place, use each output's unique witness + // With all the inputs in place, use each output's unique input script // function to generate the final witness required for spending. - addWitness := func(idx int, tso Input) error { - witness, err := tso.BuildWitness( + addInputScript := func(idx int, tso Input) error { + inputScript, err := tso.CraftInputScript( signer, sweepTx, hashCache, idx, ) if err != nil { return err } - sweepTx.TxIn[idx].Witness = witness + sweepTx.TxIn[idx].Witness = inputScript.Witness + + if len(inputScript.SigScript) != 0 { + sweepTx.TxIn[idx].SignatureScript = inputScript.SigScript + } return nil } - // Finally we'll attach a valid witness to each csv and cltv input + // Finally we'll attach a valid input script to each csv and cltv input // within the sweeping transaction. for i, input := range inputs { - if err := addWitness(i, input); err != nil { + if err := addInputScript(i, input); err != nil { return nil, err } }