mirror of
https://github.com/lightningnetwork/lnd.git
synced 2025-06-05 04:30:16 +02:00
multi: use prev output fetcher where possible
This commit is contained in:
parent
72c9582b85
commit
f130eddb92
@ -1096,11 +1096,15 @@ func (bo *breachedOutput) SignDesc() *input.SignDescriptor {
|
|||||||
// 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) CraftInputScript(signer input.Signer, txn *wire.MsgTx,
|
func (bo *breachedOutput) CraftInputScript(signer input.Signer, txn *wire.MsgTx,
|
||||||
hashCache *txscript.TxSigHashes, txinIdx int) (*input.Script, error) {
|
hashCache *txscript.TxSigHashes,
|
||||||
|
prevOutputFetcher txscript.PrevOutputFetcher,
|
||||||
|
txinIdx int) (*input.Script, 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.
|
||||||
bo.witnessFunc = bo.witnessType.WitnessGenerator(signer, bo.SignDesc())
|
signDesc := bo.SignDesc()
|
||||||
|
signDesc.PrevOutputFetcher = prevOutputFetcher
|
||||||
|
bo.witnessFunc = bo.witnessType.WitnessGenerator(signer, signDesc)
|
||||||
|
|
||||||
// Now that we have ensured that the witness generation function has
|
// Now that we have ensured that the witness generation function has
|
||||||
// been initialized, we can proceed to execute it and generate the
|
// been initialized, we can proceed to execute it and generate the
|
||||||
@ -1397,8 +1401,8 @@ func (b *BreachArbiter) sweepSpendableOutputsTxn(txWeight int64,
|
|||||||
|
|
||||||
// Compute the total amount contained in the inputs.
|
// Compute the total amount contained in the inputs.
|
||||||
var totalAmt btcutil.Amount
|
var totalAmt btcutil.Amount
|
||||||
for _, input := range inputs {
|
for _, inp := range inputs {
|
||||||
totalAmt += btcutil.Amount(input.SignDesc().Output.Value)
|
totalAmt += btcutil.Amount(inp.SignDesc().Output.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// We'll actually attempt to target inclusion within the next two
|
// We'll actually attempt to target inclusion within the next two
|
||||||
@ -1424,10 +1428,10 @@ func (b *BreachArbiter) sweepSpendableOutputsTxn(txWeight int64,
|
|||||||
|
|
||||||
// Next, we add all of the spendable outputs as inputs to the
|
// Next, we add all of the spendable outputs as inputs to the
|
||||||
// transaction.
|
// transaction.
|
||||||
for _, input := range inputs {
|
for _, inp := range inputs {
|
||||||
txn.AddTxIn(&wire.TxIn{
|
txn.AddTxIn(&wire.TxIn{
|
||||||
PreviousOutPoint: *input.OutPoint(),
|
PreviousOutPoint: *inp.OutPoint(),
|
||||||
Sequence: input.BlocksToMaturity(),
|
Sequence: inp.BlocksToMaturity(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1440,7 +1444,11 @@ func (b *BreachArbiter) sweepSpendableOutputsTxn(txWeight int64,
|
|||||||
|
|
||||||
// Create a sighash cache to improve the performance of hashing and
|
// Create a sighash cache to improve the performance of hashing and
|
||||||
// signing SigHashAll inputs.
|
// signing SigHashAll inputs.
|
||||||
hashCache := input.NewTxSigHashesV0Only(txn)
|
prevOutputFetcher, err := input.MultiPrevOutFetcher(inputs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
hashCache := txscript.NewTxSigHashes(txn, prevOutputFetcher)
|
||||||
|
|
||||||
// Create a closure that encapsulates the process of initializing a
|
// Create a closure that encapsulates the process of initializing a
|
||||||
// particular output's witness generation function, computing the
|
// particular output's witness generation function, computing the
|
||||||
@ -1452,7 +1460,7 @@ func (b *BreachArbiter) sweepSpendableOutputsTxn(txWeight int64,
|
|||||||
// transaction using the SpendableOutput's witness generation
|
// transaction using the SpendableOutput's witness generation
|
||||||
// function.
|
// function.
|
||||||
inputScript, err := so.CraftInputScript(
|
inputScript, err := so.CraftInputScript(
|
||||||
b.cfg.Signer, txn, hashCache, idx,
|
b.cfg.Signer, txn, hashCache, prevOutputFetcher, idx,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -1467,8 +1475,8 @@ func (b *BreachArbiter) sweepSpendableOutputsTxn(txWeight int64,
|
|||||||
|
|
||||||
// Finally, generate a witness for each output and attach it to the
|
// Finally, generate a witness for each output and attach it to the
|
||||||
// transaction.
|
// transaction.
|
||||||
for i, input := range inputs {
|
for i, inp := range inputs {
|
||||||
if err := addWitness(i, input); err != nil {
|
if err := addWitness(i, inp); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1648,7 +1656,7 @@ func (ret *retributionInfo) Encode(w io.Writer) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dencode deserializes a retribution from the passed byte stream.
|
// Decode deserializes a retribution from the passed byte stream.
|
||||||
func (ret *retributionInfo) Decode(r io.Reader) error {
|
func (ret *retributionInfo) Decode(r io.Reader) error {
|
||||||
var scratch [32]byte
|
var scratch [32]byte
|
||||||
|
|
||||||
|
@ -1386,8 +1386,11 @@ func getSpendTransactions(signer input.Signer, chanPoint *wire.OutPoint,
|
|||||||
case input.HtlcAcceptedRevoke:
|
case input.HtlcAcceptedRevoke:
|
||||||
fallthrough
|
fallthrough
|
||||||
case input.HtlcOfferedRevoke:
|
case input.HtlcOfferedRevoke:
|
||||||
|
cannedFetcher := txscript.NewCannedPrevOutputFetcher(
|
||||||
|
nil, 0,
|
||||||
|
)
|
||||||
inputScript, err := inp.CraftInputScript(
|
inputScript, err := inp.CraftInputScript(
|
||||||
signer, htlcSweep, hashCache, 0,
|
signer, htlcSweep, hashCache, cannedFetcher, 0,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -42,6 +42,7 @@ type Input interface {
|
|||||||
// also nested p2sh outputs.
|
// also nested p2sh outputs.
|
||||||
CraftInputScript(signer Signer, txn *wire.MsgTx,
|
CraftInputScript(signer Signer, txn *wire.MsgTx,
|
||||||
hashCache *txscript.TxSigHashes,
|
hashCache *txscript.TxSigHashes,
|
||||||
|
prevOutputFetcher txscript.PrevOutputFetcher,
|
||||||
txinIdx int) (*Script, error)
|
txinIdx int) (*Script, error)
|
||||||
|
|
||||||
// BlocksToMaturity returns the relative timelock, as a number of
|
// BlocksToMaturity returns the relative timelock, as a number of
|
||||||
@ -221,9 +222,13 @@ func NewCsvInputWithCltv(outpoint *wire.OutPoint, witnessType WitnessType,
|
|||||||
// txIndex within the passed transaction. The input scripts generated by this
|
// txIndex within the passed transaction. The input scripts generated by this
|
||||||
// method support spending p2wkh, p2wsh, and also nested p2sh outputs.
|
// method support spending p2wkh, p2wsh, and also nested p2sh outputs.
|
||||||
func (bi *BaseInput) CraftInputScript(signer Signer, txn *wire.MsgTx,
|
func (bi *BaseInput) CraftInputScript(signer Signer, txn *wire.MsgTx,
|
||||||
hashCache *txscript.TxSigHashes, txinIdx int) (*Script, error) {
|
hashCache *txscript.TxSigHashes,
|
||||||
|
prevOutputFetcher txscript.PrevOutputFetcher, txinIdx int) (*Script,
|
||||||
|
error) {
|
||||||
|
|
||||||
witnessFunc := bi.witnessType.WitnessGenerator(signer, bi.SignDesc())
|
signDesc := bi.SignDesc()
|
||||||
|
signDesc.PrevOutputFetcher = prevOutputFetcher
|
||||||
|
witnessFunc := bi.witnessType.WitnessGenerator(signer, signDesc)
|
||||||
|
|
||||||
return witnessFunc(txn, hashCache, txinIdx)
|
return witnessFunc(txn, hashCache, txinIdx)
|
||||||
}
|
}
|
||||||
@ -260,11 +265,14 @@ func MakeHtlcSucceedInput(outpoint *wire.OutPoint,
|
|||||||
// txIndex within the passed transaction. The input scripts generated by this
|
// txIndex within the passed transaction. The input scripts generated by this
|
||||||
// method support spending p2wkh, p2wsh, and also nested p2sh outputs.
|
// method support spending p2wkh, p2wsh, and also nested p2sh outputs.
|
||||||
func (h *HtlcSucceedInput) CraftInputScript(signer Signer, txn *wire.MsgTx,
|
func (h *HtlcSucceedInput) CraftInputScript(signer Signer, txn *wire.MsgTx,
|
||||||
hashCache *txscript.TxSigHashes, txinIdx int) (*Script, error) {
|
hashCache *txscript.TxSigHashes,
|
||||||
|
prevOutputFetcher txscript.PrevOutputFetcher, txinIdx int) (*Script,
|
||||||
|
error) {
|
||||||
|
|
||||||
desc := h.signDesc
|
desc := h.signDesc
|
||||||
desc.SigHashes = hashCache
|
desc.SigHashes = hashCache
|
||||||
desc.InputIndex = txinIdx
|
desc.InputIndex = txinIdx
|
||||||
|
desc.PrevOutputFetcher = prevOutputFetcher
|
||||||
|
|
||||||
witness, err := SenderHtlcSpendRedeem(
|
witness, err := SenderHtlcSpendRedeem(
|
||||||
signer, &desc, txn, h.preimage,
|
signer, &desc, txn, h.preimage,
|
||||||
@ -291,7 +299,9 @@ type HtlcSecondLevelAnchorInput struct {
|
|||||||
// createWitness creates a witness allowing the passed transaction to
|
// createWitness creates a witness allowing the passed transaction to
|
||||||
// spend the input.
|
// spend the input.
|
||||||
createWitness func(signer Signer, txn *wire.MsgTx,
|
createWitness func(signer Signer, txn *wire.MsgTx,
|
||||||
hashCache *txscript.TxSigHashes, txinIdx int) (wire.TxWitness, error)
|
hashCache *txscript.TxSigHashes,
|
||||||
|
prevOutputFetcher txscript.PrevOutputFetcher,
|
||||||
|
txinIdx int) (wire.TxWitness, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequiredTxOut returns the tx out needed to be present on the sweep tx for
|
// RequiredTxOut returns the tx out needed to be present on the sweep tx for
|
||||||
@ -313,9 +323,12 @@ func (i *HtlcSecondLevelAnchorInput) RequiredLockTime() (uint32, bool) {
|
|||||||
// method support spending p2wkh, p2wsh, and also nested p2sh outputs.
|
// method support spending p2wkh, p2wsh, and also nested p2sh outputs.
|
||||||
func (i *HtlcSecondLevelAnchorInput) CraftInputScript(signer Signer,
|
func (i *HtlcSecondLevelAnchorInput) CraftInputScript(signer Signer,
|
||||||
txn *wire.MsgTx, hashCache *txscript.TxSigHashes,
|
txn *wire.MsgTx, hashCache *txscript.TxSigHashes,
|
||||||
txinIdx int) (*Script, error) {
|
prevOutputFetcher txscript.PrevOutputFetcher, txinIdx int) (*Script,
|
||||||
|
error) {
|
||||||
|
|
||||||
witness, err := i.createWitness(signer, txn, hashCache, txinIdx)
|
witness, err := i.createWitness(
|
||||||
|
signer, txn, hashCache, prevOutputFetcher, txinIdx,
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -335,11 +348,13 @@ func MakeHtlcSecondLevelTimeoutAnchorInput(signedTx *wire.MsgTx,
|
|||||||
// 2nd timeout transaction.
|
// 2nd timeout transaction.
|
||||||
createWitness := func(signer Signer, txn *wire.MsgTx,
|
createWitness := func(signer Signer, txn *wire.MsgTx,
|
||||||
hashCache *txscript.TxSigHashes,
|
hashCache *txscript.TxSigHashes,
|
||||||
|
prevOutputFetcher txscript.PrevOutputFetcher,
|
||||||
txinIdx int) (wire.TxWitness, error) {
|
txinIdx int) (wire.TxWitness, error) {
|
||||||
|
|
||||||
desc := signDetails.SignDesc
|
desc := signDetails.SignDesc
|
||||||
desc.SigHashes = NewTxSigHashesV0Only(txn)
|
desc.SigHashes = txscript.NewTxSigHashes(txn, prevOutputFetcher)
|
||||||
desc.InputIndex = txinIdx
|
desc.InputIndex = txinIdx
|
||||||
|
desc.PrevOutputFetcher = prevOutputFetcher
|
||||||
|
|
||||||
return SenderHtlcSpendTimeout(
|
return SenderHtlcSpendTimeout(
|
||||||
signDetails.PeerSig, signDetails.SigHashType, signer,
|
signDetails.PeerSig, signDetails.SigHashType, signer,
|
||||||
@ -373,11 +388,13 @@ func MakeHtlcSecondLevelSuccessAnchorInput(signedTx *wire.MsgTx,
|
|||||||
// success transaction.
|
// success transaction.
|
||||||
createWitness := func(signer Signer, txn *wire.MsgTx,
|
createWitness := func(signer Signer, txn *wire.MsgTx,
|
||||||
hashCache *txscript.TxSigHashes,
|
hashCache *txscript.TxSigHashes,
|
||||||
|
prevOutputFetcher txscript.PrevOutputFetcher,
|
||||||
txinIdx int) (wire.TxWitness, error) {
|
txinIdx int) (wire.TxWitness, error) {
|
||||||
|
|
||||||
desc := signDetails.SignDesc
|
desc := signDetails.SignDesc
|
||||||
desc.SigHashes = hashCache
|
desc.SigHashes = hashCache
|
||||||
desc.InputIndex = txinIdx
|
desc.InputIndex = txinIdx
|
||||||
|
desc.PrevOutputFetcher = prevOutputFetcher
|
||||||
|
|
||||||
return ReceiverHtlcSpendRedeem(
|
return ReceiverHtlcSpendRedeem(
|
||||||
signDetails.PeerSig, signDetails.SigHashType,
|
signDetails.PeerSig, signDetails.SigHashType,
|
||||||
|
@ -74,6 +74,11 @@ type SignDescriptor struct {
|
|||||||
// generating the final sighash for signing.
|
// generating the final sighash for signing.
|
||||||
SigHashes *txscript.TxSigHashes
|
SigHashes *txscript.TxSigHashes
|
||||||
|
|
||||||
|
// PrevOutputFetcher is an interface that can return the output
|
||||||
|
// information on all UTXOs that are being spent in this transaction.
|
||||||
|
// This MUST be set when spending Taproot outputs.
|
||||||
|
PrevOutputFetcher txscript.PrevOutputFetcher
|
||||||
|
|
||||||
// InputIndex is the target input within the transaction that should be
|
// InputIndex is the target input within the transaction that should be
|
||||||
// signed.
|
// signed.
|
||||||
InputIndex int
|
InputIndex int
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package input
|
package input
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
)
|
)
|
||||||
@ -18,3 +20,25 @@ func NewTxSigHashesV0Only(tx *wire.MsgTx) *txscript.TxSigHashes {
|
|||||||
nilFetcher := txscript.NewCannedPrevOutputFetcher(nil, 0)
|
nilFetcher := txscript.NewCannedPrevOutputFetcher(nil, 0)
|
||||||
return txscript.NewTxSigHashes(tx, nilFetcher)
|
return txscript.NewTxSigHashes(tx, nilFetcher)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MultiPrevOutFetcher returns a txscript.MultiPrevOutFetcher for the given set
|
||||||
|
// of inputs.
|
||||||
|
func MultiPrevOutFetcher(inputs []Input) (*txscript.MultiPrevOutFetcher, error) {
|
||||||
|
fetcher := txscript.NewMultiPrevOutFetcher(nil)
|
||||||
|
for _, inp := range inputs {
|
||||||
|
op := inp.OutPoint()
|
||||||
|
desc := inp.SignDesc()
|
||||||
|
|
||||||
|
if op == nil {
|
||||||
|
return nil, fmt.Errorf("missing input outpoint")
|
||||||
|
}
|
||||||
|
|
||||||
|
if desc == nil || desc.Output == nil {
|
||||||
|
return nil, fmt.Errorf("missing input utxo information")
|
||||||
|
}
|
||||||
|
|
||||||
|
fetcher.AddPrevOut(*op, desc.Output)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetcher, nil
|
||||||
|
}
|
||||||
|
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/btcutil/psbt"
|
"github.com/btcsuite/btcd/btcutil/psbt"
|
||||||
"github.com/btcsuite/btcd/txscript"
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcwallet/waddrmgr"
|
"github.com/btcsuite/btcwallet/waddrmgr"
|
||||||
|
"github.com/btcsuite/btcwallet/wallet"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
"github.com/lightningnetwork/lnd/lnwallet/chainfee"
|
||||||
@ -107,7 +108,8 @@ func (b *BtcWallet) SignPsbt(packet *psbt.Packet) error {
|
|||||||
// there are inputs that we don't know how to sign, we won't return any
|
// there are inputs that we don't know how to sign, we won't return any
|
||||||
// error. So it's possible we're not the final signer.
|
// error. So it's possible we're not the final signer.
|
||||||
tx := packet.UnsignedTx
|
tx := packet.UnsignedTx
|
||||||
sigHashes := input.NewTxSigHashesV0Only(tx)
|
prevOutputFetcher := wallet.PsbtPrevOutputFetcher(packet)
|
||||||
|
sigHashes := txscript.NewTxSigHashes(tx, prevOutputFetcher)
|
||||||
for idx := range tx.TxIn {
|
for idx := range tx.TxIn {
|
||||||
in := packet.Inputs[idx]
|
in := packet.Inputs[idx]
|
||||||
|
|
||||||
|
@ -107,9 +107,15 @@ func (f *FullIntent) CompileFundingTx(extraInputs []*wire.TxIn,
|
|||||||
|
|
||||||
// Next, sign all inputs that are ours, collecting the signatures in
|
// Next, sign all inputs that are ours, collecting the signatures in
|
||||||
// order of the inputs.
|
// order of the inputs.
|
||||||
|
prevOutFetcher := NewSegWitV0DualFundingPrevOutputFetcher(
|
||||||
|
f.coinSource, extraInputs,
|
||||||
|
)
|
||||||
signDesc := input.SignDescriptor{
|
signDesc := input.SignDescriptor{
|
||||||
HashType: txscript.SigHashAll,
|
HashType: txscript.SigHashAll,
|
||||||
SigHashes: input.NewTxSigHashesV0Only(fundingTx),
|
SigHashes: txscript.NewTxSigHashes(
|
||||||
|
fundingTx, prevOutFetcher,
|
||||||
|
),
|
||||||
|
PrevOutputFetcher: prevOutFetcher,
|
||||||
}
|
}
|
||||||
for i, txIn := range fundingTx.TxIn {
|
for i, txIn := range fundingTx.TxIn {
|
||||||
// We can only sign this input if it's ours, so we'll ask the
|
// We can only sign this input if it's ours, so we'll ask the
|
||||||
@ -374,3 +380,55 @@ func (w *WalletAssembler) FundingTxAvailable() {}
|
|||||||
// A compile-time assertion to ensure the WalletAssembler meets the
|
// A compile-time assertion to ensure the WalletAssembler meets the
|
||||||
// FundingTxAssembler interface.
|
// FundingTxAssembler interface.
|
||||||
var _ FundingTxAssembler = (*WalletAssembler)(nil)
|
var _ FundingTxAssembler = (*WalletAssembler)(nil)
|
||||||
|
|
||||||
|
// SegWitV0DualFundingPrevOutputFetcher is a txscript.PrevOutputFetcher that
|
||||||
|
// knows about local and remote funding inputs.
|
||||||
|
//
|
||||||
|
// TODO(guggero): Support dual funding with p2tr inputs, currently only segwit
|
||||||
|
// v0 inputs are supported.
|
||||||
|
type SegWitV0DualFundingPrevOutputFetcher struct {
|
||||||
|
local CoinSource
|
||||||
|
remote *txscript.MultiPrevOutFetcher
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ txscript.PrevOutputFetcher = (*SegWitV0DualFundingPrevOutputFetcher)(nil)
|
||||||
|
|
||||||
|
// NewSegWitV0DualFundingPrevOutputFetcher creates a new
|
||||||
|
// txscript.PrevOutputFetcher from the given local and remote inputs.
|
||||||
|
//
|
||||||
|
// NOTE: Since the actual pkScript and amounts aren't passed in, this will just
|
||||||
|
// make sure that nothing will panic when creating a SegWit v0 sighash. But this
|
||||||
|
// code will NOT WORK for transactions that spend any Taproot inputs!
|
||||||
|
func NewSegWitV0DualFundingPrevOutputFetcher(localSource CoinSource,
|
||||||
|
remoteInputs []*wire.TxIn) txscript.PrevOutputFetcher {
|
||||||
|
|
||||||
|
remote := txscript.NewMultiPrevOutFetcher(nil)
|
||||||
|
for _, inp := range remoteInputs {
|
||||||
|
// We add an empty output to prevent the sighash calculation
|
||||||
|
// from panicking. But this will always detect the inputs as
|
||||||
|
// SegWig v0!
|
||||||
|
remote.AddPrevOut(inp.PreviousOutPoint, &wire.TxOut{})
|
||||||
|
}
|
||||||
|
return &SegWitV0DualFundingPrevOutputFetcher{
|
||||||
|
local: localSource,
|
||||||
|
remote: remote,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchPrevOutput attempts to fetch the previous output referenced by the
|
||||||
|
// passed outpoint.
|
||||||
|
//
|
||||||
|
// NOTE: This is a part of the txscript.PrevOutputFetcher interface.
|
||||||
|
func (d *SegWitV0DualFundingPrevOutputFetcher) FetchPrevOutput(
|
||||||
|
op wire.OutPoint) *wire.TxOut {
|
||||||
|
|
||||||
|
// Try the local source first. This will return nil if our internal
|
||||||
|
// wallet doesn't know the outpoint.
|
||||||
|
coin, err := d.local.CoinFromOutPoint(op)
|
||||||
|
if err == nil && coin != nil {
|
||||||
|
return &coin.TxOut
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall back to the remote
|
||||||
|
return d.remote.FetchPrevOutput(op)
|
||||||
|
}
|
||||||
|
@ -133,9 +133,11 @@ func (r *RPCKeyRing) SendOutputs(outputs []*wire.TxOut,
|
|||||||
|
|
||||||
// We know at this point that we only have inputs from our own wallet.
|
// We know at this point that we only have inputs from our own wallet.
|
||||||
// So we can just compute the input script using the remote signer.
|
// So we can just compute the input script using the remote signer.
|
||||||
|
outputFetcher := lnwallet.NewWalletPrevOutputFetcher(r.WalletController)
|
||||||
signDesc := input.SignDescriptor{
|
signDesc := input.SignDescriptor{
|
||||||
HashType: txscript.SigHashAll,
|
HashType: txscript.SigHashAll,
|
||||||
SigHashes: input.NewTxSigHashesV0Only(tx),
|
SigHashes: txscript.NewTxSigHashes(tx, outputFetcher),
|
||||||
|
PrevOutputFetcher: outputFetcher,
|
||||||
}
|
}
|
||||||
for i, txIn := range tx.TxIn {
|
for i, txIn := range tx.TxIn {
|
||||||
// We can only sign this input if it's ours, so we'll ask the
|
// We can only sign this input if it's ours, so we'll ask the
|
||||||
@ -579,6 +581,32 @@ func (r *RPCKeyRing) remoteSign(tx *wire.MsgTx, signDesc *input.SignDescriptor,
|
|||||||
return nil, fmt.Errorf("error converting TX into PSBT: %v", err)
|
return nil, fmt.Errorf("error converting TX into PSBT: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need to add witness information for all inputs! Otherwise, we'll
|
||||||
|
// have a problem when attempting to sign a taproot input!
|
||||||
|
for idx := range packet.Inputs {
|
||||||
|
// Skip the input we're signing for, that will get a special
|
||||||
|
// treatment later on.
|
||||||
|
if idx == signDesc.InputIndex {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
txIn := tx.TxIn[idx]
|
||||||
|
info, err := r.WalletController.FetchInputInfo(
|
||||||
|
&txIn.PreviousOutPoint,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
log.Warnf("No UTXO info found for index %d "+
|
||||||
|
"(prev_outpoint=%v), won't be able to sign "+
|
||||||
|
"for taproot output!", idx,
|
||||||
|
txIn.PreviousOutPoint)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
packet.Inputs[idx].WitnessUtxo = &wire.TxOut{
|
||||||
|
Value: int64(info.Value),
|
||||||
|
PkScript: info.PkScript,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Catch incorrect signing input index, just in case.
|
// Catch incorrect signing input index, just in case.
|
||||||
if signDesc.InputIndex < 0 || signDesc.InputIndex >= len(packet.Inputs) {
|
if signDesc.InputIndex < 0 || signDesc.InputIndex >= len(packet.Inputs) {
|
||||||
return nil, fmt.Errorf("invalid input index in sign descriptor")
|
return nil, fmt.Errorf("invalid input index in sign descriptor")
|
||||||
|
@ -2266,3 +2266,36 @@ func validateUpfrontShutdown(shutdown lnwire.DeliveryAddress,
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WalletPrevOutputFetcher is a txscript.PrevOutputFetcher that can fetch
|
||||||
|
// outputs from a given wallet controller.
|
||||||
|
type WalletPrevOutputFetcher struct {
|
||||||
|
wc WalletController
|
||||||
|
}
|
||||||
|
|
||||||
|
// A compile time assertion that WalletPrevOutputFetcher implements the
|
||||||
|
// txscript.PrevOutputFetcher interface.
|
||||||
|
var _ txscript.PrevOutputFetcher = (*WalletPrevOutputFetcher)(nil)
|
||||||
|
|
||||||
|
// NewWalletPrevOutputFetcher creates a new WalletPrevOutputFetcher that fetches
|
||||||
|
// previous outputs from the given wallet controller.
|
||||||
|
func NewWalletPrevOutputFetcher(wc WalletController) *WalletPrevOutputFetcher {
|
||||||
|
return &WalletPrevOutputFetcher{
|
||||||
|
wc: wc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FetchPrevOutput attempts to fetch the previous output referenced by the
|
||||||
|
// passed outpoint. A nil value will be returned if the passed outpoint doesn't
|
||||||
|
// exist.
|
||||||
|
func (w *WalletPrevOutputFetcher) FetchPrevOutput(op wire.OutPoint) *wire.TxOut {
|
||||||
|
utxo, err := w.wc.FetchInputInfo(&op)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &wire.TxOut{
|
||||||
|
Value: int64(utxo.Value),
|
||||||
|
PkScript: utxo.PkScript,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1623,7 +1623,9 @@ func (i *testInput) RequiredTxOut() *wire.TxOut {
|
|||||||
// encode the spending outpoint and the tx input index as part of the returned
|
// encode the spending outpoint and the tx input index as part of the returned
|
||||||
// witness.
|
// witness.
|
||||||
func (i *testInput) CraftInputScript(_ input.Signer, txn *wire.MsgTx,
|
func (i *testInput) CraftInputScript(_ input.Signer, txn *wire.MsgTx,
|
||||||
hashCache *txscript.TxSigHashes, txinIdx int) (*input.Script, error) {
|
hashCache *txscript.TxSigHashes,
|
||||||
|
prevOutputFetcher txscript.PrevOutputFetcher,
|
||||||
|
txinIdx int) (*input.Script, error) {
|
||||||
|
|
||||||
// We'll encode the outpoint in the witness, so we can assert that the
|
// We'll encode the outpoint in the witness, so we can assert that the
|
||||||
// expected input was signed at the correct index.
|
// expected input was signed at the correct index.
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
"github.com/btcsuite/btcd/blockchain"
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
"github.com/lightningnetwork/lnd/lnwallet"
|
"github.com/lightningnetwork/lnd/lnwallet"
|
||||||
@ -263,13 +264,18 @@ func createSweepTx(inputs []input.Input, outputs []*wire.TxOut,
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
hashCache := input.NewTxSigHashesV0Only(sweepTx)
|
prevInputFetcher, err := input.MultiPrevOutFetcher(inputs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating prev input fetcher "+
|
||||||
|
"for hash cache: %v", err)
|
||||||
|
}
|
||||||
|
hashCache := txscript.NewTxSigHashes(sweepTx, prevInputFetcher)
|
||||||
|
|
||||||
// With all the inputs in place, use each output's unique input script
|
// 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.
|
||||||
addInputScript := func(idx int, tso input.Input) error {
|
addInputScript := func(idx int, tso input.Input) error {
|
||||||
inputScript, err := tso.CraftInputScript(
|
inputScript, err := tso.CraftInputScript(
|
||||||
signer, sweepTx, hashCache, idx,
|
signer, sweepTx, hashCache, prevInputFetcher, idx,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -2,6 +2,7 @@ package lookout
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/btcsuite/btcd/blockchain"
|
"github.com/btcsuite/btcd/blockchain"
|
||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
@ -177,11 +178,11 @@ func (p *JusticeDescriptor) assembleJusticeTxn(txWeight int64,
|
|||||||
// First, construct add the breached inputs to our justice transaction
|
// First, construct add the breached inputs to our justice transaction
|
||||||
// and compute the total amount that will be swept.
|
// and compute the total amount that will be swept.
|
||||||
var totalAmt btcutil.Amount
|
var totalAmt btcutil.Amount
|
||||||
for _, input := range inputs {
|
for _, inp := range inputs {
|
||||||
totalAmt += btcutil.Amount(input.txOut.Value)
|
totalAmt += btcutil.Amount(inp.txOut.Value)
|
||||||
justiceTxn.AddTxIn(&wire.TxIn{
|
justiceTxn.AddTxIn(&wire.TxIn{
|
||||||
PreviousOutPoint: input.outPoint,
|
PreviousOutPoint: inp.outPoint,
|
||||||
Sequence: input.sequence,
|
Sequence: inp.sequence,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,20 +218,22 @@ func (p *JusticeDescriptor) assembleJusticeTxn(txWeight int64,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Attach each of the provided witnesses to the transaction.
|
// Attach each of the provided witnesses to the transaction.
|
||||||
for _, input := range inputs {
|
prevOutFetcher, err := prevOutFetcher(inputs)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error creating previous output "+
|
||||||
|
"fetcher: %v", err)
|
||||||
|
}
|
||||||
|
for _, inp := range inputs {
|
||||||
// Lookup the input's new post-sort position.
|
// Lookup the input's new post-sort position.
|
||||||
i := inputIndex[input.outPoint]
|
i := inputIndex[inp.outPoint]
|
||||||
justiceTxn.TxIn[i].Witness = input.witness
|
justiceTxn.TxIn[i].Witness = inp.witness
|
||||||
|
|
||||||
// Validate the reconstructed witnesses to ensure they are valid
|
// Validate the reconstructed witnesses to ensure they are valid
|
||||||
// for the breached inputs.
|
// for the breached inputs.
|
||||||
vm, err := txscript.NewEngine(
|
vm, err := txscript.NewEngine(
|
||||||
input.txOut.PkScript, justiceTxn, i,
|
inp.txOut.PkScript, justiceTxn, i,
|
||||||
txscript.StandardVerifyFlags,
|
txscript.StandardVerifyFlags,
|
||||||
nil, nil, input.txOut.Value,
|
nil, nil, inp.txOut.Value, prevOutFetcher,
|
||||||
txscript.NewCannedPrevOutputFetcher(
|
|
||||||
input.txOut.PkScript, input.txOut.Value,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -345,3 +348,20 @@ func buildWitness(witnessStack [][]byte, witnessScript []byte) [][]byte {
|
|||||||
|
|
||||||
return witness
|
return witness
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// prevOutFetcher returns a txscript.MultiPrevOutFetcher for the given set
|
||||||
|
// of inputs.
|
||||||
|
func prevOutFetcher(inputs []*breachedInput) (*txscript.MultiPrevOutFetcher,
|
||||||
|
error) {
|
||||||
|
|
||||||
|
fetcher := txscript.NewMultiPrevOutFetcher(nil)
|
||||||
|
for _, inp := range inputs {
|
||||||
|
if inp.txOut == nil {
|
||||||
|
return nil, fmt.Errorf("missing input utxo information")
|
||||||
|
}
|
||||||
|
|
||||||
|
fetcher.AddPrevOut(inp.outPoint, inp.txOut)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetcher, nil
|
||||||
|
}
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/btcsuite/btcd/btcec/v2"
|
"github.com/btcsuite/btcd/btcec/v2"
|
||||||
"github.com/btcsuite/btcd/btcutil"
|
"github.com/btcsuite/btcd/btcutil"
|
||||||
"github.com/btcsuite/btcd/btcutil/txsort"
|
"github.com/btcsuite/btcd/btcutil/txsort"
|
||||||
|
"github.com/btcsuite/btcd/txscript"
|
||||||
"github.com/btcsuite/btcd/wire"
|
"github.com/btcsuite/btcd/wire"
|
||||||
"github.com/lightningnetwork/lnd/channeldb"
|
"github.com/lightningnetwork/lnd/channeldb"
|
||||||
"github.com/lightningnetwork/lnd/input"
|
"github.com/lightningnetwork/lnd/input"
|
||||||
@ -262,10 +263,14 @@ func (t *backupTask) craftSessionPayload(
|
|||||||
// information. This will either be contain both the to-local and
|
// information. This will either be contain both the to-local and
|
||||||
// to-remote outputs, or only be the to-local output.
|
// to-remote outputs, or only be the to-local output.
|
||||||
inputs := t.inputs()
|
inputs := t.inputs()
|
||||||
for prevOutPoint, input := range inputs {
|
prevOutputFetcher := txscript.NewMultiPrevOutFetcher(nil)
|
||||||
|
for prevOutPoint, inp := range inputs {
|
||||||
|
prevOutputFetcher.AddPrevOut(
|
||||||
|
prevOutPoint, inp.SignDesc().Output,
|
||||||
|
)
|
||||||
justiceTxn.AddTxIn(&wire.TxIn{
|
justiceTxn.AddTxIn(&wire.TxIn{
|
||||||
PreviousOutPoint: prevOutPoint,
|
PreviousOutPoint: prevOutPoint,
|
||||||
Sequence: input.BlocksToMaturity(),
|
Sequence: inp.BlocksToMaturity(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,7 +289,7 @@ func (t *backupTask) craftSessionPayload(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Construct a sighash cache to improve signing performance.
|
// Construct a sighash cache to improve signing performance.
|
||||||
hashCache := input.NewTxSigHashesV0Only(justiceTxn)
|
hashCache := txscript.NewTxSigHashes(justiceTxn, prevOutputFetcher)
|
||||||
|
|
||||||
// Since the transaction inputs could have been reordered as a result of
|
// Since the transaction inputs could have been reordered as a result of
|
||||||
// the BIP69 sort, create an index mapping each prevout to it's new
|
// the BIP69 sort, create an index mapping each prevout to it's new
|
||||||
@ -303,7 +308,7 @@ func (t *backupTask) craftSessionPayload(
|
|||||||
|
|
||||||
// Construct the full witness required to spend this input.
|
// Construct the full witness required to spend this input.
|
||||||
inputScript, err := inp.CraftInputScript(
|
inputScript, err := inp.CraftInputScript(
|
||||||
signer, justiceTxn, hashCache, i,
|
signer, justiceTxn, hashCache, prevOutputFetcher, i,
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return hint, nil, err
|
return hint, nil, err
|
||||||
|
Loading…
x
Reference in New Issue
Block a user