multi: fix and test v1 output spend ntfns

Because Taproot key spend only spends don't allow us to re-construct the
spent pkScript from the witness alone, we cannot support registering
spend notifications for v1 pkScripts only. We instead require the
outpoint to be specified. This commit makes it possible to only match by
outpoint and also adds an itest for it.
This commit is contained in:
Oliver Gugger committed 2022-03-24 18:02:40 +01:00
1 parent ef98f2df8a
commit 3b5585c12b
6 files changed
+137 -7

No files matched your search

+5
View File
@@ -23,6 +23,7 @@ import (
"github.com/jackc/pgx/v4/pgxpool"
"github.com/lightningnetwork/lnd/chanbackup"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/chainrpc"
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
@@ -361,6 +362,7 @@ type HarnessNode struct {
Watchtower watchtowerrpc.WatchtowerClient
WatchtowerClient wtclientrpc.WatchtowerClientClient
StateClient lnrpc.StateClient
ChainClient chainrpc.ChainNotifierClient
}
// RPCClients wraps a list of RPC clients into a single struct for easier
@@ -378,6 +380,7 @@ type RPCClients struct {
Watchtower watchtowerrpc.WatchtowerClient
WatchtowerClient wtclientrpc.WatchtowerClientClient
State lnrpc.StateClient
ChainClient chainrpc.ChainNotifierClient
}
// Assert *HarnessNode implements the lnrpc.LightningClient interface.
@@ -929,6 +932,7 @@ func (hn *HarnessNode) InitRPCClients(c *grpc.ClientConn) {
WatchtowerClient: wtclientrpc.NewWatchtowerClientClient(c),
Signer: signrpc.NewSignerClient(c),
State: lnrpc.NewStateClient(c),
ChainClient: chainrpc.NewChainNotifierClient(c),
}
}
@@ -949,6 +953,7 @@ func (hn *HarnessNode) initLightningClient() error {
hn.WatchtowerClient = wtclientrpc.NewWatchtowerClientClient(conn)
hn.SignerClient = signrpc.NewSignerClient(conn)
hn.StateClient = lnrpc.NewStateClient(conn)
hn.ChainClient = chainrpc.NewChainNotifierClient(conn)
// Wait until the server is fully started.
if err := hn.WaitUntilServerActive(); err != nil {
+77 -4
View File
@@ -15,6 +15,7 @@ import (
"github.com/btcsuite/btcd/wire"
"github.com/lightningnetwork/lnd/input"
"github.com/lightningnetwork/lnd/lnrpc"
"github.com/lightningnetwork/lnd/lnrpc/chainrpc"
"github.com/lightningnetwork/lnd/lnrpc/signrpc"
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
"github.com/lightningnetwork/lnd/lntest"
@@ -101,8 +102,36 @@ func testTaprootKeySpend(ctxt context.Context, t *harnessTest,
}
assertWalletUnspent(t, net.Alice, op)
// Before we confirm the transaction, let's register a confirmation
// listener for it, which we expect to fire after mining a block.
p2trAddr, err := btcutil.DecodeAddress(
p2trResp.Address, harnessNetParams,
)
require.NoError(t.t, err)
p2trPkScript, err := txscript.PayToAddrScript(p2trAddr)
require.NoError(t.t, err)
_, currentHeight, err := net.Miner.Client.GetBestBlock()
require.NoError(t.t, err)
confClient, err := net.Alice.ChainClient.RegisterConfirmationsNtfn(
ctxt, &chainrpc.ConfRequest{
Script: p2trPkScript,
Txid: txid[:],
HeightHint: uint32(currentHeight),
NumConfs: 1,
},
)
require.NoError(t.t, err)
// Mine another block to clean up the mempool.
mineBlocks(t, net, 1, 1)
// We now expect our confirmation to go through.
confMsg, err := confClient.Recv()
require.NoError(t.t, err)
conf := confMsg.GetConf()
require.NotNil(t.t, conf)
require.Equal(t.t, conf.BlockHeight, uint32(currentHeight+1))
}
// testTaprootScriptSpend tests sending to and spending from p2tr script
@@ -163,6 +192,10 @@ func testTaprootScriptSpend(ctxt context.Context, t *harnessTest,
p2trOutputIndex := getOutputIndex(
t, net.Miner, txid, tapScriptAddr.String(),
)
p2trOutpoint := wire.OutPoint{
Hash: *txid,
Index: uint32(p2trOutputIndex),
}
// Clear the mempool.
mineBlocks(t, net, 1, 1)
@@ -184,10 +217,7 @@ func testTaprootScriptSpend(ctxt context.Context, t *harnessTest,
tx := wire.NewMsgTx(2)
tx.TxIn = []*wire.TxIn{{
PreviousOutPoint: wire.OutPoint{
Hash: *txid,
Index: uint32(p2trOutputIndex),
},
PreviousOutPoint: p2trOutpoint,
}}
value := int64(800_000 - requiredFee)
tx.TxOut = []*wire.TxOut{{
@@ -235,6 +265,42 @@ func testTaprootScriptSpend(ctxt context.Context, t *harnessTest,
txWeight := blockchain.GetTransactionWeight(btcutil.NewTx(tx))
require.Equal(t.t, txWeight, estimatedWeight)
// Before we publish the tx that spends the p2tr transaction, we want to
// register a spend listener that we expect to fire after mining the
// block.
_, currentHeight, err := net.Miner.Client.GetBestBlock()
require.NoError(t.t, err)
// For a Taproot output we cannot leave the outpoint empty. Let's make
// sure the API returns the correct error here.
spendClient, err := net.Alice.ChainClient.RegisterSpendNtfn(
ctxt, &chainrpc.SpendRequest{
Script: p2trPkScript,
HeightHint: uint32(currentHeight),
},
)
require.NoError(t.t, err)
// The error is only thrown when trying to read a message.
_, err = spendClient.Recv()
require.Contains(
t.t, err.Error(),
"cannot register witness v1 spend request without outpoint",
)
// Now try again, this time with the outpoint set.
spendClient, err = net.Alice.ChainClient.RegisterSpendNtfn(
ctxt, &chainrpc.SpendRequest{
Outpoint: &chainrpc.Outpoint{
Hash: p2trOutpoint.Hash[:],
Index: p2trOutpoint.Index,
},
Script: p2trPkScript,
HeightHint: uint32(currentHeight),
},
)
require.NoError(t.t, err)
_, err = net.Alice.WalletKitClient.PublishTransaction(
ctxt, &walletrpc.Transaction{
TxHex: buf.Bytes(),
@@ -257,6 +323,13 @@ func testTaprootScriptSpend(ctxt context.Context, t *harnessTest,
// Mine another block to clean up the mempool and to make sure the spend
// tx is actually included in a block.
mineBlocks(t, net, 1, 1)
// We now expect our spend event to go through.
spendMsg, err := spendClient.Recv()
require.NoError(t.t, err)
spend := spendMsg.GetSpend()
require.NotNil(t.t, spend)
require.Equal(t.t, spend.SpendingHeight, uint32(currentHeight+1))
}
// testTaprootKeySpendRPC tests that a tapscript address can also be spent using