diff --git a/lnrpc/signrpc/signer_server.go b/lnrpc/signrpc/signer_server.go index 1474c0ec9..b191176b3 100644 --- a/lnrpc/signrpc/signer_server.go +++ b/lnrpc/signrpc/signer_server.go @@ -376,6 +376,24 @@ func (s *Server) SignOutputRaw(_ context.Context, in *SignReq) (*SignResp, InputIndex: int(signDesc.InputIndex), PrevOutputFetcher: prevOutputFetcher, }) + + // Are we trying to sign for a Taproot output? Then we need all + // previous outputs being declared, otherwise we'd run into a + // panic later on. + if txscript.IsPayToTaproot(signDesc.Output.PkScript) { + for idx, txIn := range txToSign.TxIn { + utxo := prevOutputFetcher.FetchPrevOutput( + txIn.PreviousOutPoint, + ) + if utxo == nil { + return nil, fmt.Errorf("error signing "+ + "taproot output, transaction "+ + "input %d is missing its "+ + "previous outpoint information", + idx) + } + } + } } // Now that we've mapped all the proper sign descriptors, we can diff --git a/lntest/itest/lnd_taproot_test.go b/lntest/itest/lnd_taproot_test.go index cb0510a49..20d5d37dc 100644 --- a/lntest/itest/lnd_taproot_test.go +++ b/lntest/itest/lnd_taproot_test.go @@ -232,6 +232,29 @@ func testTaprootScriptSpend(ctxt context.Context, t *harnessTest, PkScript: p2trPkScript, Value: 800_000, }} + + // Before we actually sign, we want to make sure that we get an error + // when we try to sign for a Taproot output without specifying all UTXO + // information. + _, err = net.Alice.SignerClient.SignOutputRaw( + ctxt, &signrpc.SignReq{ + RawTxBytes: buf.Bytes(), + SignDescs: []*signrpc.SignDescriptor{{ + Output: utxoInfo[0], + InputIndex: 0, + KeyDesc: keyDesc, + Sighash: uint32(txscript.SigHashDefault), + WitnessScript: leaf2.Script, + }}, + }, + ) + require.Error(t.t, err) + require.Contains( + t.t, err.Error(), "error signing taproot output, transaction "+ + "input 0 is missing its previous outpoint information", + ) + + // Do the actual signing now. signResp, err := net.Alice.SignerClient.SignOutputRaw( ctxt, &signrpc.SignReq{ RawTxBytes: buf.Bytes(),