lnwallet+sweep: extend the WitnessGenerator type to return an InputScript
In this commit, we extend the WitnessGenerator type to now return an InputScript. This allows it to be more encompassing, as now callers can expect a sigScript to be populated if the input being swept requires a sigScript field. Along the way, we've also renamed input.BuildWitness to input.CraftInputScript. We also take a step towards allowing the sweeper to sweep transactions for n2pwkh outputs. We do so by modifying the BuiltWitness method to instead return an InputScript. Additionally, when populating inputs if a sigScript is present, it will now be populated.
This commit is contained in:
parent
bd9ebbd5af
commit
c18e166e03
@ -807,13 +807,13 @@ func (bo *breachedOutput) SignDesc() *lnwallet.SignDescriptor {
|
|||||||
return &bo.signDesc
|
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
|
// breached output. It does so by first generating and memoizing the witness
|
||||||
// generation function, which parameterized primarily by the witness type and
|
// generation function, which parameterized primarily by the witness type and
|
||||||
// sign descriptor. The method then returns the witness computed by invoking
|
// sign descriptor. The method then returns the witness computed by invoking
|
||||||
// this function on the first and subsequent calls.
|
// this function on the first and subsequent calls.
|
||||||
func (bo *breachedOutput) BuildWitness(signer lnwallet.Signer, txn *wire.MsgTx,
|
func (bo *breachedOutput) CraftInputScript(signer lnwallet.Signer, txn *wire.MsgTx,
|
||||||
hashCache *txscript.TxSigHashes, txinIdx int) ([][]byte, error) {
|
hashCache *txscript.TxSigHashes, txinIdx int) (*lnwallet.InputScript, error) {
|
||||||
|
|
||||||
// First, we ensure that the witness generation function has been
|
// First, we ensure that the witness generation function has been
|
||||||
// initialized for this breached output.
|
// 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
|
// First, we construct a valid witness for this outpoint and
|
||||||
// transaction using the SpendableOutput's witness generation
|
// transaction using the SpendableOutput's witness generation
|
||||||
// function.
|
// function.
|
||||||
witness, err := so.BuildWitness(b.cfg.Signer, txn, hashCache,
|
inputScript, err := so.CraftInputScript(
|
||||||
idx)
|
b.cfg.Signer, txn, hashCache, idx,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then, we add the witness to the transaction at the
|
// Then, we add the witness to the transaction at the
|
||||||
// appropriate txin index.
|
// appropriate txin index.
|
||||||
txn.TxIn[idx].Witness = witness
|
txn.TxIn[idx].Witness = inputScript.Witness
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -110,18 +110,22 @@ func (wt WitnessType) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WitnessGenerator represents a function which is able to generate the final
|
// WitnessGenerator represents a function which is able to generate the final
|
||||||
// witness for a particular public key script. This function acts as an
|
// witness for a particular public key script. Additionally, if required, this
|
||||||
// abstraction layer, hiding the details of the underlying script.
|
// 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,
|
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
|
// GenWitnessFunc will return a WitnessGenerator function that an output uses
|
||||||
// uses to generate the witness for a sweep transaction.
|
// 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,
|
func (wt WitnessType) GenWitnessFunc(signer Signer,
|
||||||
descriptor *SignDescriptor) WitnessGenerator {
|
descriptor *SignDescriptor) WitnessGenerator {
|
||||||
|
|
||||||
return func(tx *wire.MsgTx, hc *txscript.TxSigHashes,
|
return func(tx *wire.MsgTx, hc *txscript.TxSigHashes,
|
||||||
inputIndex int) ([][]byte, error) {
|
inputIndex int) (*InputScript, error) {
|
||||||
|
|
||||||
desc := descriptor
|
desc := descriptor
|
||||||
desc.SigHashes = hc
|
desc.SigHashes = hc
|
||||||
@ -129,34 +133,98 @@ func (wt WitnessType) GenWitnessFunc(signer Signer,
|
|||||||
|
|
||||||
switch wt {
|
switch wt {
|
||||||
case CommitmentTimeLock:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
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:
|
case HtlcOfferedRemoteTimeout:
|
||||||
// We pass in a value of -1 for the timeout, as we
|
// We pass in a value of -1 for the timeout, as we
|
||||||
// expect the caller to have already set the lock time
|
// expect the caller to have already set the lock time
|
||||||
// value.
|
// 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:
|
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:
|
default:
|
||||||
return nil, fmt.Errorf("unknown witness type: %v", wt)
|
return nil, fmt.Errorf("unknown witness type: %v", wt)
|
||||||
|
@ -6,7 +6,10 @@ import (
|
|||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"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 {
|
type Input interface {
|
||||||
// Outpoint returns the reference to the output being spent, used to
|
// Outpoint returns the reference to the output being spent, used to
|
||||||
// construct the corresponding transaction input.
|
// construct the corresponding transaction input.
|
||||||
@ -21,12 +24,14 @@ type Input interface {
|
|||||||
// that spends this output.
|
// that spends this output.
|
||||||
SignDesc() *lnwallet.SignDescriptor
|
SignDesc() *lnwallet.SignDescriptor
|
||||||
|
|
||||||
// BuildWitness returns a valid witness allowing this output to be
|
// CraftInputScript returns a valid set of input scripts allowing this
|
||||||
// spent, the witness should be attached to the transaction at the
|
// output to be spent. The returns input scripts should target the
|
||||||
// location determined by the given `txinIdx`.
|
// input at location txIndex within the passed transaction. The input
|
||||||
BuildWitness(signer lnwallet.Signer, txn *wire.MsgTx,
|
// scripts generated by this method support spending p2wkh, p2wsh, and
|
||||||
|
// also nested p2sh outputs.
|
||||||
|
CraftInputScript(signer lnwallet.Signer, txn *wire.MsgTx,
|
||||||
hashCache *txscript.TxSigHashes,
|
hashCache *txscript.TxSigHashes,
|
||||||
txinIdx int) ([][]byte, error)
|
txinIdx int) (*lnwallet.InputScript, error)
|
||||||
|
|
||||||
// BlocksToMaturity returns the relative timelock, as a number of
|
// BlocksToMaturity returns the relative timelock, as a number of
|
||||||
// blocks, that must be built on top of the confirmation height before
|
// 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
|
// CraftInputScript returns a valid set of input scripts allowing this output
|
||||||
// breached output. It does so by generating the witness generation function,
|
// to be spent. The returns input scripts should target the input at location
|
||||||
// which is parameterized primarily by the witness type and sign descriptor.
|
// txIndex within the passed transaction. The input scripts generated by this
|
||||||
// The method then returns the witness computed by invoking this function.
|
// method support spending p2wkh, p2wsh, and also nested p2sh outputs.
|
||||||
func (bi *BaseInput) BuildWitness(signer lnwallet.Signer, txn *wire.MsgTx,
|
func (bi *BaseInput) CraftInputScript(signer lnwallet.Signer, txn *wire.MsgTx,
|
||||||
hashCache *txscript.TxSigHashes, txinIdx int) ([][]byte, error) {
|
hashCache *txscript.TxSigHashes, txinIdx int) (*lnwallet.InputScript, error) {
|
||||||
|
|
||||||
witnessFunc := bi.witnessType.GenWitnessFunc(
|
witnessFunc := bi.witnessType.GenWitnessFunc(
|
||||||
signer, bi.SignDesc(),
|
signer, bi.SignDesc(),
|
||||||
@ -138,20 +143,27 @@ func MakeHtlcSucceedInput(outpoint *wire.OutPoint,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BuildWitness computes a valid witness that allows us to spend from the
|
// CraftInputScript returns a valid set of input scripts allowing this output
|
||||||
// breached output. For HtlcSpendInput it will need to make the preimage part
|
// to be spent. The returns input scripts should target the input at location
|
||||||
// of the witness.
|
// txIndex within the passed transaction. The input scripts generated by this
|
||||||
func (h *HtlcSucceedInput) BuildWitness(signer lnwallet.Signer, txn *wire.MsgTx,
|
// method support spending p2wkh, p2wsh, and also nested p2sh outputs.
|
||||||
hashCache *txscript.TxSigHashes, txinIdx int) ([][]byte, error) {
|
func (h *HtlcSucceedInput) CraftInputScript(signer lnwallet.Signer, txn *wire.MsgTx,
|
||||||
|
hashCache *txscript.TxSigHashes, txinIdx int) (*lnwallet.InputScript, error) {
|
||||||
|
|
||||||
desc := h.signDesc
|
desc := h.signDesc
|
||||||
desc.SigHashes = hashCache
|
desc.SigHashes = hashCache
|
||||||
desc.InputIndex = txinIdx
|
desc.InputIndex = txinIdx
|
||||||
|
|
||||||
return lnwallet.SenderHtlcSpendRedeem(
|
witness, err := lnwallet.SenderHtlcSpendRedeem(
|
||||||
signer, &desc, txn,
|
signer, &desc, txn, h.preimage,
|
||||||
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
|
// BlocksToMaturity returns the relative timelock, as a number of blocks, that
|
||||||
|
@ -216,25 +216,29 @@ func createSweepTx(inputs []Input, outputPkScript []byte,
|
|||||||
|
|
||||||
hashCache := txscript.NewTxSigHashes(sweepTx)
|
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.
|
// function to generate the final witness required for spending.
|
||||||
addWitness := func(idx int, tso Input) error {
|
addInputScript := func(idx int, tso Input) error {
|
||||||
witness, err := tso.BuildWitness(
|
inputScript, err := tso.CraftInputScript(
|
||||||
signer, sweepTx, hashCache, idx,
|
signer, sweepTx, hashCache, idx,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
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
|
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.
|
// within the sweeping transaction.
|
||||||
for i, input := range inputs {
|
for i, input := range inputs {
|
||||||
if err := addWitness(i, input); err != nil {
|
if err := addInputScript(i, input); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user