multi: use prev output fetcher where possible
This commit is contained in:
13 files changed
+255
-44
No files matched your search
@@ -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]
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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")
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user