multi: use prev output fetcher where possible

This commit is contained in:
Oliver Gugger committed 2022-03-24 18:02:37 +01:00
1 parent 72c9582b85
commit f130eddb92
13 files changed
+255 -44

No files matched your search

+3 -1
View File
@@ -9,6 +9,7 @@ import (
"github.com/btcsuite/btcd/btcutil/psbt"
"github.com/btcsuite/btcd/txscript"
"github.com/btcsuite/btcwallet/waddrmgr"
"github.com/btcsuite/btcwallet/wallet"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnwallet"
"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
// error. So it's possible we're not the final signer.
tx := packet.UnsignedTx
sigHashes := input.NewTxSigHashesV0Only(tx)
prevOutputFetcher := wallet.PsbtPrevOutputFetcher(packet)
sigHashes := txscript.NewTxSigHashes(tx, prevOutputFetcher)
for idx := range tx.TxIn {
in := packet.Inputs[idx]
+60 -2
View File
@@ -107,9 +107,15 @@ func (f *FullIntent) CompileFundingTx(extraInputs []*wire.TxIn,
// Next, sign all inputs that are ours, collecting the signatures in
// order of the inputs.
prevOutFetcher := NewSegWitV0DualFundingPrevOutputFetcher(
f.coinSource, extraInputs,
)
signDesc := input.SignDescriptor{
HashType: txscript.SigHashAll,
SigHashes: input.NewTxSigHashesV0Only(fundingTx),
HashType: txscript.SigHashAll,
SigHashes: txscript.NewTxSigHashes(
fundingTx, prevOutFetcher,
),
PrevOutputFetcher: prevOutFetcher,
}
for i, txIn := range fundingTx.TxIn {
// 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
// FundingTxAssembler interface.
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)
}
+30 -2
View File
@@ -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.
// So we can just compute the input script using the remote signer.
outputFetcher := lnwallet.NewWalletPrevOutputFetcher(r.WalletController)
signDesc := input.SignDescriptor{
HashType: txscript.SigHashAll,
SigHashes: input.NewTxSigHashesV0Only(tx),
HashType: txscript.SigHashAll,
SigHashes: txscript.NewTxSigHashes(tx, outputFetcher),
PrevOutputFetcher: outputFetcher,
}
for i, txIn := range tx.TxIn {
// 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)
}
// 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.
if signDesc.InputIndex < 0 || signDesc.InputIndex >= len(packet.Inputs) {
return nil, fmt.Errorf("invalid input index in sign descriptor")
+33
View File
@@ -2266,3 +2266,36 @@ func validateUpfrontShutdown(shutdown lnwire.DeliveryAddress,
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,
}
}