Skip to content

Commit 7c14269

Browse files
committed
itest: RegisterSpendNtfn detects reorgs
Added itest "reorg_notifications" which tests that RegisterSpendNtfn behaves as expected during a reorg. A reorg notification is produced after a reorg affects the block which has produced a spending notification for this registration.
1 parent 9059a4e commit 7c14269

File tree

2 files changed

+199
-0
lines changed

2 files changed

+199
-0
lines changed

itest/list_on_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ var allTestCases = []*lntest.TestCase{
4646
Name: "send selected coins channel reserve",
4747
TestFunc: testSendSelectedCoinsChannelReserve,
4848
},
49+
{
50+
Name: "reorg notifications",
51+
TestFunc: testReorgNotifications,
52+
},
4953
{
5054
Name: "disconnecting target peer",
5155
TestFunc: testDisconnectingTargetPeer,

itest/lnd_misc_test.go

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@ import (
44
"encoding/hex"
55
"fmt"
66
"os"
7+
"time"
78

89
"github.com/btcsuite/btcd/btcutil"
10+
"github.com/btcsuite/btcd/chaincfg/chainhash"
911
"github.com/btcsuite/btcd/txscript"
1012
"github.com/btcsuite/btcd/wire"
1113
"github.com/btcsuite/btcwallet/wallet"
1214
"github.com/lightningnetwork/lnd/funding"
1315
"github.com/lightningnetwork/lnd/input"
1416
"github.com/lightningnetwork/lnd/lncfg"
1517
"github.com/lightningnetwork/lnd/lnrpc"
18+
"github.com/lightningnetwork/lnd/lnrpc/chainrpc"
1619
"github.com/lightningnetwork/lnd/lnrpc/invoicesrpc"
1720
"github.com/lightningnetwork/lnd/lnrpc/routerrpc"
1821
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
@@ -1391,3 +1394,195 @@ func testGRPCNotFound(ht *lntest.HarnessTest) {
13911394
RHash: rHash,
13921395
}, notFoundErr)
13931396
}
1397+
1398+
// testReorgNotifications tests that RegisterSpendNtfn behaves as expected
1399+
// during a reorg. A reorg notification is produced after a reorg affects the
1400+
// block which has produced a spending notification for this registration.
1401+
func testReorgNotifications(ht *lntest.HarnessTest) {
1402+
alice := ht.NewNodeWithCoins("Alice", nil)
1403+
bob := ht.NewNode("Bob", nil)
1404+
1405+
const tx1Amount = 1_000_000
1406+
1407+
// Alice will send coins to herself, Bob will watch spending and
1408+
// confirmation of the transaction. We make sure that a node can watch
1409+
// transactions which are not a part of its wallet.
1410+
respAddr := alice.RPC.NewAddress(&lnrpc.NewAddressRequest{
1411+
Type: lnrpc.AddressType_TAPROOT_PUBKEY,
1412+
})
1413+
txid1Str := alice.RPC.SendCoins(&lnrpc.SendCoinsRequest{
1414+
Addr: respAddr.Address,
1415+
Amount: tx1Amount,
1416+
SatPerVbyte: 2,
1417+
}).Txid
1418+
txid1, err := chainhash.NewHashFromStr(txid1Str)
1419+
require.NoError(ht, err)
1420+
tx1 := ht.AssertTxInMempool(*txid1)
1421+
1422+
// Find the output of tx1.
1423+
tx1OutIndex := -1
1424+
for i, txOut := range tx1.TxOut {
1425+
if txOut.Value == tx1Amount {
1426+
tx1OutIndex = i
1427+
}
1428+
}
1429+
require.NotEqual(ht, -1, tx1OutIndex)
1430+
tx1op := wire.OutPoint{
1431+
Hash: *txid1,
1432+
Index: uint32(tx1OutIndex),
1433+
}
1434+
tx1opLnrpc := &lnrpc.OutPoint{
1435+
TxidStr: txid1Str,
1436+
OutputIndex: uint32(tx1OutIndex),
1437+
}
1438+
tx1opChainrpc := &chainrpc.Outpoint{
1439+
Hash: txid1[:],
1440+
Index: uint32(tx1OutIndex),
1441+
}
1442+
pkscript := tx1.TxOut[tx1OutIndex].PkScript
1443+
1444+
// Now fee bump the output of the first transaction.
1445+
alice.RPC.BumpFee(&walletrpc.BumpFeeRequest{
1446+
Outpoint: tx1opLnrpc,
1447+
Immediate: true,
1448+
SatPerVbyte: 20,
1449+
})
1450+
1451+
// Now find the fee bump tx.
1452+
listSweepsReq := &walletrpc.ListSweepsRequest{
1453+
Verbose: true,
1454+
1455+
// startHeight -1 means include only unconfirmed.
1456+
StartHeight: -1,
1457+
}
1458+
var tx2aLnrpc *lnrpc.Transaction
1459+
require.NoError(ht, wait.NoError(func() error {
1460+
sweepsResp := alice.RPC.ListSweeps(listSweepsReq)
1461+
sweepsDetails := sweepsResp.GetTransactionDetails()
1462+
if sweepsDetails == nil {
1463+
return fmt.Errorf("no sweep details")
1464+
}
1465+
if len(sweepsDetails.Transactions) != 1 {
1466+
return fmt.Errorf("got %d sweeps, want %d",
1467+
len(sweepsDetails.Transactions), 1)
1468+
}
1469+
tx2aLnrpc = sweepsDetails.Transactions[0]
1470+
1471+
return nil
1472+
}, defaultTimeout))
1473+
require.Len(ht, tx2aLnrpc.PreviousOutpoints, 1)
1474+
require.Equal(
1475+
ht, tx1op.String(), tx2aLnrpc.PreviousOutpoints[0].Outpoint,
1476+
)
1477+
txid2a, err := chainhash.NewHashFromStr(tx2aLnrpc.TxHash)
1478+
require.NoError(ht, err)
1479+
tx2a := ht.AssertTxInMempool(*txid2a)
1480+
1481+
// Fee bump the output of the first transaction agin with a higher fee
1482+
// rate to get RBF transaction tx2b.
1483+
alice.RPC.BumpFee(&walletrpc.BumpFeeRequest{
1484+
Outpoint: tx1opLnrpc,
1485+
Immediate: true,
1486+
SatPerVbyte: 200,
1487+
})
1488+
var tx2bLnrpc *lnrpc.Transaction
1489+
require.NoError(ht, wait.NoError(func() error {
1490+
sweepsResp := alice.RPC.ListSweeps(listSweepsReq)
1491+
sweepsDetails := sweepsResp.GetTransactionDetails()
1492+
if sweepsDetails == nil {
1493+
return fmt.Errorf("no sweep details")
1494+
}
1495+
for _, tx := range sweepsDetails.Transactions {
1496+
if tx.TxHash != tx2aLnrpc.TxHash {
1497+
tx2bLnrpc = tx
1498+
break
1499+
}
1500+
}
1501+
if tx2bLnrpc == nil {
1502+
return fmt.Errorf("tx2aLnrpc hasn't been replaced yet")
1503+
}
1504+
1505+
return nil
1506+
}, defaultTimeout))
1507+
require.Len(ht, tx2bLnrpc.PreviousOutpoints, 1)
1508+
require.Equal(
1509+
ht, tx1op.String(), tx2bLnrpc.PreviousOutpoints[0].Outpoint,
1510+
)
1511+
txid2b, err := chainhash.NewHashFromStr(tx2bLnrpc.TxHash)
1512+
require.NoError(ht, err)
1513+
tx2b := ht.AssertTxInMempool(*txid2b)
1514+
1515+
// Mine tx1 only.
1516+
ht.Miner().MineBlockWithTxes([]*btcutil.Tx{btcutil.NewTx(tx1)})
1517+
1518+
// Bob starts watching spending of tx1op.
1519+
spendClient := bob.RPC.RegisterSpendNtfn(&chainrpc.SpendRequest{
1520+
Outpoint: tx1opChainrpc,
1521+
Script: pkscript,
1522+
HeightHint: ht.CurrentHeight(),
1523+
})
1524+
1525+
// Mine tx2b.
1526+
block1 := ht.Miner().MineBlockWithTxes(
1527+
[]*btcutil.Tx{btcutil.NewTx(tx2b)},
1528+
)
1529+
1530+
// Make sure RegisterSpendNtfn noticed the spending. Give 10s to receive
1531+
// the notification.
1532+
spendReceived := make(chan struct{})
1533+
go func() {
1534+
spendMsg, err := spendClient.Recv()
1535+
require.NoError(ht, err)
1536+
spendDetails := spendMsg.GetSpend()
1537+
require.NotNil(ht, spendDetails)
1538+
require.Equal(ht, txid2b[:], spendDetails.SpendingTxHash)
1539+
close(spendReceived)
1540+
}()
1541+
select {
1542+
case <-time.After(10 * time.Second):
1543+
ht.Fatal("Failed to receive spending notification")
1544+
case <-spendReceived:
1545+
}
1546+
1547+
// Reorg block1.
1548+
blockHash1 := block1.Header.BlockHash()
1549+
require.NoError(ht, ht.Miner().Client.InvalidateBlock(&blockHash1))
1550+
1551+
// Mine empty blocks to evict block1 in bitcoin backend (e.g. bitcoind).
1552+
ht.Miner().MineEmptyBlocks(2)
1553+
1554+
// Make sure RegisterSpendNtfn noticed the reorg. Transaction tx2b was
1555+
// just unconfirmed. Give 10s to receive the notification.
1556+
reorgReceived := make(chan struct{})
1557+
go func() {
1558+
spendMsg, err := spendClient.Recv()
1559+
require.NoError(ht, err)
1560+
require.NotNil(ht, spendMsg.GetReorg())
1561+
close(reorgReceived)
1562+
}()
1563+
select {
1564+
case <-time.After(10 * time.Second):
1565+
ht.Fatal("Failed to receive reorg notification")
1566+
case <-reorgReceived:
1567+
}
1568+
1569+
// Mine tx2a to confirm a different version of spending.
1570+
ht.Miner().MineBlockWithTxes([]*btcutil.Tx{btcutil.NewTx(tx2a)})
1571+
1572+
// Make sure RegisterSpendNtfn noticed the spending. Give 10s to receive
1573+
// the notification.
1574+
spendReceived = make(chan struct{})
1575+
go func() {
1576+
spendMsg, err := spendClient.Recv()
1577+
require.NoError(ht, err)
1578+
spendDetails := spendMsg.GetSpend()
1579+
require.NotNil(ht, spendDetails)
1580+
require.Equal(ht, txid2a[:], spendDetails.SpendingTxHash)
1581+
close(spendReceived)
1582+
}()
1583+
select {
1584+
case <-time.After(10 * time.Second):
1585+
ht.Fatal("Failed to receive spending notification")
1586+
case <-spendReceived:
1587+
}
1588+
}

0 commit comments

Comments
 (0)