@@ -90,6 +90,8 @@ func testTaprootImportScripts(ht *lntest.HarnessTest) {
9090 testTaprootImportTapscriptPartialReveal (ht , alice )
9191 testTaprootImportTapscriptRootHashOnly (ht , alice )
9292 testTaprootImportTapscriptFullKey (ht , alice )
93+
94+ testTaprootImportTapscriptFullKeyFundPsbt (ht , alice )
9395}
9496
9597// testTaprootSendCoinsKeySpendBip86 tests sending to and spending from
@@ -1359,6 +1361,134 @@ func testTaprootImportTapscriptFullKey(ht *lntest.HarnessTest,
13591361 )
13601362}
13611363
1364+ // testTaprootImportTapscriptFullKeyFundPsbt tests importing p2tr script
1365+ // addresses for which we only know the full Taproot key. We also test that we
1366+ // can use such an imported script to fund a PSBT.
1367+ func testTaprootImportTapscriptFullKeyFundPsbt (ht * lntest.HarnessTest ,
1368+ alice * node.HarnessNode ) {
1369+
1370+ // For the next step, we need a public key. Let's use a special family
1371+ // for this.
1372+ _ , internalKey , derivationPath := deriveInternalKey (ht , alice )
1373+
1374+ // Let's create a taproot script output now. This is a hash lock with a
1375+ // simple preimage of "foobar".
1376+ leaf1 := testScriptHashLock (ht .T , []byte ("foobar" ))
1377+
1378+ tapscript := input .TapscriptFullTree (internalKey , leaf1 )
1379+ rootHash := leaf1 .TapHash ()
1380+ taprootKey , err := tapscript .TaprootKey ()
1381+ require .NoError (ht , err )
1382+
1383+ // Import the scripts and make sure we get the same address back as we
1384+ // calculated ourselves.
1385+ req := & walletrpc.ImportTapscriptRequest {
1386+ InternalPublicKey : schnorr .SerializePubKey (taprootKey ),
1387+ Script : & walletrpc.ImportTapscriptRequest_FullKeyOnly {
1388+ FullKeyOnly : true ,
1389+ },
1390+ }
1391+ importResp := alice .RPC .ImportTapscript (req )
1392+
1393+ calculatedAddr , err := btcutil .NewAddressTaproot (
1394+ schnorr .SerializePubKey (taprootKey ), harnessNetParams ,
1395+ )
1396+ require .NoError (ht , err )
1397+ require .Equal (ht , calculatedAddr .String (), importResp .P2TrAddress )
1398+
1399+ // Send some coins to the generated tapscript address.
1400+ p2trOutpoint , p2trPkScript := sendToTaprootOutput (ht , alice , taprootKey )
1401+
1402+ p2trOutputRPC := & lnrpc.OutPoint {
1403+ TxidBytes : p2trOutpoint .Hash [:],
1404+ OutputIndex : p2trOutpoint .Index ,
1405+ }
1406+ ht .AssertUTXOInWallet (alice , p2trOutputRPC , "imported" )
1407+ ht .AssertWalletAccountBalance (alice , "imported" , testAmount , 0 )
1408+
1409+ // We now fund a PSBT that spends the imported tapscript address.
1410+ utxo := & wire.TxOut {
1411+ Value : testAmount ,
1412+ PkScript : p2trPkScript ,
1413+ }
1414+ _ , sweepPkScript := newAddrWithScript (
1415+ ht , alice , lnrpc .AddressType_WITNESS_PUBKEY_HASH ,
1416+ )
1417+
1418+ output := & wire.TxOut {
1419+ PkScript : sweepPkScript ,
1420+ Value : 1 ,
1421+ }
1422+ packet , err := psbt .New (
1423+ []* wire.OutPoint {& p2trOutpoint }, []* wire.TxOut {output }, 2 , 0 ,
1424+ []uint32 {0 },
1425+ )
1426+ require .NoError (ht , err )
1427+
1428+ // We have everything we need to know to sign the PSBT.
1429+ in := & packet .Inputs [0 ]
1430+ in .Bip32Derivation = []* psbt.Bip32Derivation {{
1431+ PubKey : internalKey .SerializeCompressed (),
1432+ Bip32Path : derivationPath ,
1433+ }}
1434+ in .TaprootBip32Derivation = []* psbt.TaprootBip32Derivation {{
1435+ XOnlyPubKey : schnorr .SerializePubKey (internalKey ),
1436+ Bip32Path : derivationPath ,
1437+ }}
1438+ in .SighashType = txscript .SigHashDefault
1439+ in .TaprootMerkleRoot = rootHash [:]
1440+ in .WitnessUtxo = utxo
1441+
1442+ var buf bytes.Buffer
1443+ require .NoError (ht , packet .Serialize (& buf ))
1444+
1445+ change := & walletrpc.PsbtCoinSelect_ExistingOutputIndex {
1446+ ExistingOutputIndex : 0 ,
1447+ }
1448+ fundResp := alice .RPC .FundPsbt (& walletrpc.FundPsbtRequest {
1449+ Template : & walletrpc.FundPsbtRequest_CoinSelect {
1450+ CoinSelect : & walletrpc.PsbtCoinSelect {
1451+ Psbt : buf .Bytes (),
1452+ ChangeOutput : change ,
1453+ },
1454+ },
1455+ Fees : & walletrpc.FundPsbtRequest_SatPerVbyte {
1456+ SatPerVbyte : 1 ,
1457+ },
1458+ })
1459+
1460+ // Sign the manually funded PSBT now.
1461+ signResp := alice .RPC .SignPsbt (& walletrpc.SignPsbtRequest {
1462+ FundedPsbt : fundResp .FundedPsbt ,
1463+ })
1464+
1465+ signedPacket , err := psbt .NewFromRawBytes (
1466+ bytes .NewReader (signResp .SignedPsbt ), false ,
1467+ )
1468+ require .NoError (ht , err )
1469+
1470+ // We should be able to finalize the PSBT and extract the sweep TX now.
1471+ err = psbt .MaybeFinalizeAll (signedPacket )
1472+ require .NoError (ht , err )
1473+
1474+ sweepTx , err := psbt .Extract (signedPacket )
1475+ require .NoError (ht , err )
1476+
1477+ buf .Reset ()
1478+ err = sweepTx .Serialize (& buf )
1479+ require .NoError (ht , err )
1480+
1481+ // Publish the sweep transaction and then mine it as well.
1482+ alice .RPC .PublishTransaction (& walletrpc.Transaction {
1483+ TxHex : buf .Bytes (),
1484+ })
1485+
1486+ // Mine one block which should contain the sweep transaction.
1487+ block := ht .MineBlocksAndAssertNumTxes (1 , 1 )[0 ]
1488+ sweepTxHash := sweepTx .TxHash ()
1489+ ht .AssertTxInBlock (block , sweepTxHash )
1490+ }
1491+
13621492// clearWalletImportedTapscriptBalance manually assembles and then attempts to
13631493// sign a TX to sweep funds from an imported tapscript address.
13641494func clearWalletImportedTapscriptBalance (ht * lntest.HarnessTest ,
0 commit comments