diff --git a/lntest/itest/lnd_remote_signer_test.go b/lntest/itest/lnd_remote_signer_test.go index 80c855fa9..7a30e5c74 100644 --- a/lntest/itest/lnd_remote_signer_test.go +++ b/lntest/itest/lnd_remote_signer_test.go @@ -127,6 +127,9 @@ func testRemoteSigner(net *lntest.NetworkHarness, t *harnessTest) { defer cancel() testTaprootSendCoinsKeySpendBip86(ctxt, tt, wo, net) + testTaprootComputeInputScriptKeySpendBip86( + ctxt, tt, wo, net, + ) testTaprootSignOutputRawScriptSpend(ctxt, tt, wo, net) testTaprootSignOutputRawKeySpendBip86(ctxt, tt, wo, net) testTaprootSignOutputRawKeySpendRootHash( diff --git a/lntest/itest/lnd_taproot_test.go b/lntest/itest/lnd_taproot_test.go index 9999a553c..1b9e6b6a9 100644 --- a/lntest/itest/lnd_taproot_test.go +++ b/lntest/itest/lnd_taproot_test.go @@ -47,6 +47,7 @@ func testTaproot(net *lntest.NetworkHarness, t *harnessTest) { defer cancel() testTaprootSendCoinsKeySpendBip86(ctxt, t, net.Alice, net) + testTaprootComputeInputScriptKeySpendBip86(ctxt, t, net.Alice, net) testTaprootSignOutputRawScriptSpend(ctxt, t, net.Alice, net) testTaprootSignOutputRawKeySpendBip86(ctxt, t, net.Alice, net) testTaprootSignOutputRawKeySpendRootHash(ctxt, t, net.Alice, net) @@ -117,6 +118,108 @@ func testTaprootSendCoinsKeySpendBip86(ctxt context.Context, confirmAddress(ctxt, t, net, alice, p2trResp.Address) } +// testTaprootComputeInputScriptKeySpendBip86 tests sending to and spending +// from p2tr key spend only (BIP-0086) addresses through the ComputeInputScript +// RPC. +func testTaprootComputeInputScriptKeySpendBip86(ctxt context.Context, + t *harnessTest, alice *lntest.HarnessNode, net *lntest.NetworkHarness) { + + // We'll start the test by sending Alice some coins, which she'll use to + // send to herself on a p2tr output. + net.SendCoins(t.t, btcutil.SatoshiPerBitcoin, alice) + + // Let's create a p2tr address now. + p2trAddr, p2trPkScript := newAddrWithScript( + ctxt, t.t, alice, lnrpc.AddressType_TAPROOT_PUBKEY, + ) + + // Send the coins from Alice's wallet to her own, but to the new p2tr + // address. + _, err := alice.SendCoins(ctxt, &lnrpc.SendCoinsRequest{ + Addr: p2trAddr.String(), + Amount: testAmount, + }) + require.NoError(t.t, err) + + txid, err := waitForTxInMempool(net.Miner.Client, defaultTimeout) + require.NoError(t.t, err) + + // Wait until bob has seen the tx and considers it as owned. + p2trOutputIndex := getOutputIndex(t, net.Miner, txid, p2trAddr.String()) + op := &lnrpc.OutPoint{ + TxidBytes: txid[:], + OutputIndex: uint32(p2trOutputIndex), + } + assertWalletUnspent(t, alice, op) + + p2trOutpoint := wire.OutPoint{ + Hash: *txid, + Index: uint32(p2trOutputIndex), + } + + // Mine a block to clean up the mempool. + mineBlocks(t, net, 1, 1) + + // We'll send the coins back to a p2wkh address. + p2wkhAddr, p2wkhPkScript := newAddrWithScript( + ctxt, t.t, alice, lnrpc.AddressType_WITNESS_PUBKEY_HASH, + ) + + // Create fee estimation for a p2tr input and p2wkh output. + feeRate := chainfee.SatPerKWeight(12500) + estimator := input.TxWeightEstimator{} + estimator.AddTaprootKeySpendInput(txscript.SigHashDefault) + estimator.AddP2WKHOutput() + estimatedWeight := int64(estimator.Weight()) + requiredFee := feeRate.FeeForWeight(estimatedWeight) + + tx := wire.NewMsgTx(2) + tx.TxIn = []*wire.TxIn{{ + PreviousOutPoint: p2trOutpoint, + }} + value := int64(testAmount - requiredFee) + tx.TxOut = []*wire.TxOut{{ + PkScript: p2wkhPkScript, + Value: value, + }} + + var buf bytes.Buffer + require.NoError(t.t, tx.Serialize(&buf)) + + utxoInfo := []*signrpc.TxOut{{ + PkScript: p2trPkScript, + Value: testAmount, + }} + signResp, err := alice.SignerClient.ComputeInputScript( + ctxt, &signrpc.SignReq{ + RawTxBytes: buf.Bytes(), + SignDescs: []*signrpc.SignDescriptor{{ + Output: utxoInfo[0], + InputIndex: 0, + Sighash: uint32(txscript.SigHashDefault), + }}, + PrevOutputs: utxoInfo, + }, + ) + require.NoError(t.t, err) + + tx.TxIn[0].Witness = signResp.InputScripts[0].Witness + + // Serialize, weigh and publish the TX now, then make sure the + // coins are sent and confirmed to the final sweep destination address. + publishTxAndConfirmSweep( + ctxt, t, net, alice, tx, estimatedWeight, + &chainrpc.SpendRequest{ + Outpoint: &chainrpc.Outpoint{ + Hash: p2trOutpoint.Hash[:], + Index: p2trOutpoint.Index, + }, + Script: p2trPkScript, + }, + p2wkhAddr.String(), + ) +} + // testTaprootSignOutputRawScriptSpend tests sending to and spending from p2tr // script addresses using the script path with the SignOutputRaw RPC. func testTaprootSignOutputRawScriptSpend(ctxt context.Context, t *harnessTest,