Skip to content

Commit 72320b2

Browse files
Roasbeefguggero
authored andcommitted
itest: add new testCustomChannelsHtlcForceClose itest
In this commit, we add a new itest that tests the 4 sweeping cases for HTLCs: * local success * remote success * local timeout * remote timeout To test this, we have Alice load up her commitment transaction with 4 HTLCs (2 incoming, 2 outgoing) using hodl invoices. Then we force close her commitment transaction. In this scenario, she needs to broadcast a second level transaction to ultimately timeout, while Bob can sweep directly from the commitment transaction.
1 parent 2b6cac3 commit 72320b2

File tree

5 files changed

+549
-1
lines changed

5 files changed

+549
-1
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ require (
3636
github.com/urfave/cli v1.22.9
3737
go.etcd.io/bbolt v1.3.11
3838
golang.org/x/crypto v0.25.0
39+
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8
3940
golang.org/x/net v0.27.0
4041
golang.org/x/sync v0.8.0
4142
google.golang.org/grpc v1.65.0
@@ -201,7 +202,6 @@ require (
201202
go.uber.org/mock v0.4.0 // indirect
202203
go.uber.org/multierr v1.6.0 // indirect
203204
go.uber.org/zap v1.23.0 // indirect
204-
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
205205
golang.org/x/mod v0.17.0 // indirect
206206
golang.org/x/sys v0.22.0 // indirect
207207
golang.org/x/term v0.22.0 // indirect

itest/assertions.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ package itest
33
import (
44
"context"
55
"fmt"
6+
"testing"
67

78
"github.com/btcsuite/btcd/chaincfg/chainhash"
89
"github.com/btcsuite/btcd/wire"
910
"github.com/lightningnetwork/lnd/channeldb"
1011
"github.com/lightningnetwork/lnd/lnrpc"
12+
"github.com/lightningnetwork/lnd/lnrpc/walletrpc"
1113
"github.com/lightningnetwork/lnd/lntest"
1214
"github.com/lightningnetwork/lnd/lntest/wait"
1315
"github.com/stretchr/testify/require"
@@ -205,3 +207,27 @@ func assertChannelClosed(ctx context.Context, t *harnessTest,
205207

206208
return closingTxid
207209
}
210+
211+
func assertSweepExists(t *testing.T, node *HarnessNode,
212+
witnessType walletrpc.WitnessType) {
213+
214+
ctxb := context.Background()
215+
err := wait.NoError(func() error {
216+
pendingSweeps, err := node.WalletKitClient.PendingSweeps(
217+
ctxb, &walletrpc.PendingSweepsRequest{},
218+
)
219+
if err != nil {
220+
return err
221+
}
222+
223+
for _, sweep := range pendingSweeps.PendingSweeps {
224+
if sweep.WitnessType == witnessType {
225+
return nil
226+
}
227+
}
228+
229+
return fmt.Errorf("failed to find second level sweep: %v",
230+
toProtoJSON(t, pendingSweeps))
231+
}, defaultTimeout)
232+
require.NoError(t, err)
233+
}

itest/assets_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1868,3 +1868,91 @@ func toProtoJSON(t *testing.T, resp proto.Message) string {
18681868

18691869
return string(jsonBytes)
18701870
}
1871+
1872+
func assertNumHtlcs(t *testing.T, node *HarnessNode, expected int) {
1873+
t.Helper()
1874+
1875+
ctxb := context.Background()
1876+
1877+
err := wait.NoError(func() error {
1878+
listChansRequest := &lnrpc.ListChannelsRequest{}
1879+
listChansResp, err := node.ListChannels(ctxb, listChansRequest)
1880+
if err != nil {
1881+
return err
1882+
}
1883+
1884+
var numHtlcs int
1885+
for _, channel := range listChansResp.Channels {
1886+
numHtlcs += len(channel.PendingHtlcs)
1887+
}
1888+
1889+
if numHtlcs != expected {
1890+
return fmt.Errorf("expected %v HTLCs, got %v, %v", expected, numHtlcs, spew.Sdump(toProtoJSON(t, listChansResp)))
1891+
}
1892+
1893+
return nil
1894+
}, defaultTimeout)
1895+
require.NoError(t, err)
1896+
}
1897+
1898+
type forceCloseExpiryInfo struct {
1899+
currentHeight uint32
1900+
csvDelay uint32
1901+
1902+
cltvDelays map[lntypes.Hash]uint32
1903+
1904+
localAssetBalance uint64
1905+
remoteAssetBalance uint64
1906+
1907+
t *testing.T
1908+
1909+
node *HarnessNode
1910+
}
1911+
1912+
func (f *forceCloseExpiryInfo) blockTillExpiry(hash lntypes.Hash) uint32 {
1913+
ctxb := context.Background()
1914+
nodeInfo, err := f.node.GetInfo(ctxb, &lnrpc.GetInfoRequest{})
1915+
require.NoError(f.t, err)
1916+
1917+
cltv, ok := f.cltvDelays[hash]
1918+
require.True(f.t, ok)
1919+
1920+
f.t.Logf("current_height=%v, expiry=%v, mining %v blocks",
1921+
nodeInfo.BlockHeight, cltv, cltv-nodeInfo.BlockHeight)
1922+
1923+
return cltv - nodeInfo.BlockHeight
1924+
}
1925+
1926+
func newCloseExpiryInfo(t *testing.T, node *HarnessNode) forceCloseExpiryInfo {
1927+
ctxb := context.Background()
1928+
1929+
listChansRequest := &lnrpc.ListChannelsRequest{}
1930+
listChansResp, err := node.ListChannels(ctxb, listChansRequest)
1931+
require.NoError(t, err)
1932+
1933+
mainChan := listChansResp.Channels[0]
1934+
1935+
nodeInfo, err := node.GetInfo(ctxb, &lnrpc.GetInfoRequest{})
1936+
require.NoError(t, err)
1937+
1938+
cltvs := make(map[lntypes.Hash]uint32)
1939+
for _, htlc := range mainChan.PendingHtlcs {
1940+
var payHash lntypes.Hash
1941+
copy(payHash[:], htlc.HashLock)
1942+
cltvs[payHash] = htlc.ExpirationHeight
1943+
}
1944+
1945+
var assetData rfqmsg.JsonAssetChannel
1946+
err = json.Unmarshal(mainChan.CustomChannelData, &assetData)
1947+
require.NoError(t, err)
1948+
1949+
return forceCloseExpiryInfo{
1950+
csvDelay: mainChan.CsvDelay,
1951+
currentHeight: nodeInfo.BlockHeight,
1952+
cltvDelays: cltvs,
1953+
localAssetBalance: assetData.Assets[0].LocalBalance,
1954+
remoteAssetBalance: assetData.Assets[0].RemoteBalance,
1955+
t: t,
1956+
node: node,
1957+
}
1958+
}

0 commit comments

Comments
 (0)